summaryrefslogtreecommitdiff
path: root/test_ffi/tests/thread_safe_test.js
diff options
context:
space:
mode:
authorAapo Alasuutari <aapo.alasuutari@gmail.com>2022-06-28 12:23:36 +0300
committerGitHub <noreply@github.com>2022-06-28 14:53:36 +0530
commit00f4521b205bf25c79f0fa7c9a6840941342bda4 (patch)
treeb988db360143384ae216022846b518ceb262c80a /test_ffi/tests/thread_safe_test.js
parente1c90963fbbf6571ae1b66971b83159681928ec3 (diff)
feat(ext/ffi): Thread safe callbacks (#14942)
Diffstat (limited to 'test_ffi/tests/thread_safe_test.js')
-rw-r--r--test_ffi/tests/thread_safe_test.js101
1 files changed, 101 insertions, 0 deletions
diff --git a/test_ffi/tests/thread_safe_test.js b/test_ffi/tests/thread_safe_test.js
new file mode 100644
index 000000000..e54114055
--- /dev/null
+++ b/test_ffi/tests/thread_safe_test.js
@@ -0,0 +1,101 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+// deno-lint-ignore-file
+
+const targetDir = Deno.execPath().replace(/[^\/\\]+$/, "");
+const [libPrefix, libSuffix] = {
+ darwin: ["lib", "dylib"],
+ linux: ["lib", "so"],
+ windows: ["", "dll"],
+}[Deno.build.os];
+const libPath = `${targetDir}/${libPrefix}test_ffi.${libSuffix}`;
+
+const resourcesPre = Deno.resources();
+
+const dylib = Deno.dlopen(libPath, {
+ store_function: {
+ parameters: ["function"],
+ result: "void",
+ },
+ call_stored_function: {
+ parameters: [],
+ result: "void",
+ },
+ call_stored_function_thread_safe: {
+ parameters: [],
+ result: "void",
+ },
+});
+
+let resolveWorker;
+let workerResponsePromise;
+
+const worker = new Worker(
+ new URL("./thread_safe_test_worker.js", import.meta.url).href,
+ { type: "module" },
+);
+
+worker.addEventListener("message", () => {
+ if (resolveWorker) {
+ resolveWorker();
+ }
+});
+
+const sendWorkerMessage = async (data) => {
+ workerResponsePromise = new Promise((res) => {
+ resolveWorker = res;
+ });
+ worker.postMessage(data);
+ await workerResponsePromise;
+};
+
+// Test step 1: Register main thread callback, trigger on worker thread
+
+const mainThreadCallback = new Deno.UnsafeCallback(
+ { parameters: [], result: "void" },
+ () => {
+ console.log("Callback on main thread");
+ },
+);
+
+mainThreadCallback.ref();
+
+dylib.symbols.store_function(mainThreadCallback.pointer);
+
+await sendWorkerMessage("call");
+
+// Test step 2: Register on worker thread, trigger on main thread
+
+await sendWorkerMessage("register");
+
+dylib.symbols.call_stored_function();
+
+// Unref both main and worker thread callbacks and terminate the wrorker: Note, the stored function pointer in lib is now dangling.
+
+mainThreadCallback.unref();
+await sendWorkerMessage("unref");
+worker.terminate();
+
+// Test step 3: Register a callback that will be the only thing left keeping the isolate from exiting.
+// Rely on it to keep Deno running until the callback comes in and unrefs the callback, after which Deno should exit.
+
+const cleanupCallback = new Deno.UnsafeCallback(
+ { parameters: [], result: "void" },
+ () => {
+ console.log("Callback being called");
+ Promise.resolve().then(() => cleanup());
+ },
+);
+
+cleanupCallback.ref();
+
+function cleanup() {
+ cleanupCallback.unref();
+ console.log("Isolate should now exit");
+}
+
+dylib.symbols.store_function(cleanupCallback.pointer);
+
+console.log(
+ "Calling callback, isolate should stay asleep until callback is called",
+);
+dylib.symbols.call_stored_function_thread_safe();