ffi: avoid copying the threadsafe callback wrapper on the calling thread (#31332)
`FFI_Callback_threadsafe_call` runs on whatever thread the C library invokes the callback from, and captured the `FFICallbackFunctionWrapper` by value into the event-loop task, copy-constructing its two `JSC::Strong` members off the JS thread. This change makes the wrapper `ThreadSafeRefCounted`, captures a `Ref<>` in the task instead (an atomic increment), caches the `ScriptExecutionContextIdentifier` at construction time so the calling thread never reads through a `Strong`, and turns `FFICallbackFunctionWrapper_destroy` into a `deref()` so pending tasks keep the wrapper alive across `JSCallback.close()`. The wrapper is now non-copyable, so the by-value capture cannot come back. Adds the first CI coverage for threadsafe `JSCallback`s actually invoked from a foreign thread (via a TinyCC-compiled pthread fixture in `cc.test.ts`): one test delivers 200 invocations from a worker thread while the JS thread churns handle allocations, and one closes the callback while invocations are still queued and verifies they are all still delivered. These tests exercise and pin the new behavior; the underlying off-thread handle allocation was timing-dependent, so the change itself is verified by inspection (no `JSC::Strong` is constructed, copied, or destroyed off the JS thread on any reachable path) plus repeated local runs.
J
Jarred Sumner committed
9e6a19ba2e3c43f0782c9c9fa24a608f9824bb06
Parent: 29f1f7c
Committed by GitHub <noreply@github.com>
on 5/26/2026, 12:05:24 AM