1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-01 16:51:13 -05:00
denoland-deno/test_ffi/tests/thread_safe_test.js
Aapo Alasuutari 3be5381060
fix(test_ffi): thread_safe_callback is flaky (#19640)
Attempts to fix the thread_safe_callback flakiness. It's unclear what
the flake is about, the exit code is apparently `C0000005` or
`ACCESS_VIOLATION`, pointing to an issue with memory access. My only
guess is that maybe dropping the `Option<extern "C" fn ()>` is somehow
checking the validity of the function pointer and since the function has
been dropped, the pointer is no longer valid and sometimes points to
memory that should not be accessed.

So now the will explicitly drop the functions before they get
deallocated. If this doesn't fix the flake then something beyond my
understanding is wrong.
2023-06-28 14:41:59 -06:00

106 lines
2.6 KiB
JavaScript

// Copyright 2018-2023 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 worker: Note, the stored function pointer in lib is now dangling.
dylib.symbols.store_function(null);
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");
queueMicrotask(() => cleanup());
},
);
cleanupCallback.ref();
function cleanup() {
cleanupCallback.unref();
dylib.symbols.store_function(null);
mainThreadCallback.close();
cleanupCallback.close();
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();