nodejs / abi-stable-node

Repository used by the Node-API team to manage work related to Node-API and node-addon-api
239 stars 47 forks source link

Threadsafe function call executes after everything finished. #369

Closed necil closed 5 years ago

necil commented 5 years ago

Hello I am currently using N-API on node v11.12.0

I have two threads on addon side. first thread is my main thread and second one contains a loop that calls a function with specific interval.

I create thread safe function as follows (part of register() function):

  napi_value global, add_two;
  napi_status status = napi_get_global(env, &global);
  status = napi_get_named_property(env, global, "AddTwo", &add_two);
  napi_threadsafe_function func;
  napi_value str;
  napi_create_string_utf8(env, "no_name", NAPI_AUTO_LENGTH, &str);

  status = napi_create_threadsafe_function(env, add_two, 0, str, 0, 4, 0, my_finalize, 0, 
  my_callback, &func);

Also callback function:

void my_callback(napi_env env, napi_value js_callback, void* context, void* data)
{
    napi_value global, add_two, arg;
    napi_status status = napi_get_global(env, &global);

    status = napi_create_int32(env, 1337, &arg);
    if (status != napi_ok) return;

    napi_value* argv = &arg;
    size_t argc = 1;

    napi_value return_val;
    status = napi_call_function(env, global, js_callback, argc, argv, &return_val);
    if (status != napi_ok) return;
}

nodejs code:

global.AddTwo = function (num) {
    console.log("test");
    return num + 2;
}
register();  //this function starts the thread that triggers js function

for(var i = 0; i < 10; i++){
  console.log("pass1");
  sleep(200);//sleeps for 200 ms
}

for(var i = 0; i < 10; i++){
  console.log("pass2");
  sleep(200);//sleeps for 200 ms
}

I see the log test. However it happens after other two for loops completes. By the way interval of this call is much less than these for loop times. Therefore I expect to see a mixed logs of pass1 or pass2 and test. As I mentioned above it starts to work through the loops after both loops finished it starts to print test as it triggers from native code. Since it passes both loops I am not suspecting that I do not block event loop completely(thats why I use to second loop). It seems like i am making a mistake and my callback only runs at when threadsafe function's destructor is reached. Am I missing anything or this is the expected behavior of this functionality?

gabrielschulhof commented 5 years ago

@necil You didn't post what the secondary thread actually does, but I assume it calls the thread-safe function several times, or even in a loop.

The fact that you see "test" after both loops are done is the expected behaviour for the code you show, because sleep() does not mean "suspend execution of JavaScript thereby giving Node.js a chance to check the event loop". It means "prevent the main thread from running for 200 ms". This also prevents Node.js from examining the event loop. So, although you have sleep() in your loop, it's still a "busy" loop in the sense that it does not allow examination of the event loop while the loop is executing.

gabrielschulhof commented 5 years ago

You can get interleaving if you replace the busy loops with idle loops:

global.AddTwo = function (num) {
  process.stdout.write('\r\033[Ktest ' + num + '\r');
};

register();

new Promise((resolve) => {
  let i = 0;
  const interval = setInterval(() => {
    console.log('\033[Kpass1');
    i++;
    if (i == 10) {
      clearInterval(interval);
      resolve();
    }
  }, 200);
})
.then(() => {
  return new Promise((resolve) => {
    let i = 0;
    const interval = setInterval(() => {
      console.log('\033[Kpass2');
      i++;
      if (i == 10) {
        clearInterval(interval);
        resolve();
      }
    }, 200);
  });
});

I added some escape sequences to the output formatting because if you have the secondary thread run in a busy loop the words "test " totally flood the screen.

necil commented 5 years ago

Thank you i will try it

necil commented 5 years ago

Tried and it works as it should. Thanks a lot. By the way I know my code was lacking some details, its because some parts of the code contains business logic and not possible for me to post that part online. Sorry for inconvenience.

gabrielschulhof commented 5 years ago

@necil no problem. Glad you got it sorted out 🙂