summaryrefslogtreecommitdiff
path: root/test/node-api/test_threadsafe_function
diff options
context:
space:
mode:
authorGabriel Schulhof <gabriel.schulhof@intel.com>2020-04-06 10:16:15 -0700
committerGabriel Schulhof <gabriel.schulhof@intel.com>2020-04-09 02:41:28 -0700
commitaeb7084fe6446350ec032e9819746126811bf44f (patch)
treeb1173c9ecd5c049f5af7298c181e587b437cd60d /test/node-api/test_threadsafe_function
parentb82d72c199e70a0c9fbeb6c06715ef8120b3de2f (diff)
downloadnode-new-aeb7084fe6446350ec032e9819746126811bf44f.tar.gz
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. PR-URL: https://github.com/nodejs/node/pull/32689 Fixes: https://github.com/nodejs/node/issues/32615 Signed-off-by: Gabriel Schulhof <gabriel.schulhof@intel.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: David Carlier <devnexen@gmail.com> Reviewed-By: Chengzhong Wu <legendecas@gmail.com> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
Diffstat (limited to 'test/node-api/test_threadsafe_function')
-rw-r--r--test/node-api/test_threadsafe_function/binding.c55
-rw-r--r--test/node-api/test_threadsafe_function/binding.gyp5
-rw-r--r--test/node-api/test_threadsafe_function/test.js11
3 files changed, 67 insertions, 4 deletions
diff --git a/test/node-api/test_threadsafe_function/binding.c b/test/node-api/test_threadsafe_function/binding.c
index c9c5261538..9f2fa5f9bd 100644
--- a/test/node-api/test_threadsafe_function/binding.c
+++ b/test/node-api/test_threadsafe_function/binding.c
@@ -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;
@@ -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,
diff --git a/test/node-api/test_threadsafe_function/binding.gyp b/test/node-api/test_threadsafe_function/binding.gyp
index b60352e05a..34587eed3d 100644
--- a/test/node-api/test_threadsafe_function/binding.gyp
+++ b/test/node-api/test_threadsafe_function/binding.gyp
@@ -2,7 +2,10 @@
'targets': [
{
'target_name': 'binding',
- 'sources': ['binding.c']
+ 'sources': [
+ 'binding.c',
+ '../../js-native-api/common.c'
+ ]
}
]
}
diff --git a/test/node-api/test_threadsafe_function/test.js b/test/node-api/test_threadsafe_function/test.js
index 3603d79ee6..f5afe225f0 100644
--- a/test/node-api/test_threadsafe_function/test.js
+++ b/test/node-api/test_threadsafe_function/test.js
@@ -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'
+}));