Node.js howto: make asynchronous callback from thread

Since Node.js uses libuv to perform event loop in main thread, all asynchronous callbacks from out thread should fall into main thread. If we directly emit callback from our thread, system core dump occurs. The way making asynchronous callbacks is posting request(the uv_work_t* object) into Node.js main thread, carrying our data, letting the Node.js' main thread(the V8 engine thread) does the calling.

The following code represents how to do that by creating a node.js module which runs a different thread and fires the callback.

1. Makefile.

CC=g++

addon.o: \
  addon.cxx
  $(CC) -o addon.o addon.cxx

2. binding.gyp

{
  "targets": [
    {
      "cflags": ["-fpermissive", "-fexceptions", "-std=c++0x", "-fPIC"],
      #"ldflags":["-L/usr/lib/", "-lboost_thread"],
      "libraries":["/usr/lib/libboost_thread.so"],
      "cflags_cc!":["-fno-rtti", "-fno-exceptions"],
      "cflags_cc+":["-frtti", "-fexceptions"],
      "target_name": "addon",
      "sources": ["addon.cxx"]
    }
  ]
}

3. addon.cxx

#include <v8.h>
#include <node.h>

#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/thread/thread.hpp>
#include <iostream>

using namespace v8;

Persistent<Function> theCallback;

void nop(uv_work_t* req)
{
  // Do nothing.
}

void uv_exit(uv_work_t* req, int status)
{
  delete req;

  HandleScope scope;
  const unsigned argc = 1;
  Local<Value> argv[argc] = {
    Integer::New(-1)
  };

  theCallback->Call(Context::GetCurrent()->Global(), argc, argv);

  theCallback.Dispose();
}

void uv_req(uv_work_t* req, int status)
{
  int x = (int)(req->data);
  delete req;

  HandleScope scope;

  const unsigned argc = 1;
  Local<Value> argv[argc] = {
    Local<Value>::New(Integer::New(x))
  };

  theCallback->Call(Context::GetCurrent()->Global(), argc, argv);
}

Handle<Value> RunCallback(const Arguments& args)
{
  HandleScope scope;

  theCallback = Persistent<Function>::New(Local<Function>::Cast(args[0]));

  boost::thread thd([](){

    uv_work_t* uv;

    for(int i = 0; ; i++)
    {
      uv = new uv_work_t;
      uv->data = i;

      int r = uv_queue_work(uv_default_loop(), uv, nop, uv_req);

      if (r != 0)
      {
        delete uv;
      }
    }

    uv = new uv_work_t;
    uv->data = 0;

    int r = uv_queue_work(uv_default_loop(), uv, nop, uv_exit);

    if (r != 0)
    {
      delete uv;
    }

    // Give up CPU
    boost::this_thread::sleep(boost::posix_time::milliseconds(100));
  });

  return scope.Close(Undefined());
}

void Init(Handle<Object> exports, Handle<Object> module)
{
  module->Set(String::NewSymbol("exports"),
    FunctionTemplate::New(RunCallback)->GetFunction());
}

NODE_MODULE(addon, Init)

4. test.js

var x = require('./build/Debug/addon');

x(function(arg){
  console.log(arg);
});

5. Configure, make and run.

$node-gyp configure && node-gyp build && node test.js

The console will relentless print numbers until ctrl-c has been pressed.


Good luck! :-)



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章