Skip to content

Instantly share code, notes, and snippets.

@edhemphill
Created February 28, 2012 22:43
Show Gist options
  • Save edhemphill/1935788 to your computer and use it in GitHub Desktop.
Save edhemphill/1935788 to your computer and use it in GitHub Desktop.
C++ threaded extension for node.js w/ gyp build
// Originally at:
// http://bravenewmethod.wordpress.com/2011/03/30/callbacks-from-threaded-node-js-c-extension/
#include <queue>
// node headers
#include <v8.h>
#include <node.h>
#include <ev.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
using namespace node;
using namespace v8;
// handles required for callback messages
static pthread_t texample_thread;
static ev_async eio_texample_notifier;
Persistent<String> callback_symbol;
Persistent<Object> module_handle;
// message queue
std::queue<int> cb_msg_queue = std::queue<int>();
pthread_mutex_t queue_mutex = PTHREAD_MUTEX_INITIALIZER;
// The background thread
static void* TheThread(void *)
{
int i = 0;
while(true) {
// fire event every 5 seconds
sleep(5);
pthread_mutex_lock(&queue_mutex);
cb_msg_queue.push(i);
pthread_mutex_unlock(&queue_mutex);
i++;
// wake up callback
ev_async_send(EV_DEFAULT_UC_ &eio_texample_notifier);
}
return NULL;
}
// callback that runs the javascript in main thread
static void Callback(EV_P_ ev_async *watcher, int revents)
{
HandleScope scope;
assert(watcher == &eio_texample_notifier);
assert(revents == EV_ASYNC);
// locate callback from the module context if defined by script
// texample = require('texample')
// texample.callback = function( ... ) { ..
Local<Value> callback_v = module_handle->Get(callback_symbol);
if (!callback_v->IsFunction()) {
// callback not defined, ignore
return;
}
Local<Function> callback = Local<Function>::Cast(callback_v);
// dequeue callback message
pthread_mutex_lock(&queue_mutex);
int number = cb_msg_queue.front();
cb_msg_queue.pop();
pthread_mutex_unlock(&queue_mutex);
TryCatch try_catch;
// prepare arguments for the callback
Local<Value> argv[1];
argv[0] = Local<Value>::New(Integer::New(number));
// call the callback and handle possible exception
callback->Call(module_handle, 1, argv);
if (try_catch.HasCaught()) {
FatalException(try_catch);
}
}
// Start the background thread
Handle<Value> Start(const Arguments &args)
{
HandleScope scope;
// start background thread and event handler for callback
ev_async_init(&eio_texample_notifier, Callback);
//ev_set_priority(&eio_texample_notifier, EV_MAXPRI);
ev_async_start(EV_DEFAULT_UC_ &eio_texample_notifier);
ev_unref(EV_DEFAULT_UC);
pthread_create(&texample_thread, NULL, TheThread, 0);
return True();
}
void Initialize(Handle<Object> target)
{
HandleScope scope;
NODE_SET_METHOD(target, "start", Start);
callback_symbol = NODE_PSYMBOL("callback");
// store handle for callback context
module_handle = Persistent<Object>::New(target);
}
extern "C" {
static void Init(Handle<Object> target)
{
Initialize(target);
}
NODE_MODULE(texample, Init);
}
{
'variables': {
'node_texample_sources': [
"texample.cpp"
],
'node_root': '../node-0.7.0',
'node_root_win': 'c:\\node', # fill in with Windows info if needed
'deps_root_win': 'c:\\dev2',
'target_arch' : 'x64' # needed b/c we are on a 64-bit Linux
},
'targets': [
{
'target_name': 'texample',
'product_name': 'texample',
'type': 'loadable_module',
'product_prefix': '',
'product_extension':'node',
'sources': [
'<@(node_texample_sources)'
],
'defines': [
'PLATFORM="<(OS)"',
'_FILE_OFFSET_BITS=64'
],
'conditions': [
[ 'OS=="linux"', {
'cflags': ['-fPIC'], # needed on 64-bit linux, for the .node SO to work
'libraries': [
'../mylib/mylib.so' # put your own library requirements here
],
'include_dirs': [
'src/',
'<@(node_root)/include/node',
'<@(node_root)/include',
'<@(node_root)/deps/uv/include/uv-private',
'../expanded-prereqs/include'
],
'defines': [
#'HAVE_CAIRO',
'_LARGEFILE_SOURCE'
],
}],
[ 'OS=="win"', {
'defines': [
'HAVE_CAIRO',
'PLATFORM="win32"',
'_LARGEFILE_SOURCE',
'_FILE_OFFSET_BITS=64',
'_WINDOWS',
'__WINDOWS__', # ltdl
'BUILDING_NODE_EXTENSION'
],
'libraries': [
'mapnik2.lib',
'node.lib',
'icuuc.lib',
'libboost_regex-vc100-mt-1_48.lib',
],
'include_dirs': [
'c:\\mapnik-2.0\\include',
'<@(node_root_win)\\src',
'<@(node_root_win)\\deps\\uv\\include\\private',
],
'msvs_settings': {
'VCLinkerTool': {
'AdditionalLibraryDirectories': [
'<@(node_root_win)\\Release\\lib',
'<@(node_root_win)\\Release',
'<@(deps_root_win)\\mapnik-packaging\\windows\\build\\src\\msvc-9.0\\release\\threading-multi',
'<@(deps_root_win)\\boost-vc100\\lib',
'<@(deps_root_win)\\icu\\lib',
],
},
},
},
],
]
},
],
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment