nodejs addon async callback with libuv

1.3k views Asked by At

I have a node js service that calls into a native C library. The native library, fire events repeatedly and continuously. These events are delivered to a C callback function. My goal is to call a Javascript callback from this native C callback.

Based on my reading, I am using uv_async_init and uv_async_send to accomplish this goal.

The problem I am encountering is that my native C callback function gets called many times and in there uv_async_send is called many times, but the function passed to uv_async_init is called only once and only when my program exits.

Here is my C code:

=====================================================

#include "jsaudio.h"
#include <iostream>

using namespace v8;

static void recordAudioCallback(int index);
void asyncmsg(uv_async_t* handle);
Callback* cbPeriodic;
uv_async_t async;
uv_loop_t* loop;

NAN_METHOD(createEngine) {
    std::cout << "==> createEngine\n" ;
}

void createAudioRecorder(const Nan::FunctionCallbackInfo<v8::Value>& info) {
    std::cout << "==> createAudioRecorder\n";
}

void startRecording(const Nan::FunctionCallbackInfo<v8::Value>& info) {
    std::cout << "==> startRecording\n";
    cbPeriodic = new Callback(info[0].As<Function>());
    loop = uv_default_loop();
    uv_async_init(loop, &async, asyncmsg);
}

void asyncmsg(uv_async_t* handle) {
  std::cout << "==> asyncmsg \n";
  Nan::HandleScope scope;
  v8::Isolate* isolate = v8::Isolate::GetCurrent();
  Local<Value> argv[] = { v8::String::NewFromUtf8(isolate, "Hello world") };
  cbPeriodic->Call(1, argv);
}

/* This my callback that gets called many times, by a native library*/
static void recordAudioCallback(int index) {
  std::cout << "==> recordAudioCallback " << index << "\n";
  uv_async_send(&async);
}

======================================================

Here is my test node.js code that calls the above native code

const jsAudio = new JsAudio({sampleRate: 48000, bufferSize: 8192})

function Test () {
  jsAudio.createEngine();

  jsAudio.createAudioRecorder();

  jsAudio.startRecording(function(error, result) {
    if (error) {
      console.log('startRecording failed: ' + error);
    } else {
      console.log('startRecording result: ' + result);
    }
  });
}

Test();

var waitTill = new Date(new Date().getTime() + 3 * 1000);
while(waitTill > new Date()){}

jsAudio.shutdown();

console.log('program exit...');

==========================================================

Here is the out:

==> createEngine
==> createAudioRecorder
==> startRecording
==> recordAudioCallback 0
==> recordAudioCallback 1
==> recordAudioCallback 0
==> recordAudioCallback 1
==> recordAudioCallback 0
==> shutdown
program exit...
==> asyncmsg 
startRecording failed: Hello world

===========================================================

Why asyncmsg is called only once! even though recordAudioCallback is called several times! Any why is it called after program exits!

Any help is appreciated.

1

There are 1 answers

3
harmic On

The actual execution of Javascript code in node.js is single threaded. This means that callbacks cannot be executed in the middle of some already executing JS code.

Your sample code contains a busy loop which runs continuously for a time, then the program ends. There is no opportunity for your callbacks to be executed while that loop is executing.

Try replacing that with a call to setTimeout to set a timeout at the required interval. Something like this:

const jsAudio = new JsAudio({sampleRate: 48000, bufferSize: 8192})

function Test () {
  jsAudio.createEngine();

  jsAudio.createAudioRecorder();

  jsAudio.startRecording(function(error, result) {
    if (error) {
      console.log('startRecording failed: ' + error);
    } else {
      console.log('startRecording result: ' + result);
    }
  });
}

Test();

setTimeout(function() {
    jsAudio.shutdown();
    console.log('program exit...');
}, 3000);

Note that even though the call to setTimeout would return straight away, your program does not exit. That is because node.js has an event loop which continues as long as there are some pending timers or other events waiting to be processed.

See this section of the node.js documentation for an explanation of the event loop.