Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
116 commits
Select commit Hold shift + click to select a range
ea85768
deps: V8: cherry-pick dc3a90be6ca7
targos Apr 12, 2020
cc0395c
Revert "n-api: detect deadlocks in thread-safe function"
Apr 16, 2020
fd4320c
n-api: detect deadlocks in thread-safe function
Apr 6, 2020
b80b08f
worker: fix type check in receiveMessageOnPort
addaleax Apr 9, 2020
1996198
doc: add puzpuzpuz to collaborators
puzpuzpuz Apr 13, 2020
f4e9bd6
test: use symlinks to copy shells
Apr 7, 2020
c6078f0
build: add build from tarball
Mar 6, 2020
b304df9
async_hooks: prevent sync methods of async storage exiting outer context
Qard Feb 25, 2020
52d8afc
async_hooks: merge run and exit methods
puzpuzpuz Mar 14, 2020
ea09c0f
doc: document `buffer.from` returns internal pool buffer
HarshithaKP Apr 7, 2020
bff11a9
src: remove unused v8 Array namespace
juanarbol Apr 10, 2020
c0e4ac4
doc: improve AsyncLocalStorage sample
puzpuzpuz Apr 10, 2020
d937874
buffer: mark pool ArrayBuffer as untransferable
addaleax Apr 10, 2020
1436f53
tls: provide default cipher list from command line
addaleax Apr 10, 2020
a1178b6
buffer: add type check in bidirectionalIndexOf
Flarna Apr 10, 2020
95cd771
doc: remove warning from `response.writeHead`
cecchi Apr 7, 2020
c8d4360
build: use same flags as V8 for ASAN
mmarchini Apr 10, 2020
5c4d8cd
build: re-enable ASAN Action using clang
mmarchini Mar 21, 2020
a0be60e
doc: updated guidance for n-api changes
mhdawson Apr 8, 2020
8fb7852
cli, report: move --report-on-fatalerror to stable
cjihrig Mar 26, 2020
ae034c4
doc: add transform stream destroy() return value
cjihrig Apr 11, 2020
e1809c8
build: add configure flag to build V8 with DCHECKs
addaleax Apr 11, 2020
83f1e98
lib: created isValidCallback helper
yashLadha Apr 5, 2020
2f7e372
src: ignore GCC -Wcast-function-type for v8.h
danbev Apr 6, 2020
f1273e8
test: cover node entry type in perf_hooks
julianduque Apr 10, 2020
b7da587
doc: note that signatures of binary may be from subkeys
haqer1 Apr 1, 2020
98db564
test: mark test-http2-reset-flood flaky on all
sam-github Apr 13, 2020
edc10d4
test: mark test-worker-prof flaky on arm
sam-github Apr 13, 2020
46cafad
test: replace equal with strictEqual
JesuHrz Apr 8, 2020
3b590d4
process: suggest --trace-warnings when printing warning
addaleax Apr 12, 2020
5b01772
src: use basename(argv0) for --trace-uncaught suggestion
addaleax Apr 12, 2020
85b333b
http: refactor agent 'free' handler
ronag Apr 12, 2020
23a4d60
test: mark cpu-prof-dir-worker flaky on all
sam-github Apr 13, 2020
8774cb4
fs: use finished over destroy w/ cb
ronag Apr 13, 2020
3f4bb8d
doc: improve net docs
ronag Apr 13, 2020
740f864
test: only detect uname on supported os
dmabupt Apr 13, 2020
4baf41f
test: replace console.log/error() with debuglog
daemon1024 Apr 6, 2020
832ea52
build: remove .git folders when testing V8
richardlau Apr 16, 2020
7980f6f
doc: improve consistency in usage of NULL
mhdawson Apr 8, 2020
a72d1d3
src: remove unused using in node_worker.cc
danbev Apr 14, 2020
520347c
module: fix memory leak when require error occurs
lianxuify Apr 14, 2020
5dba49d
doc: missing brackets
Apr 4, 2020
0c7391c
module: remove experimental modules warning
guybedford Feb 26, 2020
cc47645
doc: add juanarbol as collaborator
juanarbol Apr 17, 2020
45a125c
doc: add N-API version 6 to table
mhdawson Apr 13, 2020
9fd0c35
src: remove redundant v8::HeapSnapshot namespace
juanarbol Apr 14, 2020
f7b25c0
tools: decrease timeout in test.py
addaleax Apr 15, 2020
e529a32
src: elevate v8 namespaces
nimit95 Apr 15, 2020
87149c4
test: changed function to arrow function
nimit95 Apr 16, 2020
43adbe6
doc: add `tsc-agenda` to onboarding labels list
Trott Apr 13, 2020
b36eb75
stream: inline unbuffered _write
ronag Apr 16, 2020
507240c
stream: close iterator in Readable.from
vadzim Apr 14, 2020
8a85afa
lib: remove unnecesary else block
ddazal Apr 3, 2020
0b2cff2
fs: remove unnecessary else statement
JesuHrz Apr 4, 2020
c1f54c7
src: remove validation of unreachable code
juanarbol Apr 13, 2020
8c1a69c
doc: synch SECURITY.md with website
Trott Apr 17, 2020
771ca7d
deps: upgrade to libuv 1.36.0
cjihrig Apr 15, 2020
afe7f41
deps: upgrade to libuv 1.37.0
cjihrig Apr 19, 2020
7bd51fb
perf_hooks: remove unnecessary assignment when name is undefined
rickyes Apr 18, 2020
04b1f63
fs: extract kWriteFileMaxChunkSize constant
rickyes Apr 3, 2020
c596759
doc: fix typo in zlib.md
chenmnkken Apr 17, 2020
76e960c
doc: fix usage of folder and directory terms in fs.md
karan1205 Apr 18, 2020
944e010
test: mark test-child-process-fork-args as flaky on Windows
puzpuzpuz Apr 20, 2020
faeb408
doc: corrected ERR_SOCKET_CANNOT_SEND message
willarmiros Apr 14, 2020
0fdc55f
src: fix null deref in AllocatedBuffer::clear
fowles Apr 16, 2020
8c48d16
doc: fix typo in security-release-process.md
SASUKE40 Apr 19, 2020
4432bb2
module: partial doc removal of --experimental-modules
MylesBorins Apr 18, 2020
bfa19c4
tls: move getAllowUnauthorized to internal/options
jasnell Apr 18, 2020
e540d5c
module: improve error for invalid package targets
MylesBorins Apr 17, 2020
cb93c60
module: exports not exported for null resolutions
guybedford Apr 14, 2020
a673c8f
http2: wait for secureConnect before initializing
Apr 21, 2020
27837fe
fs: update validateOffsetLengthRead in utils.js
daemon1024 Apr 17, 2020
015f33c
src: use using NewStringType
rickyes Apr 14, 2020
c402edd
tools: remove unused code in doc generation tool
Trott Apr 18, 2020
287bd8a
doc: elevate diagnostic report to tier1
gireeshpunathil Mar 25, 2020
99f4af4
doc: remove repeated word in modules.md
kodekage Apr 19, 2020
347f71a
deps: V8: cherry-pick e1eac1b16c96
Apr 21, 2020
c3025f3
test: test-async-wrap-constructor prefer forEach
dericop Apr 3, 2020
2eb5262
doc: add angle brackets around implicit links
nschonni Apr 6, 2020
f7c7713
doc: ignore no-literal-urls in changelogs
nschonni Apr 6, 2020
a5bed1e
doc: convert bare email addresses to mailto links
nschonni Apr 23, 2020
e372b8b
doc: ignore no-literal-urls in README
nschonni Apr 23, 2020
2c364d4
lib: simplify function process.emitWarning
himself65 Apr 22, 2020
4169bc4
test: refactor events tests for invalid listeners
edsadr Apr 11, 2020
fae1aed
doc: updated directory entry information
Apr 12, 2020
a223ccc
test: better error validations for event-capture
edsadr Apr 10, 2020
bd096bd
src: delete MicroTaskPolicy namespace
juanarbol Apr 14, 2020
d178250
lib: unnecessary const assignment for class
yashLadha Apr 21, 2020
5dc4349
inspector: only write coverage in fully bootstrapped Environments
joyeecheung Apr 21, 2020
8809699
vm: add importModuleDynamically option to compileFunction
devsnek Apr 21, 2020
f155690
src: assignment to valid type
yashLadha Apr 16, 2020
790c453
test: remove timers-blocking-callback
Fishrock123 Apr 15, 2020
dd1cc1a
doc: avoid tautology in README
ishaanjain1898 Apr 22, 2020
18fd841
buffer,n-api: fix double ArrayBuffer::Detach() during cleanup
addaleax Apr 24, 2020
5caa627
deps: V8: backport 3f8dc4b2e5ba
ryzokuken Apr 22, 2020
f6274af
tls: add highWaterMark option for connect
rickyes Apr 11, 2020
29d7202
deps: upgrade openssl sources to 1.1.1g
hassaanp Apr 21, 2020
28c39d3
deps: update archs files for OpenSSL-1.1.1g
hassaanp Apr 21, 2020
9ebcd79
doc: add documentation for transferList arg at worker threads
juanarbol Apr 16, 2020
428fca1
worker: fix process.env var empty key access
bl-ue Apr 26, 2020
018de28
src: fix empty-named env var assertion failure
bl-ue Apr 18, 2020
14cf904
src: do not compare against wide characters
bl-ue Apr 19, 2020
0f4d513
cluster: removed unused addressType argument from constructor
yashLadha Apr 21, 2020
c239cc6
util,readline: NFC-normalize strings before getStringWidth
addaleax Apr 25, 2020
6984fbc
test: refactor test-async-hooks-constructor
himself65 Apr 25, 2020
6b6c8d0
doc: document major finished changes in v14
ronag Apr 25, 2020
b953ab6
src: add AsyncWrapObject constructor template factory
Qard Apr 25, 2020
79d92b2
doc: improve release documentation
targos Apr 24, 2020
c97a7ce
module: refactor condition
MylesBorins Apr 22, 2020
be55aac
doc: make openssl maintenance position independent
sam-github Apr 21, 2020
bd40a8b
build: fix vcbuild error for missing Visual Studio
Hakerh400 Apr 4, 2020
de3ab9f
doc: improve WHATWG url constructor code example
lirantal Apr 11, 2020
4fc7877
n-api: fix false assumption on napi_async_context structures
legendecas Apr 19, 2020
9d18408
http: doc deprecate abort and improve docs
ronag Apr 13, 2020
386d158
doc: assign missing deprecation code
richardlau Apr 27, 2020
9fc74f1
2020-04-29, Version 13.14.0 (Current)
BridgeAR Apr 28, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
n-api: detect deadlocks in thread-safe function
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>
  • Loading branch information
Gabriel Schulhof authored and targos committed Apr 28, 2020
commit fd4320c49f0731ab8742cd2dbe871f20a7037ebb
28 changes: 26 additions & 2 deletions doc/api/n-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,7 @@ typedef enum {
napi_date_expected,
napi_arraybuffer_expected,
napi_detachable_arraybuffer_expected,
napi_would_deadlock,
} napi_status;
```

Expand Down Expand Up @@ -5095,6 +5096,19 @@ preventing data from being successfully added to the queue. If set to
`napi_call_threadsafe_function()` never blocks if the thread-safe function was
created with a maximum queue size of 0.

As a special case, when `napi_call_threadsafe_function()` is called from a
JavaScript thread, it will return `napi_would_deadlock` if the queue is full
and it was called with `napi_tsfn_blocking`. The reason for this is that the
JavaScript thread is responsible for removing items from the queue, thereby
reducing their number. Thus, if it waits for room to become available on the
queue, then it will deadlock.

`napi_call_threadsafe_function()` will also return `napi_would_deadlock` if the
thread-safe function created on one JavaScript thread is called from another
JavaScript thread. The reason for this is to prevent a deadlock arising from the
possibility that the two JavaScript threads end up waiting on one another,
thereby both deadlocking.

The actual call into JavaScript is controlled by the callback given via the
`call_js_cb` parameter. `call_js_cb` is invoked on the main thread once for each
value that was placed into the queue by a successful call to
Expand Down Expand Up @@ -5231,6 +5245,12 @@ This API may be called from any thread which makes use of `func`.
<!-- YAML
added: v10.6.0
napiVersion: 4
changes:
- version: v13.13.0
pr-url: https://github.com/nodejs/node/pull/32689
description: >
Return `napi_would_deadlock` when called with `napi_tsfn_blocking` from
the main thread or a worker thread and the queue is full.
-->

```C
Expand All @@ -5248,9 +5268,13 @@ napi_call_threadsafe_function(napi_threadsafe_function func,
`napi_tsfn_nonblocking` to indicate that the call should return immediately
with a status of `napi_queue_full` whenever the queue is full.

This API will return `napi_would_deadlock` if called with `napi_tsfn_blocking`
from the main thread and the queue is full.

This API will return `napi_closing` if `napi_release_threadsafe_function()` was
called with `abort` set to `napi_tsfn_abort` from any thread. The value is only
added to the queue if the API returns `napi_ok`.
called with `abort` set to `napi_tsfn_abort` from any thread.

The value is only added to the queue if the API returns `napi_ok`.

This API may be called from any thread which makes use of `func`.

Expand Down
10 changes: 7 additions & 3 deletions src/js_native_api_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,15 @@ typedef enum {
napi_date_expected,
napi_arraybuffer_expected,
napi_detachable_arraybuffer_expected,
napi_would_deadlock
} napi_status;
// Note: when adding a new enum value to `napi_status`, please also update
// `const int last_status` in `napi_get_last_error_info()' definition,
// in file js_native_api_v8.cc. Please also update the definition of
// `napi_status` in doc/api/n-api.md to reflect the newly added value(s).
// * `const int last_status` in the definition of `napi_get_last_error_info()'
// in file js_native_api_v8.cc.
// * `const char* error_messages[]` in file js_native_api_v8.cc with a brief
// message explaining the error.
// * the definition of `napi_status` in doc/api/n-api.md to reflect the newly
// added value(s).

typedef napi_value (*napi_callback)(napi_env env,
napi_callback_info info);
Expand Down
3 changes: 2 additions & 1 deletion src/js_native_api_v8.cc
Original file line number Diff line number Diff line change
Expand Up @@ -740,6 +740,7 @@ const char* error_messages[] = {nullptr,
"A date was expected",
"An arraybuffer was expected",
"A detachable arraybuffer was expected",
"Main thread would deadlock",
};

napi_status napi_get_last_error_info(napi_env env,
Expand All @@ -751,7 +752,7 @@ napi_status napi_get_last_error_info(napi_env env,
// message in the `napi_status` enum each time a new error message is added.
// We don't have a napi_status_last as this would result in an ABI
// change each time a message was added.
const int last_status = napi_detachable_arraybuffer_expected;
const int last_status = napi_would_deadlock;

static_assert(
NAPI_ARRAYSIZE(error_messages) == last_status + 1,
Expand Down
23 changes: 23 additions & 0 deletions src/node_api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,29 @@ class ThreadSafeFunction : public node::AsyncResource {
if (mode == napi_tsfn_nonblocking) {
return napi_queue_full;
}

// Here we check if there is a Node.js event loop running on this thread.
// If there is, and our queue is full, we return `napi_would_deadlock`. We
// do this for two reasons:
//
// 1. If this is the thread on which our own event loop runs then we
// cannot wait here because that will prevent our event loop from
// running and emptying the very queue on which we are waiting.
//
// 2. If this is not the thread on which our own event loop runs then we
// still cannot wait here because that allows the following sequence of
// events:
//
// 1. JSThread1 calls JSThread2 and blocks while its queue is full and
// because JSThread2's queue is also full.
//
// 2. JSThread2 calls JSThread1 before it's had a chance to remove an
// item from its own queue and blocks because JSThread1's queue is
// also full.
v8::Isolate* isolate = v8::Isolate::GetCurrent();
if (isolate != nullptr && node::GetCurrentEventLoop(isolate) != nullptr)
return napi_would_deadlock;

cond->Wait(lock);
}

Expand Down
55 changes: 55 additions & 0 deletions test/node-api/test_threadsafe_function/binding.c
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,60 @@ static napi_value StartThreadNoJsFunc(napi_env env, napi_callback_info info) {
/** block_on_full */true, /** alt_ref_js_cb */true);
}

static void DeadlockTestDummyMarshaller(napi_env env,
napi_value empty0,
void* empty1,
void* empty2) {}

static napi_value TestDeadlock(napi_env env, napi_callback_info info) {
napi_threadsafe_function tsfn;
napi_status status;
napi_value async_name;
napi_value return_value;

// Create an object to store the returned information.
NAPI_CALL(env, napi_create_object(env, &return_value));

// Create a string to be used with the thread-safe function.
NAPI_CALL(env, napi_create_string_utf8(env,
"N-API Thread-safe Function Deadlock Test",
NAPI_AUTO_LENGTH,
&async_name));

// Create the thread-safe function with a single queue slot and a single thread.
NAPI_CALL(env, napi_create_threadsafe_function(env,
NULL,
NULL,
async_name,
1,
1,
NULL,
NULL,
NULL,
DeadlockTestDummyMarshaller,
&tsfn));

// Call the threadsafe function. This should succeed and fill the queue.
NAPI_CALL(env, napi_call_threadsafe_function(tsfn, NULL, napi_tsfn_blocking));

// Call the threadsafe function. This should not block, but return
// `napi_would_deadlock`. We save the resulting status in an object to be
// returned.
status = napi_call_threadsafe_function(tsfn, NULL, napi_tsfn_blocking);
add_returned_status(env,
"deadlockTest",
return_value,
"Main thread would deadlock",
napi_would_deadlock,
status);

// Clean up the thread-safe function before returning.
NAPI_CALL(env, napi_release_threadsafe_function(tsfn, napi_tsfn_release));

// Return the result.
return return_value;
}

// Module init
static napi_value Init(napi_env env, napi_value exports) {
size_t index;
Expand Down Expand Up @@ -305,6 +359,7 @@ static napi_value Init(napi_env env, napi_value exports) {
DECLARE_NAPI_PROPERTY("StopThread", StopThread),
DECLARE_NAPI_PROPERTY("Unref", Unref),
DECLARE_NAPI_PROPERTY("Release", Release),
DECLARE_NAPI_PROPERTY("TestDeadlock", TestDeadlock),
};

NAPI_CALL(env, napi_define_properties(env, exports,
Expand Down
5 changes: 4 additions & 1 deletion test/node-api/test_threadsafe_function/binding.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
'targets': [
{
'target_name': 'binding',
'sources': ['binding.c']
'sources': [
'binding.c',
'../../js-native-api/common.c'
]
}
]
}
11 changes: 8 additions & 3 deletions test/node-api/test_threadsafe_function/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -210,8 +210,13 @@ new Promise(function testWithoutJSMarshaller(resolve) {
}))
.then((result) => assert.strictEqual(result.indexOf(0), -1))

// Start a child process to test rapid teardown
// Start a child process to test rapid teardown.
.then(() => testUnref(binding.MAX_QUEUE_SIZE))

// Start a child process with an infinite queue to test rapid teardown
.then(() => testUnref(0));
// Start a child process with an infinite queue to test rapid teardown.
.then(() => testUnref(0))

// Test deadlock prevention.
.then(() => assert.deepStrictEqual(binding.TestDeadlock(), {
deadlockTest: 'Main thread would deadlock'
}));