diff --git a/lib/ffi.js b/lib/ffi.js index 15c15753fb5a61..a0280368671ecd 100644 --- a/lib/ffi.js +++ b/lib/ffi.js @@ -86,3 +86,4 @@ exports.Closure = function Closure(cif, fn) { */ exports.call = binding.call; +exports.callAsync = binding.callAsync; diff --git a/src/node_ffi.cc b/src/node_ffi.cc index 677e33a1732d14..f566542b9dcedd 100644 --- a/src/node_ffi.cc +++ b/src/node_ffi.cc @@ -103,6 +103,73 @@ static void Call(const FunctionCallbackInfo& args) { } +/* + * Called on the thread pool. + */ + +void CallAsyncWork(uv_work_t *req) { + async_call *data = reinterpret_cast(req->data); + + ffi_call( + data->cif, + FFI_FN(data->fn), + data->res, + data->argv); +} + + +/** + * Called on the JS thread, after CallAsyncWork() has completed. + */ + +void CallAsyncDone(uv_work_t *req) { + async_call *data = reinterpret_cast(req->data); + HandleScope scope(data->isolate); + + Local cb = Local::Cast( + Local::New(data->isolate, data->callback)); + + // invoke the registered callback function + MakeCallback(data->isolate, + data->isolate->GetCurrentContext()->Global(), cb, 0, nullptr); + + // dispose of our persistent handle, and free memory + data->callback.Reset(); + delete data; +} + + +static void CallAsync(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + + if (!Buffer::HasInstance(args[0])) + return env->ThrowTypeError("expected Buffer instance as first argument"); + if (!Buffer::HasInstance(args[1])) + return env->ThrowTypeError("expected Buffer instance as second argument"); + if (!Buffer::HasInstance(args[2])) + return env->ThrowTypeError("expected Buffer instance as third argument"); + if (!Buffer::HasInstance(args[3])) + return env->ThrowTypeError("expected Buffer instance as fourth argument"); + if (!args[4]->IsFunction()) + return env->ThrowTypeError("expected callback function as fifth argument"); + + async_call* data = new async_call; + data->callback.Reset(env->isolate(), args[4].As()); + data->cif = reinterpret_cast(Buffer::Data(args[0].As())); + data->fn = Buffer::Data(args[1].As()); + data->res = reinterpret_cast(Buffer::Data(args[2].As())); + data->argv = reinterpret_cast(Buffer::Data(args[3].As())); + data->isolate = env->isolate(); + + uv_work_t *req = new uv_work_t; + req->data = data; + + uv_queue_work(uv_default_loop(), req, + CallAsyncWork, + (uv_after_work_cb)CallAsyncDone); +} + + static void ArgFree(char* data, void* hint) { /* do nothing */ } @@ -363,6 +430,7 @@ void Initialize(Handle target, env->SetMethod(target, "prepCif", PrepCif); env->SetMethod(target, "call", Call); + env->SetMethod(target, "callAsync", CallAsync); env->SetMethod(target, "closureAlloc", ClosureAlloc); // initialize our threaded invokation stuff diff --git a/src/node_ffi.h b/src/node_ffi.h index b75074bfab6899..73567f48cc8da7 100644 --- a/src/node_ffi.h +++ b/src/node_ffi.h @@ -24,6 +24,20 @@ typedef struct _closure_info { } closure_info; +/** + * Created for every ffi.callAsync() invokation. + */ + +typedef struct _async_call { + Persistent callback; + ffi_cif* cif; + char* fn; + void* res; + void** argv; + Isolate* isolate; +} async_call; + + /** * Synchronization object to ensure following order of execution: * -> WaitForExecution() invoked