Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
#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" ]
}
]
}
*/
@JoneXie1986

This comment has been minimized.

Copy link

commented Oct 15, 2015

nice

@paulhauner

This comment has been minimized.

Copy link

commented Apr 13, 2016

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

@Globik

This comment has been minimized.

Copy link

commented Aug 7, 2016

@paulhauner and what is node.js 6? :) is working this asynch sample?

@ustulation

This comment has been minimized.

Copy link

commented Sep 15, 2017

@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) ?

@drllr

This comment has been minimized.

Copy link

commented Apr 30, 2019

Thanks!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.