Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add context awareness to Nan::AsyncWorker and Nan::Callback #731

Merged
merged 9 commits into from
Feb 13, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ LINT_SOURCES = \
test/cpp/error.cpp \
test/cpp/gc.cpp \
test/cpp/indexedinterceptors.cpp \
test/cpp/callbackcontext.cpp \
test/cpp/converters.cpp \
test/cpp/isolatedata.cpp \
test/cpp/json-parse.cpp \
Expand All @@ -70,6 +71,7 @@ LINT_SOURCES = \
test/cpp/returnvalue.cpp \
test/cpp/setcallhandler.cpp \
test/cpp/settemplate.cpp \
test/cpp/sleep.h \
test/cpp/strings.cpp \
test/cpp/symbols.cpp \
test/cpp/threadlocal.cpp \
Expand Down
15 changes: 11 additions & 4 deletions doc/asyncworker.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,17 @@

`Nan::AsyncWorker` is an _abstract_ class that you can subclass to have much of the annoying asynchronous queuing and handling taken care of for you. It can even store arbitrary V8 objects for you and have them persist while the asynchronous work is in progress.

This class internally handles the details of creating an [`AsyncResource`][AsyncResource], and running the callback in the
correct async context. To be able to identify the async resources created by this class in async-hooks, provide a
`resource_name` to the constructor. It is recommended that the module name be used as a prefix to the `resource_name` to avoid
collisions in the names. For more details see [`AsyncResource`][AsyncResource] documentation. The `resource_name` needs to stay valid for the lifetime of the worker instance.

Definition:

```c++
class AsyncWorker {
public:
explicit AsyncWorker(Callback *callback_);
explicit AsyncWorker(Callback *callback_, const char* resource_name = "nan:AsyncWorker");

virtual ~AsyncWorker();

Expand Down Expand Up @@ -73,9 +78,9 @@ Definition:
template<class T>
class AsyncProgressWorkerBase<T> : public AsyncWorker {
public:
explicit AsyncProgressWorker(Callback *callback_);
explicit AsyncProgressWorkerBase(Callback *callback_, const char* resource_name = ...);

virtual ~AsyncProgressWorker();
virtual ~AsyncProgressWorkerBase();

void WorkProgress();

Expand Down Expand Up @@ -108,7 +113,7 @@ Definition:
template<class T>
class AsyncProgressQueueWorker<T> : public AsyncWorker {
public:
explicit AsyncProgressQueueWorker(Callback *callback_);
explicit AsyncProgressQueueWorker(Callback *callback_, const char* resource_name = "nan:AsyncProgressQueueWorker");

virtual ~AsyncProgressQueueWorker();

Expand Down Expand Up @@ -137,3 +142,5 @@ Definition:
```c++
void AsyncQueueWorker(AsyncWorker *);
```

[AsyncResource]: "node_misc.html#api_nan_asyncresource"
29 changes: 24 additions & 5 deletions doc/callback.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ class Callback {

v8::Local<v8::Function> operator*() const;

v8::Local<v8::Value> operator()(v8::Local<v8::Object> target,
int argc = 0,
v8::Local<v8::Value> argv[] = 0) const;
MaybeLocal<v8::Value> operator()(AsyncResource* async_resource,
v8::Local<v8::Object> target,
int argc = 0,
v8::Local<v8::Value> argv[] = 0) const;

v8::Local<v8::Value> operator()(int argc = 0,
v8::Local<v8::Value> argv[] = 0) const;
MaybeLocal<v8::Value> operator()(AsyncResource* async_resource,
int argc = 0,
v8::Local<v8::Value> argv[] = 0) const;

void SetFunction(const v8::Local<v8::Function> &fn);

Expand All @@ -39,6 +41,23 @@ class Callback {

void Reset();

MaybeLocal<v8::Value> Call(v8::Local<v8::Object> target,
int argc,
v8::Local<v8::Value> argv[],
AsyncResource* async_resource) const;
MaybeLocal<v8::Value> Call(int argc,
v8::Local<v8::Value> argv[],
AsyncResource* async_resource) const;

// Legacy versions. Use the versions that accept an async_resource instead
// as they run the callback in the correct async context as specified by the
// resource.
v8::Local<v8::Value> operator()(v8::Local<v8::Object> target,
int argc = 0,
v8::Local<v8::Value> argv[] = 0) const;

v8::Local<v8::Value> operator()(int argc = 0,
v8::Local<v8::Value> argv[] = 0) const;
v8::Local<v8::Value> Call(v8::Local<v8::Object> target,
int argc,
v8::Local<v8::Value> argv[]) const;
Expand Down
141 changes: 102 additions & 39 deletions nan.h
Original file line number Diff line number Diff line change
Expand Up @@ -1273,14 +1273,6 @@ class Utf8String {

#endif // NODE_MODULE_VERSION

//=== async_context ============================================================

#if NODE_MODULE_VERSION >= NODE_8_0_MODULE_VERSION
typedef node::async_context async_context;
#else
struct async_context {};
#endif

// === AsyncResource ===========================================================

class AsyncResource {
Expand All @@ -1295,9 +1287,7 @@ class Utf8String {
maybe_resource.IsEmpty() ? New<v8::Object>()
: maybe_resource.ToLocalChecked();

node::async_context context =
node::EmitAsyncInit(isolate, resource, resource_name);
asyncContext = static_cast<async_context>(context);
context = node::EmitAsyncInit(isolate, resource, resource_name);
#endif
}

Expand All @@ -1310,18 +1300,14 @@ class Utf8String {
: maybe_resource.ToLocalChecked();
v8::Local<v8::String> name_string =
New<v8::String>(name).ToLocalChecked();
node::async_context context =
node::EmitAsyncInit(isolate, resource, name_string);
asyncContext = static_cast<async_context>(context);
context = node::EmitAsyncInit(isolate, resource, name_string);
#endif
}

~AsyncResource() {
#if NODE_MODULE_VERSION >= NODE_8_0_MODULE_VERSION
v8::Isolate* isolate = v8::Isolate::GetCurrent();
node::async_context node_context =
static_cast<node::async_context>(asyncContext);
node::EmitAsyncDestroy(isolate, node_context);
node::EmitAsyncDestroy(isolate, context);
#endif
}

Expand All @@ -1334,8 +1320,7 @@ class Utf8String {
return MakeCallback(target, func, argc, argv);
#else
return node::MakeCallback(
v8::Isolate::GetCurrent(), target, func, argc, argv,
static_cast<node::async_context>(asyncContext));
v8::Isolate::GetCurrent(), target, func, argc, argv, context);
#endif
}

Expand All @@ -1348,8 +1333,7 @@ class Utf8String {
return MakeCallback(target, symbol, argc, argv);
#else
return node::MakeCallback(
v8::Isolate::GetCurrent(), target, symbol, argc, argv,
static_cast<node::async_context>(asyncContext));
v8::Isolate::GetCurrent(), target, symbol, argc, argv, context);
#endif
}

Expand All @@ -1362,13 +1346,15 @@ class Utf8String {
return MakeCallback(target, method, argc, argv);
#else
return node::MakeCallback(
v8::Isolate::GetCurrent(), target, method, argc, argv,
static_cast<node::async_context>(asyncContext));
v8::Isolate::GetCurrent(), target, method, argc, argv, context);
#endif
}

private:
async_context asyncContext;
NAN_DISALLOW_ASSIGN_COPY_MOVE(AsyncResource)
#if NODE_MODULE_VERSION >= NODE_8_0_MODULE_VERSION
node::async_context context;
#endif
};

typedef void (*FreeCallback)(char *data, void *hint);
Expand Down Expand Up @@ -1506,6 +1492,21 @@ class Callback {
return this->Call(argc, argv);
}

inline MaybeLocal<v8::Value> operator()(
AsyncResource* resource
, int argc = 0
, v8::Local<v8::Value> argv[] = 0) const {
return this->Call(argc, argv, resource);
}

inline MaybeLocal<v8::Value> operator()(
AsyncResource* resource
, v8::Local<v8::Object> target
, int argc = 0
, v8::Local<v8::Value> argv[] = 0) const {
return this->Call(target, argc, argv, resource);
}

// TODO(kkoopa): remove
inline void SetFunction(const v8::Local<v8::Function> &fn) {
Reset(fn);
Expand All @@ -1531,7 +1532,7 @@ class Callback {
Call(v8::Local<v8::Object> target
, int argc
, v8::Local<v8::Value> argv[]) const {
#if (NODE_MODULE_VERSION > NODE_0_10_MODULE_VERSION)
#if NODE_MODULE_VERSION > NODE_0_10_MODULE_VERSION
v8::Isolate *isolate = v8::Isolate::GetCurrent();
return Call_(isolate, target, argc, argv);
#else
Expand All @@ -1541,7 +1542,39 @@ class Callback {

inline v8::Local<v8::Value>
Call(int argc, v8::Local<v8::Value> argv[]) const {
#if (NODE_MODULE_VERSION > NODE_0_10_MODULE_VERSION)
#if NODE_MODULE_VERSION > NODE_0_10_MODULE_VERSION
v8::Isolate *isolate = v8::Isolate::GetCurrent();
v8::EscapableHandleScope scope(isolate);
return scope.Escape(
Call_(isolate, isolate->GetCurrentContext()->Global(), argc, argv));
#else
v8::HandleScope scope;
return scope.Close(Call_(v8::Context::GetCurrent()->Global(), argc, argv));
#endif
}

inline MaybeLocal<v8::Value>
Call(v8::Local<v8::Object> target
, int argc
, v8::Local<v8::Value> argv[]
, AsyncResource* resource) const {
#if NODE_MODULE_VERSION >= NODE_8_0_MODULE_VERSION
v8::Isolate* isolate = v8::Isolate::GetCurrent();
return Call_(isolate, target, argc, argv, resource);
#elif NODE_MODULE_VERSION > NODE_0_10_MODULE_VERSION
v8::Isolate *isolate = v8::Isolate::GetCurrent();
return Call_(isolate, target, argc, argv);
#else
return Call_(target, argc, argv);
#endif
}

inline MaybeLocal<v8::Value>
Call(int argc, v8::Local<v8::Value> argv[], AsyncResource* resource) const {
#if NODE_MODULE_VERSION >= NODE_8_0_MODULE_VERSION
v8::Isolate* isolate = v8::Isolate::GetCurrent();
return Call(isolate->GetCurrentContext()->Global(), argc, argv, resource);
#elif NODE_MODULE_VERSION > NODE_0_10_MODULE_VERSION
v8::Isolate *isolate = v8::Isolate::GetCurrent();
v8::EscapableHandleScope scope(isolate);
return scope.Escape(
Expand All @@ -1556,7 +1589,22 @@ class Callback {
NAN_DISALLOW_ASSIGN_COPY_MOVE(Callback)
Persistent<v8::Function> handle_;

#if (NODE_MODULE_VERSION > NODE_0_10_MODULE_VERSION)
#if NODE_MODULE_VERSION >= NODE_8_0_MODULE_VERSION
MaybeLocal<v8::Value> Call_(v8::Isolate *isolate
, v8::Local<v8::Object> target
, int argc
, v8::Local<v8::Value> argv[]
, AsyncResource* resource) const {
EscapableHandleScope scope;
v8::Local<v8::Function> func = New(handle_);
auto maybe = resource->runInAsyncScope(target, func, argc, argv);
v8::Local<v8::Value> local;
if (!maybe.ToLocal(&local)) return MaybeLocal<v8::Value>();
return scope.Escape(local);
}
#endif

#if NODE_MODULE_VERSION > NODE_0_10_MODULE_VERSION
v8::Local<v8::Value> Call_(v8::Isolate *isolate
, v8::Local<v8::Object> target
, int argc
Expand Down Expand Up @@ -1601,13 +1649,15 @@ class Callback {

/* abstract */ class AsyncWorker {
public:
explicit AsyncWorker(Callback *callback_)
explicit AsyncWorker(Callback *callback_,
const char* resource_name = "nan:AsyncWorker")
: callback(callback_), errmsg_(NULL) {
request.data = this;

HandleScope scope;
v8::Local<v8::Object> obj = New<v8::Object>();
persistentHandle.Reset(obj);
async_resource = new AsyncResource(obj, resource_name);

This comment was marked as off-topic.

This comment was marked as off-topic.

}

virtual ~AsyncWorker() {
Expand All @@ -1617,6 +1667,7 @@ class Callback {
persistentHandle.Reset();
delete callback;
delete[] errmsg_;
delete async_resource;
}

virtual void WorkComplete() {
Expand Down Expand Up @@ -1676,11 +1727,12 @@ class Callback {
protected:
Persistent<v8::Object> persistentHandle;
Callback *callback;
AsyncResource *async_resource;

virtual void HandleOKCallback() {
HandleScope scope;

callback->Call(0, NULL);
callback->Call(0, NULL, async_resource);
}

virtual void HandleErrorCallback() {
Expand All @@ -1689,7 +1741,7 @@ class Callback {
v8::Local<v8::Value> argv[] = {
v8::Exception::Error(New<v8::String>(ErrorMessage()).ToLocalChecked())
};
callback->Call(1, argv);
callback->Call(1, argv, async_resource);
}

void SetErrorMessage(const char *msg) {
Expand All @@ -1711,8 +1763,10 @@ class Callback {

/* abstract */ class AsyncBareProgressWorkerBase : public AsyncWorker {
public:
explicit AsyncBareProgressWorkerBase(Callback *callback_)
: AsyncWorker(callback_) {
explicit AsyncBareProgressWorkerBase(
Callback *callback_,
const char* resource_name = "nan:AsyncBareProgressWorkerBase")
: AsyncWorker(callback_, resource_name) {
uv_async_init(
uv_default_loop()
, &async
Expand Down Expand Up @@ -1751,8 +1805,10 @@ template<class T>
/* abstract */
class AsyncBareProgressWorker : public AsyncBareProgressWorkerBase {
public:
explicit AsyncBareProgressWorker(Callback *callback_)
: AsyncBareProgressWorkerBase(callback_) {
explicit AsyncBareProgressWorker(
Callback *callback_,
const char* resource_name = "nan:AsyncBareProgressWorker")
: AsyncBareProgressWorkerBase(callback_, resource_name) {
}

virtual ~AsyncBareProgressWorker() {
Expand Down Expand Up @@ -1791,8 +1847,11 @@ template<class T>
/* abstract */
class AsyncProgressWorkerBase : public AsyncBareProgressWorker<T> {
public:
explicit AsyncProgressWorkerBase(Callback *callback_)
: AsyncBareProgressWorker<T>(callback_), asyncdata_(NULL), asyncsize_(0) {
explicit AsyncProgressWorkerBase(
Callback *callback_,
const char* resource_name = "nan:AsyncProgressWorkerBase")
: AsyncBareProgressWorker<T>(callback_, resource_name), asyncdata_(NULL),
asyncsize_(0) {
uv_mutex_init(&async_lock);
}

Expand Down Expand Up @@ -1847,8 +1906,10 @@ template<class T>
/* abstract */
class AsyncBareProgressQueueWorker : public AsyncBareProgressWorkerBase {
public:
explicit AsyncBareProgressQueueWorker(Callback *callback_)
: AsyncBareProgressWorkerBase(callback_) {
explicit AsyncBareProgressQueueWorker(
Callback *callback_,
const char* resource_name = "nan:AsyncBareProgressQueueWorker")
: AsyncBareProgressWorkerBase(callback_, resource_name) {
}

virtual ~AsyncBareProgressQueueWorker() {
Expand Down Expand Up @@ -1884,7 +1945,9 @@ template<class T>
/* abstract */
class AsyncProgressQueueWorker : public AsyncBareProgressQueueWorker<T> {
public:
explicit AsyncProgressQueueWorker(Callback *callback_)
explicit AsyncProgressQueueWorker(
Callback *callback_,
const char* resource_name = "nan:AsyncProgressQueueWorker")
: AsyncBareProgressQueueWorker<T>(callback_) {
uv_mutex_init(&async_lock);
}
Expand Down
Loading