Repository navigation
ThreadSafeFunction(napi_threadsafe_function) works badly with BlockingCall() #556
Description
Activity
Hello @o2genum ,
Can you post some code that you are using? Here's some code using BlockingCall() in all three overloads:
node-addon-api/test/threadsafe_function/threadsafe_function.cc
Lines 52 to 66 in dd9fa8a
auto callback = [](Env env, Function jsCallback, int* data) { jsCallback.Call({ Number::New(env, *data) }); }; switch (info->type) { case ThreadSafeFunctionInfo::DEFAULT: status = tsfn.BlockingCall(); break; case ThreadSafeFunctionInfo::BLOCKING: status = tsfn.BlockingCall(&ints[index], callback); break; case ThreadSafeFunctionInfo::NON_BLOCKING: status = tsfn.NonBlockingCall(&ints[index], callback); break; } We have two tests: one using
BlockingCallwith no callback or data, and one usingBlockingCallwith data and callback, passing anint*tocallback, which receives that value.node-addon-api/test/threadsafe_function/threadsafe_function_sum.cc
Lines 35 to 41 in e10e683
void entryWithTSFN(ThreadSafeFunction tsfn, int threadId) { std::this_thread::sleep_for(std::chrono::milliseconds(std::rand() % 100 + 1)); tsfn.BlockingCall( [=](Napi::Env env, Function callback) { callback.Call( { Number::New(env, static_cast<double>(threadId))}); }); tsfn.Release(); } And here we use
BlockingCall()with only callback and no data.Ahh, I am sorry, I misunderstood your issue. Using the node-addon-api with a pre-constructed
napi_threadsafe_functionfails. I will take a look. Thanks!Reacted by Andrey MoiseevNice! By the way, I want to note I could have done without wrapping TSFN from C API into C++ API one if there was a way to call
napi_unref_threadsafe_functionfrom C++ API, to prevent the TSFN from keeping event loop alive.- added a commit that references this issue
on Oct 9, 2019 Hi @o2genum ,
Yeah, so I'm not sure how we can wrap an existing
napi_tsfnin a newThreadSafeFunction... And yep, the main problem is with how theTSFNis not a "1:1 wrapper" with napi_tsfn functions I suppose.When creating a node-addon-api
TSFN, you do not specify acall_js_cbparameter like you do in underlying napi_create_threadsafe_function. Instead, this is passed as the optionalCallbackparameter in the[Non]BlockingCallmethod. (In theTSFNwrapper, this function is used, and the optionalCallbackandDataare passed as a wrapped function pointer inside thevoid* datapointer)I'll need to discuss this with the napi team for a good approach / if it's even possible.
In the mean time, we have two PRs that can solve your problem:
- tsfn: Add wrappers for Ref and Unref #561: This adds wrappers for
napi_ref_threadsafe_function/napi_unref_threadsafe_functiondirectly - tsfn: Implement copy constructor #546: By removing the unique pointer, we can add an
operator napi_threadsafe_function. This will allow you to pass a wrapped TSFN directly tonapi_unref_threadsafe_function
Reacted by Andrey Moiseev- tsfn: Add wrappers for Ref and Unref #561: This adds wrappers for
Hi @gabrielschulhof / @mhdawson ,
I wanted to bring this up at yesterday's meeting but alas...
To address this issue, I can think of two ways but both work off this general paradigm:
- Store new instance property
bool _isInternal - Create private
ThreadSafeFunction(napi_threadsafe_function, bool isInternal)to track that it is an internally-created TSFN wrapper. This constructor is used withinNew - If developer calls existing public
ThreadSafeFunction(napi_threadsafe_function), it would defaultisInternaltofalse - Use
_isInternalinside[Non]BlockingCalland/orCallInternal
Now I say "two" ways because:
1. If we make this_isInternalaconst boolon the wrapper, [I believe] we can use static asserts to throw a compile-time error when the with-callback[Non]BlockingCallmethod is used. However, if it isconst, we can no longer use the assignment constructor to overwrite an existing TSFN with a different one, as the property cannot change.
2. Make it non-const, and throw a run-time error.Edit: option 1 is not feasible
- Store new instance property
As discussed in 21-Oct meeting: The other existing
[Non]BlockingCall()methods cover all use-cases for a node-addon-api created TSFN (no data, with function, with function+data). In order to handle this use-case of an already-existing napi_tsfn, we can create new methodThreadSafeFunction::[Non]BlockingCall(DataType* data)which imply to usedatawith the underlyingnapi_threadsafe_functiondirectly. Can passnullptrto use no data.- added a commit that references this issue
on Oct 23, 2019 - added 2 commits that reference this issue
on Oct 29, 2019 - added a commit that references this issue
on Nov 10, 2019 - added 2 commits that reference this issue
on Aug 24, 2022 - added 2 commits that reference this issue
on Aug 26, 2022 - added 2 commits that reference this issue
on Sep 19, 2022 - added 2 commits that reference this issue
on Aug 11, 2023
When a
ThreadSafeFunctioninstance is created fromnapi_threadsafe_function, itsBlockingCallmethod doesn't work properly:napi_create_threadsafe_functionis used)datapointer is incorrect for some reason.napi_call_threadsafe_functionworks fine.I'm doing this because I want to
napi_unref_threadsafe_functionwithout using all the verbose C APIs.