nbind icon indicating copy to clipboard operation
nbind copied to clipboard

Promise wrapped nbind functions blocks

Open neophob opened this issue 7 years ago • 2 comments

I have a c function that might take several seconds to complete. So I wrap that call as a promise in the JS part of the code. I use this code in a express server - and while the c code is running, express does not answers any requests. this means the c code blocks. here's a little example

#include <unistd.h>
void foo() {
  sleep(30);
}

#include "nbind/nbind.h"
NBIND_GLOBAL() {
  function(foo);
}

My JS code calls this function like this:

return new BlueBirdPromise((resolve) => {
    lib.foo();
    resolve();
  });

Question:

  • Is my implementation invalid?
  • what can I do that this calls do not block?

neophob avatar Jan 04 '18 10:01 neophob

I setup an minimal example project, that shows the issue: https://github.com/neophob/nbind-blocking

Tested with node v6 and v8.

So I guess to fix this behaviour I should use libuv threads (either native or using wrappers like NaN should provide some help) and callbacks. can you point me to an example where this is used?

neophob avatar Jan 04 '18 12:01 neophob

Ok the correct way to address cpu bound tasks (or tasks which run long time) is using the libuv worker threads (uv_queue_work) so the EventLoop is not blocked

I found a excellent example here: https://github.com/paulhauner/example-async-node-addon/blob/master/async-addon/async-addon.cc

but I have now the problem, that I cannot when I call

struct AsyncDeviceInfo {
  uv_work_t request;
  std::unique_ptr<nbind::cbFunction> cb;
};

static void WorkAsync(uv_work_t *req) {
  AsyncDeviceInfo *work = static_cast<AsyncDeviceInfo *>(req->data);    
  sleep(3);
}

static void WorkAsyncComplete(uv_work_t *req) {
  AsyncDeviceInfo *asyncDeviceInfo = static_cast<AsyncDeviceInfo *>(req->data);
  (*asyncDeviceInfo->cb)(0,1);
  delete asyncDeviceInfo;
}

void myExposedFunction(nbind::cbFunction cb) {
  AsyncDeviceInfo *asyncDeviceInfo = new AsyncDeviceInfo();
  asyncDeviceInfo->cb = std::make_unique<nbind::cbFunction>(cb);
  // embed our datastructure to the request
  asyncDeviceInfo->request.data = asyncDeviceInfo;

  int status = uv_queue_work(uv_default_loop(), &asyncDeviceInfo->request, WorkAsync, (uv_after_work_cb) WorkAsyncComplete);
  assert(status == 0);
}

When I use this code, i get this error

FATAL ERROR: v8::HandleScope::CreateHandle() Cannot create a handle without a HandleScope
 1: node::Abort() [/Users/foobar/.nvm/versions/node/v6.9.1/bin/node]
 2: node::FatalException(v8::Isolate*, v8::Local<v8::Value>, v8::Local<v8::Message>) [/Users/foobar/.nvm/versions/node/v6.9.1/bin/node]
 3: v8::Utils::ReportApiFailure(char const*, char const*) [/Users/foobar/.nvm/versions/node/v6.9.1/bin/node]
 4: v8::internal::HandleScope::Extend(v8::internal::Isolate*) [/Users/foobar/.nvm/versions/node/v6.9.1/bin/node]
 5: v8::Integer::New(v8::Isolate*, int) [/Users/foobar/.nvm/versions/node/v6.9.1/bin/node]
 6: nbind::TypeTransformer<int, nbind::PolicyListType<> >::WireType nbind::convertToWire<int>(int) [/Users/foobar/project/node_modules/node-cec/build/Release/nbind.node]
 7: nbind::TypeTransformer<void, nbind::PolicyListType<> >::Type nbind::cbWrapper<void>::call<void, int, int>(int&&, int&&) const [/Users/foobar/project/node_modules/node-cec/build/Release/nbind.node]
 8: WorkAsyncComplete(uv_work_s*) [/Users/foobar/project/node_modules/node-cec/build/Release/nbind.node]
 9: uv__work_done [/Users/foobar/.nvm/versions/node/v6.9.1/bin/node]
10: uv__async_event [/Users/foobar/.nvm/versions/node/v6.9.1/bin/node]
11: uv__async_io [/Users/foobar/.nvm/versions/node/v6.9.1/bin/node]
12: uv__io_poll [/Users/foobar/.nvm/versions/node/v6.9.1/bin/node]
13: uv_run [/Users/foobar/.nvm/versions/node/v6.9.1/bin/node]
14: node::Start(int, char**) [/Users/foobar/.nvm/versions/node/v6.9.1/bin/node]
15: start [/Users/foobar/.nvm/versions/node/v6.9.1/bin/node]

neophob avatar Jan 05 '18 13:01 neophob