Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Asynchronous callbacks in native addons #3289

Closed
justinmchase opened this issue Mar 24, 2015 · 4 comments
Closed

Asynchronous callbacks in native addons #3289

justinmchase opened this issue Mar 24, 2015 · 4 comments

Comments

@justinmchase
Copy link

We have some code that appears to run from iojs command line successfully but fails when run as a native addon in nw.js.

Our native addon has a background thread that is using libuv to queue messages and call javascript callbacks in the right thread. Without going into too much detail, we are initializing our watcher like this:

uv_async_init(uv_default_loop(), &watcher, reinterpret_cast<uv_async_cb>(listener));

From the background thread we are queueing messages and then calling:

uv_async_send(&watcher);

In our callback function we are then doing:

Isolate* isolate = Isolate::GetCurrent();
Local<Context> context = Context::New(isolate); // crashes nwjs with error code 0x800000003
Context::Scope context_scope(context);

We have verified that isolate is not null and that the thread id of the callback function is the same as the thread id from the initialization (the ui thread).

What is the appropriate way to asynchronously call a callback from a native addon in nwjs? Should we be using uv_default_loop() for our init? Is there some other api we should be using?

I can provide more detailed code upon request.

System Info

Win8.1, x64
nw.js v0.12.0
io.js v1.2.0
Chromium 41.0.2272.76
commit hash: f6c7f79-10b9c03-be948af-459755a-2bdc251-c114bb7

@justinmchase
Copy link
Author

Actually it appears that there may be some issue with timing. If we queue and send 10 messages right away they all go through. If we wait 500ms then send a message after that, it crashes. It almost seems like the Isolate is being disposed... is that possible? I figured Isolate::GetCurrent() would return the global UI isolate, what is the isolate creation and reclamation scheme for nwjs?

@justinmchase
Copy link
Author

Here is a more complete code snippet:

#include <node.h>
#include "async.h"

using namespace v8;

static Persistent<Function, CopyablePersistentTraits<Function>> _cb;
Async* _async;

void AsyncCallback(void* data)
{
    int x = *((int*)data);
    auto isolate = Isolate::GetCurrent();
    auto context = isolate->GetCurrentContext(); // crashes nwjs here
    auto global = context->Global();

    const int argc = 1;
    Handle<Value> argv[argc];
    argv[0] = Number::New(isolate, (double)x);

    auto fn = Local<Function>::New(isolate, _cb);
    fn->Call(global, argc, argv);
}

DWORD Worker(LPVOID lparam)
{
    int x = 0;
    while(true) 
    {
        _async->send(&x);
        x++;
        Sleep(1000);
    }
    return S_OK;
}

void Start(const FunctionCallbackInfo<Value>& args)
{
    auto isolate = Isolate::GetCurrent();

    Handle<Function> arg0 = Handle<Function>::Cast(args[0]);
    Persistent<Function> cb(isolate, arg0);
    _cb = cb;

    _async = new Async(&AsyncCallback);
    CreateThread(nullptr, 0, Worker, nullptr, 0, 0);
}

void init(Handle<Object> exports, Handle<Object> module)
{
    NODE_SET_METHOD(exports, "start", Start);
}

NODE_MODULE(evolve_overlay, init)

The Async class in this snippet is very similar to this class:
https://github.com/mapbox/node-sqlite3/blob/master/src/async.h

In my index.html file I am simply doing:

var m = require('./build/Debug/test')
m.start(function (x) {
    console.log(x)
})

I have confirmed that this exact same code built against iojs v1.2.0 (the same version nwjs is using) and run from the command line instead of run through nw does not crash and apears to work as expected. It consistently crashes in nwjs however.

The behavior seems to indicate that the Isolate or the Context is being disposed thus my callback is not able to access it anymore.

Any help here would be greatly appreciated!

@justinmchase
Copy link
Author

Here is the error message I am logging from the isolate->SetFatalErrorHandler(&OnFatalError):

Fatal error!
location: v8::HandleScope::CreateHandle()
message: Cannot create a handle without a HandleScope

I'm not sure who is trying to create the HandleScope or where just yet.

@justinmchase
Copy link
Author

I figured it out! 😄

The problem was that I wasn't creating a scope in my async callback and that seemed to mess with v8. So make sure you do this:

auto isolate = Isolate::GetCurrent();
HandleScope scope(isolate);
auto context = isolate->GetCurrentContext();
auto global = context->Global();

I'm not sure why it works in iojs command line but not in nwjs... but hey, I can move forward now.

You can close this ticket, thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant