diff options
author | 2023-08-21 16:26:07 -0700 | |
---|---|---|
committer | 2023-08-21 16:26:07 -0700 | |
commit | 664119841a92d13a297e88032f9985fe7e35f77c (patch) | |
tree | 0fe664b158d2313d42bb20d6f10b795194a206bb | |
parent | 397182b400067be7b5293899fb3855524d07d8bf (diff) | |
download | bun-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.json | 3 | ||||
-rw-r--r-- | .vscode/launch.json | 4 | ||||
-rw-r--r-- | src/bun.js/base.zig | 8 | ||||
-rw-r--r-- | src/bun.js/bindings/napi.cpp | 13 | ||||
-rw-r--r-- | src/napi/napi.zig | 7 | ||||
-rwxr-xr-x | test/bun.lockb | bin | 153753 -> 153785 bytes | |||
-rw-r--r-- | test/js/third_party/fsevents/fsevents-event-loop.mjs | 10 | ||||
-rw-r--r-- | test/js/third_party/fsevents/fsevents.test.ts | 20 | ||||
-rw-r--r-- | test/js/third_party/fsevents/package.json | 6 | ||||
-rw-r--r-- | test/package.json | 1 |
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 Binary files differindex 98c35844d..c94d5aff3 100755 --- a/test/bun.lockb +++ b/test/bun.lockb 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", |