-
Notifications
You must be signed in to change notification settings - Fork 30.1k
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
n-api: detect deadlocks in thread-safe function (take two) #32860
n-api: detect deadlocks in thread-safe function (take two) #32860
Conversation
e9f9d39
to
6792324
Compare
@bnoordhuis @addaleax @gireeshpunathil @himself65 this is an alternative to #32823 based on #32823 (comment). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM if CI passes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is what I meant, yes. :-)
@gabrielschulhof Doesn’t this potentially do the wrong thing? We don’t want to know whether this is a JS thread, we want to know whether it’s the thread on which the loop runs to which this I definitely have a preference for #32823 here, and to me it seems like Ben’s original request for something that doesn’t create “invalid” |
@addaleax with this PR, the TSFN would be overly cautious. If you called a TSFN created on one JS thread from another JS thread, it would return Of course, the correct solution is to not call |
@addaleax also, in practice, as you also point out, the I will add this observation to the documentation surrounding |
@gabrielschulhof Yeah, I guess my position is more based on the fact that thread_local variables usually are code smells. What if an embedder decides to move running one Isolate/event loop combination from one thread to another? That probably not a common scenario, but it is for example something that was brought up in discussions that I have participated in. Tbh, if you want to figure out whether you’re currently on a JS thread, I’d check |
@addaleax checking |
@addaleax ... until we can have a |
Using $ git diff 96eceb7 -- src/node_api.cc diff --git a/src/node_api.cc b/src/node_api.cc
index fad9cf72a9..89ec03b088 100644
--- a/src/node_api.cc
+++ b/src/node_api.cc
@@ -154,6 +154,14 @@ class ThreadSafeFunction : public node::AsyncResource {
!is_closing) {
if (mode == napi_tsfn_nonblocking) {
return napi_queue_full;
+ } else if (v8::Isolate::GetCurrent() != nullptr) {
+ // Return `napi_would_deadlock` because waiting on the queue from a JS
+ // thread will prevent that same thread from removing items from the
+ // queue thereby causing deadlock. Deadlock can also be caused by one JS
+ // thread calling the thread-safe function of another JS thread because
+ // if two JS threads call each other's thread-safe functions blockingly
+ // when both their queues are full they will both deadlock.
+ return napi_would_deadlock;
}
cond->Wait(lock);
} where 96eceb7 is the commit before the deadlock detection was introduced. |
@bnoordhuis @himself65 @addaleax please take another look! |
@addaleax I also updated the comment accompanying the return of |
We introduce status `napi_would_deadlock` to be used as a return status by `napi_call_threadsafe_function` if the call is made with `napi_tsfn_blocking` on the main thread and the queue is full. Fixes: #32615 Signed-off-by: Gabriel Schulhof <gabriel.schulhof@intel.com> PR-URL: #32860 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com> Reviewed-By: Zeyu Yang <himself65@outlook.com>
Landed in d26ca06. |
We introduce status `napi_would_deadlock` to be used as a return status by `napi_call_threadsafe_function` if the call is made with `napi_tsfn_blocking` on the main thread and the queue is full. Fixes: nodejs#32615 Signed-off-by: Gabriel Schulhof <gabriel.schulhof@intel.com> PR-URL: nodejs#32860 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com> Reviewed-By: Zeyu Yang <himself65@outlook.com>
We introduce status `napi_would_deadlock` to be used as a return status by `napi_call_threadsafe_function` if the call is made with `napi_tsfn_blocking` on the main thread and the queue is full. Fixes: #32615 Signed-off-by: Gabriel Schulhof <gabriel.schulhof@intel.com> PR-URL: #32860 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com> Reviewed-By: Zeyu Yang <himself65@outlook.com>
We introduce status `napi_would_deadlock` to be used as a return status by `napi_call_threadsafe_function` if the call is made with `napi_tsfn_blocking` on the main thread and the queue is full. Fixes: #32615 Signed-off-by: Gabriel Schulhof <gabriel.schulhof@intel.com> PR-URL: #32860 Backport-PR-URL: #32948 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com> Reviewed-By: Zeyu Yang <himself65@outlook.com>
Notable changes: - deps: upgrade openssl sources to 1.1.1g (Hassaan Pasha) [#32971](#32971) - doc: add juanarbol as collaborator (Juan José Arboleda) [#32906](#32906) - http: doc deprecate abort and improve docs (Robert Nagy) [#32807](#32807) - module: do not warn when accessing `__esModule` of unfinished exports (Anna Henningsen) [#33048](#33048) - n-api: detect deadlocks in thread-safe function (Gabriel Schulhof) [#32860](#32860) - src: deprecate embedder APIs with replacements (Anna Henningsen) [#32858](#32858) - stream: - don't emit end after close (Robert Nagy) [#33076](#33076) - don't wait for close on legacy streams (Robert Nagy) [#33058](#33058) - pipeline should only destroy un-finished streams (Robert Nagy) [#32968](#32968) PR-URL: #33103
Notable changes: - deps: upgrade openssl sources to 1.1.1g (Hassaan Pasha) [#32971](#32971) - doc: add juanarbol as collaborator (Juan José Arboleda) [#32906](#32906) - http: doc deprecate abort and improve docs (Robert Nagy) [#32807](#32807) - module: do not warn when accessing `__esModule` of unfinished exports (Anna Henningsen) [#33048](#33048) - n-api: detect deadlocks in thread-safe function (Gabriel Schulhof) [#32860](#32860) - src: deprecate embedder APIs with replacements (Anna Henningsen) [#32858](#32858) - stream: - don't emit end after close (Robert Nagy) [#33076](#33076) - don't wait for close on legacy streams (Robert Nagy) [#33058](#33058) - pipeline should only destroy un-finished streams (Robert Nagy) [#32968](#32968) - vm: add importModuleDynamically option to compileFunction (Gus Caplan) [#32985](#32985) PR-URL: #33103
Notable changes: - deps: upgrade openssl sources to 1.1.1g (Hassaan Pasha) [#32971](#32971) - doc: add juanarbol as collaborator (Juan José Arboleda) [#32906](#32906) - http: doc deprecate abort and improve docs (Robert Nagy) [#32807](#32807) - module: do not warn when accessing `__esModule` of unfinished exports (Anna Henningsen) [#33048](#33048) - n-api: detect deadlocks in thread-safe function (Gabriel Schulhof) [#32860](#32860) - src: deprecate embedder APIs with replacements (Anna Henningsen) [#32858](#32858) - stream: - don't emit end after close (Robert Nagy) [#33076](#33076) - don't wait for close on legacy streams (Robert Nagy) [#33058](#33058) - pipeline should only destroy un-finished streams (Robert Nagy) [#32968](#32968) - vm: add importModuleDynamically option to compileFunction (Gus Caplan) [#32985](#32985) PR-URL: #33103
Notable changes: - deps: upgrade openssl sources to 1.1.1g (Hassaan Pasha) [#32971](#32971) - doc: add juanarbol as collaborator (Juan José Arboleda) [#32906](#32906) - http: doc deprecate abort and improve docs (Robert Nagy) [#32807](#32807) - module: do not warn when accessing `__esModule` of unfinished exports (Anna Henningsen) [#33048](#33048) - n-api: detect deadlocks in thread-safe function (Gabriel Schulhof) [#32860](#32860) - src: deprecate embedder APIs with replacements (Anna Henningsen) [#32858](#32858) - stream: - don't emit end after close (Robert Nagy) [#33076](#33076) - don't wait for close on legacy streams (Robert Nagy) [#33058](#33058) - pipeline should only destroy un-finished streams (Robert Nagy) [#32968](#32968) - vm: add importModuleDynamically option to compileFunction (Gus Caplan) [#32985](#32985) PR-URL: #33103
We introduce status `napi_would_deadlock` to be used as a return status by `napi_call_threadsafe_function` if the call is made with `napi_tsfn_blocking` on the main thread and the queue is full. Fixes: #32615 Signed-off-by: Gabriel Schulhof <gabriel.schulhof@intel.com> PR-URL: #32860 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com> Reviewed-By: Zeyu Yang <himself65@outlook.com>
This reverts commit d26ca06 because it breaks running the tests in debug mode, as `v8::Isolate::GetCurrent()` is not allowed if no `Isolate` is active on the current thread. Refs: nodejs#33276 Refs: nodejs#32860
This reverts commit d26ca06 because it breaks running the tests in debug mode, as `v8::Isolate::GetCurrent()` is not allowed if no `Isolate` is active on the current thread. Refs: nodejs#33276 Refs: nodejs#32860 PR-URL: nodejs#33453 Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: David Carlier <devnexen@gmail.com> Reviewed-By: Gireesh Punathil <gpunathi@in.ibm.com> Reviewed-By: Gabriel Schulhof <gabriel.schulhof@intel.com>
This reverts commit d26ca06 because it breaks running the tests in debug mode, as `v8::Isolate::GetCurrent()` is not allowed if no `Isolate` is active on the current thread. Refs: #33276 Refs: #32860 PR-URL: #33453 Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: David Carlier <devnexen@gmail.com> Reviewed-By: Gireesh Punathil <gpunathi@in.ibm.com> Reviewed-By: Gabriel Schulhof <gabriel.schulhof@intel.com>
This reverts commit d26ca06 because it breaks running the tests in debug mode, as `v8::Isolate::GetCurrent()` is not allowed if no `Isolate` is active on the current thread. Refs: #33276 Refs: #32860 PR-URL: #33453 Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: David Carlier <devnexen@gmail.com> Reviewed-By: Gireesh Punathil <gpunathi@in.ibm.com> Reviewed-By: Gabriel Schulhof <gabriel.schulhof@intel.com>
This reverts commit d26ca06 because it breaks running the tests in debug mode, as `v8::Isolate::GetCurrent()` is not allowed if no `Isolate` is active on the current thread. Refs: #33276 Refs: #32860 PR-URL: #33453 Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: David Carlier <devnexen@gmail.com> Reviewed-By: Gireesh Punathil <gpunathi@in.ibm.com> Reviewed-By: Gabriel Schulhof <gabriel.schulhof@intel.com>
uv_thread_self()
works on Windows only for threads created usinguv_thread_start()
because libuv does not useGetCurrentThreadId()
for threads that were created otherwise.
Thus we must use a thread-local static boolean which we set to
true
only on JS threads to distinguish JS threads from secondary threads,
thereby preventing deadlocks.
Signed-off-by: Gabriel Schulhof gabriel.schulhof@intel.com
Checklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passes