Normally, when writing an Add-on, we could code like this:
// Somewherevoid doSomething(std::string& str) { str = "Hello, world";}/// The implementation of add-onstatic void fork(const FunctionCallbackInfo<Value>& args) { Isolate* iso = args.GetIsolate(); /// Do something doSomething(str); /// Callback Local<Value> val = args[3]; // Assuming the forth arguments is the callback function. if (val->IsFunction()) { Local<Function> func = Function::Cast(val); func->Call(iso->GetCurrentContext()->Global(), Integer::New(iso, 0x1234)); }} // end of fork
The above code works fine unless Promise
involved. The notorious segment fault
drives me mad. The reason is that the Promise.resolve
will probably be called back from a different thread. Finally I found a way out:
/// Turn our processing into async// Define the prototype of callback.using callback = boost::function<void(int, const std::string&)>;typedef struct { std::string value; callback cb;} async_struc;AsyncWorker<async_struc> async_worker;void doSomething(std::string& arg, const callback& cb { async_struc* data = new async_struc(); data.value = arg; data.cb = cb; async_worker.post(data, [&](async_struc* x) { // Do the work. x->value += "Hello, world!"; }, [&](async_struc* x, bool canceled) { x->cb(canceled ? -1 : 0, x->value); });}
When crossing thread boundary, JavaScript object should be wrapped with Persistent
. Howover, V8
forbids us using explicit ctor
of Persistent
. An alternative is to use a structure to hold the Persistent
wrapped object and pass a structure pointer to make lambda expression capture it.
The structure looks like :
typedef struct { std::string value; //<! The value came from arguments. Persistent<Function> cb;} js_callback_info_t;
Also, according to V8
reference, we should get current Isolate
object under different threads. A HandleScope
is needed too.
Isolate* asyncIso = Isolate::GetCurrent();HandleScope scope(asyncIso);
Now rewrite the js add-on method:
static void fork(const FunctionCallbackInfo<Value>& args) { Isolate* iso = args.GetIsolate(); // Get string argument String::Utf8Value utf8str(args[0]); std::string str(*utf8str); /// Save the callback information Local<Value> val = args[3]; // Assuming the forth arguments is the callback function. js_callback_info_t* cb_info = new js_callback_info(); cb_info->value = str; cb_info->cb.Reset(iso, Local<Function>::Cast(val); doSomething(str, [&, cb_info](int err, const std::string& res) mutable { Isolate* asyncIso = Isolate::GetCurrent(); HandleScope scope(asyncIso); Local<Function> func = Local<Function>::New(asyncIso, cb_info->cb); Local<Value> params[] = { Integer::New(asyncIso, err), String::NewUtf8(asyncIso, res.c_str()) }; func->Call(asyncIso->GetCurrentContext()->Global(), 2, params); // Dispose persistent object func->cb.Reset(); // Release the memory allocated before delete cb_info; });} // end of fork
Now the Add-on is Promise-ready.