-
-
Save dmh2000/9519489 to your computer and use it in GitHub Desktop.
| #include <unistd.h> | |
| #include <node.h> | |
| #include <string.h> | |
| #include <v8.h> | |
| using namespace v8; | |
| unsigned long long count = 0; | |
| // native blocking/compute intensive function | |
| void delay(int seconds) { | |
| int i; | |
| int j; | |
| // a long computation | |
| for(i=0;i<2000000;++i) { | |
| for(j=0;j<400;++j) { | |
| count = count * seconds; | |
| } | |
| } | |
| /** | |
| * or a blocking call | |
| * sleep(seconds); | |
| */ | |
| } | |
| // the 'baton' is the carrier for data between functions | |
| struct DelayBaton | |
| { | |
| // required | |
| uv_work_t request; // libuv | |
| Persistent<Function> callback; // javascript callback | |
| // optional : data goes here. | |
| // data that doesn't go back to javascript can be any typedef | |
| // data that goes back to javascript needs to be a supported type | |
| int seconds; | |
| char greeting[256]; | |
| }; | |
| // called by libuv worker in separate thread | |
| static void DelayAsync(uv_work_t *req) | |
| { | |
| DelayBaton *baton = static_cast<DelayBaton *>(req->data); | |
| delay(baton->seconds); | |
| } | |
| // called by libuv in event loop when async function completes | |
| static void DelayAsyncAfter(uv_work_t *req,int status) | |
| { | |
| // get the reference to the baton from the request | |
| DelayBaton *baton = static_cast<DelayBaton *>(req->data); | |
| // set up return arguments | |
| Handle<Value> argv[] = | |
| { | |
| Handle<Value>(Int32::New(baton->seconds)), | |
| Handle<Value>(String::New(baton->greeting)) | |
| }; | |
| // execute the callback | |
| baton->callback->Call(Context::GetCurrent()->Global(),2,argv); | |
| // dispose the callback object from the baton | |
| baton->callback.Dispose(); | |
| // delete the baton object | |
| delete baton; | |
| } | |
| // javascript callable function | |
| Handle<Value> Delay(const Arguments &args) | |
| { | |
| // create 'baton' data carrier | |
| DelayBaton *baton = new DelayBaton; | |
| // get callback argument | |
| Handle<Function> cb = Handle<Function>::Cast(args[2]); | |
| // attach baton to uv work request | |
| baton->request.data = baton; | |
| // assign incoming arguments to baton | |
| baton->seconds = args[0]->Int32Value(); | |
| // point at the argument as a string, then copy it to the baton | |
| v8::String::Utf8Value str(args[1]); | |
| strncpy(baton->greeting,*str,sizeof(baton->greeting)); | |
| // assign callback to baton | |
| baton->callback = Persistent<Function>::New(cb); | |
| // queue the async function to the event loop | |
| // the uv default loop is the node.js event loop | |
| uv_queue_work(uv_default_loop(),&baton->request,DelayAsync,DelayAsyncAfter); | |
| // nothing returned | |
| return Undefined(); | |
| } | |
| void init(Handle<Object> exports) { | |
| // add the async function to the exports for this object | |
| exports->Set( | |
| String::NewSymbol("delay"), // javascript function name | |
| FunctionTemplate::New(Delay)->GetFunction() // attach 'Delay' function to javascript name | |
| ); | |
| } | |
| NODE_MODULE(delay, init) | |
| /** | |
| * server.js | |
| var addon = require('./build/Release/delay'); | |
| var i = 0; | |
| setInterval(function() { | |
| console.log(i++); | |
| },500); | |
| // test the delay function | |
| addon.delay(3,'hello world',function(a,b) { | |
| console.log('delay : ' + a + ',' + b); | |
| }); | |
| */ | |
| /** | |
| * binding.gyp | |
| { | |
| "targets": [ | |
| { | |
| "target_name": "delay", | |
| "sources": [ "delay.cpp" ] | |
| } | |
| ] | |
| } | |
| */ |
Thanks!
I couldn't get this to work though - could be my shabby C++ or maybe Node.js versioning.
I've got a version working with Node.js v4.4.2 running here:
https://github.com/paulhauner/example-async-node-addon
@paulhauner and what is node.js 6? :) is working this asynch sample?
@dmh2000 :
I wanted NodeJS to call C code which accepts a function pointer to give back the result asynchronously to the caller. I stumbled upon this and it's very helpful 👍 as I know almost nothing about NodeJS (javascript in general). I was expecting two have only 2 threads in total in my setup - a main thread which is the JS event loop and a background event loop which is in C.
C code:
void foo(char *param_0, int param_1, void *user_data, void(*output)(void * /*user_data*/, int /*some-result*/)) {
// Post the 4 function parameters to C event loop running in a background thread.
// When C finishes, it'll notify the outcome via given `output` function-ptr/callback in it's thread.
// The call back should have the logic to post to JS main event loop thread and return.
}
So calling foo is non-blocking.
However it seems that i need to code a wrapper around foo (which equivalently would be delay() in your example) which will call foo giving it a callback written in C and block until result is obtained via passed callback. This wrapper (the equivalent of delay) will be called in a worker thread spawned by libuv according to your explanation. This means that each time NodeJS wants to call foo a new thread will be spawned by the libuv intermediary, thus could end up having potentially 100s or threads assuming rate of calling foo is higher than C processing the result and returning it via callback.
Is that true ? Is this a standard idiom or is there a way around it (to have just 2 or 3 threads like described) ?
Thanks!!
nice