eristoddle
5/31/2015 - 4:43 PM

simple node.js async c++ addon example

#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" ]
     }
   ]
 }
 */