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

Calling n-api methods from own separate thread #1035

Closed
gryor opened this issue Dec 22, 2017 · 5 comments
Closed

Calling n-api methods from own separate thread #1035

gryor opened this issue Dec 22, 2017 · 5 comments

Comments

@gryor
Copy link

gryor commented Dec 22, 2017

  • Node.js Version: v9.3.0
  • OS: Arch Linux 64-bit, Kernel 4.14.6
  • Scope (install, code, runtime, meta, other?): code
  • Module (and version) (if relevant):

I'm trying to create a module that has an application compiled within.
The application itself needs own thread to allow node keep running simultaneously.
When the application calls n-api functions from its own thread, node will crash.
Probably a related issue is that the documentation states the environment variable must not be cached for later use. Then how could I create/get an environment for calls, which do not originate from node?

#
# Fatal error in ../deps/v8/src/isolate.h, line 536
# Debug check failed: (isolate) != nullptr.
#
(node:8606) Warning: N-API is an experimental feature and could change at any time.

Thread 7 "node" received signal SIGILL, Illegal instruction.
[Switching to Thread 0x7f1decf11700 (LWP 8615)]
v8::base::OS::Abort () at ../deps/v8/src/base/platform/platform-posix.cc:176
176         V8_IMMEDIATE_CRASH();
(gdb) bt full
#0  v8::base::OS::Abort () at ../deps/v8/src/base/platform/platform-posix.cc:176
No locals.
#1  0x00005627fd91b9a3 in V8_Fatal (file=0x5627fda7aee7 "../deps/v8/src/isolate.h", line=536, format=0x5627fdf08395 "Debug check failed: %s.")
    at ../deps/v8/src/base/logging.cc:138
        arguments = {{gp_offset = 32, fp_offset = 48, overflow_arg_area = 0x7f1decf10ab0, reg_save_area = 0x7f1decf109f0}}
#2  0x00005627fd91a938 in v8::base::(anonymous namespace)::DefaultDcheckHandler (file=0x5627fda7aee7 "../deps/v8/src/isolate.h", line=536,
    message=0x5627fda7af00 "(isolate) != nullptr") at ../deps/v8/src/base/logging.cc:56
No locals.
#3  0x00005627fd91b9cd in V8_Dcheck (file=0x5627fda7aee7 "../deps/v8/src/isolate.h", line=536, message=0x5627fda7af00 "(isolate) != nullptr")
    at ../deps/v8/src/base/logging.cc:142
No locals.
#4  0x00005627fc99ce41 in v8::internal::Isolate::Current () at ../deps/v8/src/isolate.h:536
        isolate = 0x0
#5  0x00005627fc9ae8f9 in v8::Utils::ReportApiFailure (location=0x5627fda7e63c "HandleScope::HandleScope",
    message=0x5627fda7e608 "Entering the V8 API without proper locking in place") at ../deps/v8/src/api.cc:405
        isolate = 0x7f1decf10b80
        callback = 0x5627fc9acb25 <v8::internal::ThreadManager::IsLockedByCurrentThread()+59>
#6  0x00005627fc99d769 in v8::Utils::ApiCheck (condition=false, location=0x5627fda7e63c "HandleScope::HandleScope",
    message=0x5627fda7e608 "Entering the V8 API without proper locking in place") at ../deps/v8/src/api.h:124
No locals.
#7  0x00005627fc9b10a8 in v8::HandleScope::Initialize (this=0x7f1de40923f0, isolate=0x5627ffb92350) at ../deps/v8/src/api.cc:1048
        internal_isolate = 0x5627ffb92350
        current = 0xffffffffffffff88
#8  0x00005627fc9b1035 in v8::HandleScope::HandleScope (this=0x7f1de40923f0, isolate=0x5627ffb92350) at ../deps/v8/src/api.cc:1037
No locals.
#9  0x00005627fd67138f in (anonymous namespace)::v8impl::HandleScopeWrapper::HandleScopeWrapper (this=0x7f1de40923f0, isolate=0x5627ffb92350)
    at ../src/node_api.cc:176
No locals.
#10 0x00005627fd67b6bd in napi_open_handle_scope (env=0x5627ffc2ce30, result=0x7f1decf10c68) at ../src/node_api.cc:2602
No locals.
int my_func(int id)
{
  int ret;
  napi_handle_scope scope;
  napi_value result;
  napi_value this;
  napi_value func;
  const int argc = 1;
  napi_value argv[argc];

  printf("id start %i\n", id);
  puts("trying to open scope");

  if (napi_ok != napi_open_handle_scope(env, &scope))
    napi_throw_error(env, 0, "Could not open a scope");

  puts("scope open");

  if (napi_ok != napi_get_reference_value(env, functions.this, &this))
    napi_throw_error(env, 0, "Could not get this");

  if (napi_ok != napi_get_reference_value(env, functions.id, &func))
    napi_throw_error(env, 0, "Could not get func");

  if (napi_ok != napi_create_int32(env, id, &argv[0]))
    napi_throw_error(env, 0, "Argument conversion error");

  if (napi_ok != napi_call_function(env, this, func, argc, argv, &result))
    napi_throw_error(env, 0, "Could not call javascript function");

  if (napi_ok != napi_get_value_int32(env, result, &ret))
    napi_throw_error(env, 0, "Could not retrieve the result");

  if (napi_ok != napi_close_handle_scope(env, scope))
    napi_throw_error(env, 0, "Could not close a scope");

  printf("id end %i %i\n", id, ret);

  return ret;
}
@bnoordhuis
Copy link
Member

When the application calls n-api functions from its own thread, node will crash.

Yes, you can't do that. I'm fairly sure it's mentioned in the docs.

I believe there were plans to wrap uv_async_t to make it a little easier to make callbacks from other threads but a quick glance at the docs suggests it hasn't landed yet.

@bnoordhuis
Copy link
Member

Found it: nodejs/node#17809 - still in progress.

@gireeshpunathil
Copy link
Member

nodejs/node#17809 closed in lieu of nodejs/node#17887

gabrielschulhof pushed a commit to nodejs/node that referenced this issue Jun 29, 2018
Bundle a `uv_async_t`, a `uv_idle_t`, a `uv_mutex_t`, a `uv_cond_t`,
and a `v8::Persistent<v8::Function>` to make it possible to call into JS
from another thread. The API accepts a void data pointer and a callback
which will be invoked on the loop thread and which will receive the
`napi_value` representing the JavaScript function to call so as to
perform the call into JS. The callback is run inside a
`node::CallbackScope`.

A `std::queue<void*>` is used to store calls from the secondary
threads, and an idle loop is started by the `uv_async_t` callback on the
loop thread to drain the queue, calling into JS with each item.

Items can be added to the queue blockingly or non-blockingly.

The thread-safe function can be referenced or unreferenced, with the
same semantics as libuv handles.

Re: nodejs/help#1035
Re: #20964
Fixes: #13512
PR-URL: #17887
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
targos pushed a commit to nodejs/node that referenced this issue Jun 30, 2018
Bundle a `uv_async_t`, a `uv_idle_t`, a `uv_mutex_t`, a `uv_cond_t`,
and a `v8::Persistent<v8::Function>` to make it possible to call into JS
from another thread. The API accepts a void data pointer and a callback
which will be invoked on the loop thread and which will receive the
`napi_value` representing the JavaScript function to call so as to
perform the call into JS. The callback is run inside a
`node::CallbackScope`.

A `std::queue<void*>` is used to store calls from the secondary
threads, and an idle loop is started by the `uv_async_t` callback on the
loop thread to drain the queue, calling into JS with each item.

Items can be added to the queue blockingly or non-blockingly.

The thread-safe function can be referenced or unreferenced, with the
same semantics as libuv handles.

Re: nodejs/help#1035
Re: #20964
Fixes: #13512
PR-URL: #17887
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
@gireeshpunathil
Copy link
Member

@wilhelmmatilainen - the master has the linked PR landed. Could you please check your code with that and let me know if your problem is resolved? thanks!

@gryor gryor closed this as completed Aug 3, 2018
@gryor
Copy link
Author

gryor commented Aug 3, 2018

The issue is resolved with the new threadsafe functions.

gabrielschulhof pushed a commit to gabrielschulhof/node that referenced this issue Dec 28, 2018
Bundle a `uv_async_t`, a `uv_idle_t`, a `uv_mutex_t`, a `uv_cond_t`,
and a `v8::Persistent<v8::Function>` to make it possible to call into JS
from another thread. The API accepts a void data pointer and a callback
which will be invoked on the loop thread and which will receive the
`napi_value` representing the JavaScript function to call so as to
perform the call into JS. The callback is run inside a
`node::CallbackScope`.

A `std::queue<void*>` is used to store calls from the secondary
threads, and an idle loop is started by the `uv_async_t` callback on the
loop thread to drain the queue, calling into JS with each item.

Items can be added to the queue blockingly or non-blockingly.

The thread-safe function can be referenced or unreferenced, with the
same semantics as libuv handles.

Re: nodejs/help#1035
Re: nodejs#20964
Fixes: nodejs#13512
PR-URL: nodejs#17887
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
MylesBorins pushed a commit to nodejs/node that referenced this issue Jan 18, 2019
Bundle a `uv_async_t`, a `uv_idle_t`, a `uv_mutex_t`, a `uv_cond_t`,
and a `v8::Persistent<v8::Function>` to make it possible to call into JS
from another thread. The API accepts a void data pointer and a callback
which will be invoked on the loop thread and which will receive the
`napi_value` representing the JavaScript function to call so as to
perform the call into JS. The callback is run inside a
`node::CallbackScope`.

A `std::queue<void*>` is used to store calls from the secondary
threads, and an idle loop is started by the `uv_async_t` callback on the
loop thread to drain the queue, calling into JS with each item.

Items can be added to the queue blockingly or non-blockingly.

The thread-safe function can be referenced or unreferenced, with the
same semantics as libuv handles.

Re: nodejs/help#1035
Re: #20964
Fixes: #13512
Backport-PR-URL: #25002
PR-URL: #17887
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
rvagg pushed a commit to nodejs/node that referenced this issue Feb 28, 2019
Bundle a `uv_async_t`, a `uv_idle_t`, a `uv_mutex_t`, a `uv_cond_t`,
and a `v8::Persistent<v8::Function>` to make it possible to call into JS
from another thread. The API accepts a void data pointer and a callback
which will be invoked on the loop thread and which will receive the
`napi_value` representing the JavaScript function to call so as to
perform the call into JS. The callback is run inside a
`node::CallbackScope`.

A `std::queue<void*>` is used to store calls from the secondary
threads, and an idle loop is started by the `uv_async_t` callback on the
loop thread to drain the queue, calling into JS with each item.

Items can be added to the queue blockingly or non-blockingly.

The thread-safe function can be referenced or unreferenced, with the
same semantics as libuv handles.

Re: nodejs/help#1035
Re: #20964
Fixes: #13512
Backport-PR-URL: #25002
PR-URL: #17887
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
abhishekumar-tyagi pushed a commit to abhishekumar-tyagi/node that referenced this issue May 5, 2024
Bundle a `uv_async_t`, a `uv_idle_t`, a `uv_mutex_t`, a `uv_cond_t`,
and a `v8::Persistent<v8::Function>` to make it possible to call into JS
from another thread. The API accepts a void data pointer and a callback
which will be invoked on the loop thread and which will receive the
`napi_value` representing the JavaScript function to call so as to
perform the call into JS. The callback is run inside a
`node::CallbackScope`.

A `std::queue<void*>` is used to store calls from the secondary
threads, and an idle loop is started by the `uv_async_t` callback on the
loop thread to drain the queue, calling into JS with each item.

Items can be added to the queue blockingly or non-blockingly.

The thread-safe function can be referenced or unreferenced, with the
same semantics as libuv handles.

Re: nodejs/help#1035
Re: nodejs/node#20964
Fixes: nodejs/node#13512
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

3 participants