JffiNativeMachine.call() and the Panama NativeMachine.call() create, start and then interrupt a new watchdog Thread on every call into native code (it polls caller.isInterrupted() every 1 ms to raise the interrupt flag):
Thread watchdog = new Thread(() -> { ... Thread.sleep(1); ... });
watchdog.setDaemon(true);
watchdog.start();
try { result = invokeViaEntryTrampoline(...); } finally { watchdog.interrupt(); ... }
A host function that calls back into an export (e.g. cabi_realloc to return a value) pays it again for the nested call. Measured on quickjs4j (Endive 1.1.0, javy module): 16 threads started per JS evaluation, 58 per Camel message, versus 0 on bytecode. It makes an evaluation ~2.4x slower than the bytecode build-time compiler single-threaded, and ~6x slower with 8 threads, where thread creation serializes.
Removing the watchdog from the 1.1.0 jffi runner brings the native path back in line (numbers in the quickjs4j investigation).
Suggested fix: one shared daemon watchdog (or none at all, checking the caller's interrupt flag from the host-call / memory-grow safepoints), never a thread per call.
🤖 Generated with Claude Code
JffiNativeMachine.call()and the PanamaNativeMachine.call()create, start and then interrupt a new watchdogThreadon every call into native code (it pollscaller.isInterrupted()every 1 ms to raise the interrupt flag):A host function that calls back into an export (e.g.
cabi_reallocto return a value) pays it again for the nested call. Measured on quickjs4j (Endive 1.1.0, javy module): 16 threads started per JS evaluation, 58 per Camel message, versus 0 on bytecode. It makes an evaluation ~2.4x slower than the bytecode build-time compiler single-threaded, and ~6x slower with 8 threads, where thread creation serializes.Removing the watchdog from the 1.1.0 jffi runner brings the native path back in line (numbers in the quickjs4j investigation).
Suggested fix: one shared daemon watchdog (or none at all, checking the caller's interrupt flag from the host-call / memory-grow safepoints), never a thread per call.
🤖 Generated with Claude Code