aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar dave caruso <me@paperdave.net> 2023-08-21 16:26:07 -0700
committerGravatar GitHub <noreply@github.com> 2023-08-21 16:26:07 -0700
commit664119841a92d13a297e88032f9985fe7e35f77c (patch)
tree0fe664b158d2313d42bb20d6f10b795194a206bb
parent397182b400067be7b5293899fb3855524d07d8bf (diff)
downloadbun-664119841a92d13a297e88032f9985fe7e35f77c.tar.gz
bun-664119841a92d13a297e88032f9985fe7e35f77c.tar.zst
bun-664119841a92d13a297e88032f9985fe7e35f77c.zip
Implement `napi_ref_threadsafe_function` (#4156)
* Implement napi_ref_threadsafe_function * work on this * i hate event loops * little better * clean
-rw-r--r--.vscode/c_cpp_properties.json3
-rw-r--r--.vscode/launch.json4
-rw-r--r--src/bun.js/base.zig8
-rw-r--r--src/bun.js/bindings/napi.cpp13
-rw-r--r--src/napi/napi.zig7
-rwxr-xr-xtest/bun.lockbbin153753 -> 153785 bytes
-rw-r--r--test/js/third_party/fsevents/fsevents-event-loop.mjs10
-rw-r--r--test/js/third_party/fsevents/fsevents.test.ts20
-rw-r--r--test/js/third_party/fsevents/package.json6
-rw-r--r--test/package.json1
10 files changed, 65 insertions, 7 deletions
diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json
index 8036312a6..3e50ca0a9 100644
--- a/.vscode/c_cpp_properties.json
+++ b/.vscode/c_cpp_properties.json
@@ -43,7 +43,8 @@
"${workspaceFolder}/src/bun.js/modules/*",
"${workspaceFolder}/src/deps",
"${workspaceFolder}/src/deps/boringssl/include/",
- "${workspaceFolder}/src/deps/uws/uSockets/src"
+ "${workspaceFolder}/src/deps/uws/uSockets/src",
+ "${workspaceFolder}/src/napi"
],
"limitSymbolsToIncludedHeaders": true,
"databaseFilename": ".vscode/cppdb"
diff --git a/.vscode/launch.json b/.vscode/launch.json
index fd36e4d2f..c4ddd551a 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -134,8 +134,8 @@
"request": "launch",
"name": "bun run [file]",
"program": "bun-debug",
- "args": ["--inspect=127.0.0.1:9232", "run", "/Users/jarred/Code/bun/packages/bun-vscode/example/example.js"],
- "cwd": "/Users/jarred/Code/bun/packages/bun-vscode/example",
+ "args": ["run", "${file}", "${file}"],
+ "cwd": "${fileDirname}",
"env": {
"FORCE_COLOR": "1",
"NODE_ENV": "development"
diff --git a/src/bun.js/base.zig b/src/bun.js/base.zig
index b08b82c40..e72e196a3 100644
--- a/src/bun.js/base.zig
+++ b/src/bun.js/base.zig
@@ -1682,6 +1682,14 @@ pub const PollRef = struct {
this.status = .active;
vm.uws_event_loop.?.refConcurrently();
}
+
+ pub fn refConcurrentlyFromEventLoop(this: *PollRef, loop: *JSC.EventLoop) void {
+ this.refConcurrently(loop.virtual_machine);
+ }
+
+ pub fn unrefConcurrentlyFromEventLoop(this: *PollRef, loop: *JSC.EventLoop) void {
+ this.unrefConcurrently(loop.virtual_machine);
+ }
};
const KQueueGenerationNumber = if (Environment.isMac and Environment.allow_assert) usize else u0;
diff --git a/src/bun.js/bindings/napi.cpp b/src/bun.js/bindings/napi.cpp
index 8a77723e2..2562242a8 100644
--- a/src/bun.js/bindings/napi.cpp
+++ b/src/bun.js/bindings/napi.cpp
@@ -46,6 +46,8 @@
#include "JavaScriptCore/JSSourceCode.h"
#include "JavaScriptCore/JSNativeStdFunction.h"
#include "JavaScriptCore/BigIntObject.h"
+#include "ScriptExecutionContext.h"
+#include "Strong.h"
#include "../modules/ObjectModule.h"
@@ -1763,7 +1765,16 @@ extern "C" napi_status napi_create_external(napi_env env, void* data,
auto* structure = Bun::NapiExternal::createStructure(vm, globalObject, globalObject->objectPrototype());
JSValue value = JSValue(Bun::NapiExternal::create(vm, structure, data, finalize_hint, finalize_cb));
- JSC::EnsureStillAliveScope ensureStillAlive(value);
+
+ // With `fsevents`, their napi_create_external seems to get immediatly garbage
+ // collected for some unknown reason.
+ // See https://github.com/oven-sh/bun/issues/3978 and `fsevents.test.ts`
+ JSC::Strong<Unknown>* strong = new JSC::Strong<Unknown>(vm, value);
+ globalObject->scriptExecutionContext()->postTask([strong](auto& context) -> void {
+ strong->clear();
+ delete strong;
+ });
+
*result = toNapi(value);
return napi_ok;
}
diff --git a/src/napi/napi.zig b/src/napi/napi.zig
index 99a49bb96..1d3e3e811 100644
--- a/src/napi/napi.zig
+++ b/src/napi/napi.zig
@@ -1210,7 +1210,7 @@ pub const ThreadSafeFunction = struct {
/// Neither does napi_unref_threadsafe_function mark the thread-safe
/// functions as able to be destroyed nor does napi_ref_threadsafe_function
/// prevent it from being destroyed.
- ref_for_process_exit: bool = false,
+ poll_ref: JSC.PollRef,
owning_threads: std.AutoArrayHashMapUnmanaged(u64, void) = .{},
owning_thread_lock: Lock = Lock.init(),
@@ -1345,11 +1345,11 @@ pub const ThreadSafeFunction = struct {
}
pub fn ref(this: *ThreadSafeFunction) void {
- this.ref_for_process_exit = true;
+ this.poll_ref.refConcurrentlyFromEventLoop(this.event_loop);
}
pub fn unref(this: *ThreadSafeFunction) void {
- this.ref_for_process_exit = false;
+ this.poll_ref.unrefConcurrentlyFromEventLoop(this.event_loop);
}
pub fn acquire(this: *ThreadSafeFunction) !void {
@@ -1415,6 +1415,7 @@ pub export fn napi_create_threadsafe_function(
.ctx = context,
.channel = ThreadSafeFunction.Queue.init(max_queue_size, bun.default_allocator),
.owning_threads = .{},
+ .poll_ref = JSC.PollRef.init(),
};
function.owning_threads.ensureTotalCapacity(bun.default_allocator, initial_thread_count) catch return genericFailure();
function.finalizer = .{ .ctx = thread_finalize_data, .fun = thread_finalize_cb };
diff --git a/test/bun.lockb b/test/bun.lockb
index 98c35844d..c94d5aff3 100755
--- a/test/bun.lockb
+++ b/test/bun.lockb
Binary files differ
diff --git a/test/js/third_party/fsevents/fsevents-event-loop.mjs b/test/js/third_party/fsevents/fsevents-event-loop.mjs
new file mode 100644
index 000000000..80e4729a1
--- /dev/null
+++ b/test/js/third_party/fsevents/fsevents-event-loop.mjs
@@ -0,0 +1,10 @@
+import fsevents from "fsevents";
+
+if (process.argv.length < 3) {
+ console.log("Usage: bun fsevents-event-loop.ts <directory>");
+ process.exit(1);
+}
+fsevents.watch(process.argv[2], () => {
+ console.log("it works!");
+ process.exit(0);
+});
diff --git a/test/js/third_party/fsevents/fsevents.test.ts b/test/js/third_party/fsevents/fsevents.test.ts
new file mode 100644
index 000000000..1381347e9
--- /dev/null
+++ b/test/js/third_party/fsevents/fsevents.test.ts
@@ -0,0 +1,20 @@
+import { bunEnv, bunExe } from "harness";
+import path from "path";
+import fs from "fs";
+import os from "os";
+
+test("fsevents works (napi_ref_threadsafe_function keeps event loop alive)", async () => {
+ const tempFile = fs.mkdtempSync(path.join(os.tmpdir(), "fsevents-test-"));
+ const spawned = Bun.spawn({
+ cmd: [bunExe(), "run", path.join(import.meta.dir, "fsevents-event-loop.mjs"), tempFile],
+ env: bunEnv,
+ stdio: ["pipe", "pipe", "pipe"],
+ });
+ await Bun.sleep(50);
+ if (spawned.killed) {
+ throw new Error("event loop died, test failed");
+ }
+ await Bun.write(tempFile + "/hello.txt", "test");
+ expect(await spawned.exited).toBe(0);
+ expect(await new Response(spawned.stdout).text()).toBe("it works!\n");
+});
diff --git a/test/js/third_party/fsevents/package.json b/test/js/third_party/fsevents/package.json
new file mode 100644
index 000000000..315c73e1a
--- /dev/null
+++ b/test/js/third_party/fsevents/package.json
@@ -0,0 +1,6 @@
+{
+ "name": "fsevents-test",
+ "dependencies": {
+ "fsevents": "2.3.2"
+ }
+}
diff --git a/test/package.json b/test/package.json
index f841d2003..099f3d312 100644
--- a/test/package.json
+++ b/test/package.json
@@ -17,6 +17,7 @@
"es-module-lexer": "1.3.0",
"esbuild": "0.18.6",
"express": "4.18.2",
+ "fsevents": "2.3.2",
"iconv-lite": "0.6.3",
"jest-extended": "4.0.0",
"lodash": "4.17.21",