Skip to content

ffi: stopping a Worker while it is in a callback aborts the process #66388

Description

@trivikr

Version

main

Platform

macOS 26.7.0

Subsystem

ffi

What steps will reproduce the bug?

import { Worker } from 'node:worker_threads';

// qsort() from the process image calls the JS comparator through node:ffi.
const workerSource = `
const { parentPort, workerData } = require('node:worker_threads');
const ffi = require('node:ffi');
const { lib, functions } = ffi.dlopen(null, {
  qsort: { arguments: ['pointer', 'u64', 'u64', 'pointer'], return: 'void' },
});
const compare = lib.registerCallback(
  { arguments: ['pointer', 'pointer'], return: 'i32' },
  () => {
    if (workerData === 'exit') process.exit(0);
    parentPort.postMessage('in callback');
    for (;;);
  },
);
const items = new Int32Array([2, 1]);
functions.qsort(ffi.getRawPointer(items), 2n, 4n, compare);
`;

const mode = process.argv[2];
const worker = new Worker(workerSource, { eval: true, workerData: mode });
worker.on('message', () => {
  if (mode === 'shutdown') process.exit(0);
  worker.terminate();
});
worker.on('exit', (code) => console.log(`worker exited with code ${code}`));
$ node --experimental-ffi repro.mjs shutdown    # main thread calls process.exit(0) while the Worker is in the callback
$ node --experimental-ffi repro.mjs terminate   # worker.terminate() while the Worker is in the callback
$ node --experimental-ffi repro.mjs exit        # process.exit() inside the callback

How often does it reproduce? Is there a required condition?

Always

What is the expected behavior? Why is that the expected behavior?

Only the Worker stops:

                              # shutdown: exit code 0
worker exited with code 1     # terminate
worker exited with code 0     # exit

The docs say that process.exit() in a Worker "stops the current thread rather than the current process", and that worker.terminate() stops "JavaScript execution in the worker thread". Neither of these is an exception thrown by the callback.

What do you see instead?

All three modes abort the whole process with exit code 134:

Callbacks cannot throw an exception
----- Native stack trace -----

 1: 0x102c5f1b8 node::(anonymous namespace)::DefaultAbortHandler(char const*, char const*) 
 2: 0x102cd8cfc node::ffi::DynamicLibrary::InvokeCallback(ffi_cif*, void*, void**, void*) (.cold.5) 
 3: 0x1008e5bc8 node::ffi::DynamicLibrary::InvokeCallback(ffi_cif*, void*, void**, void*) 
 4: 0x101f5e338 ffi_closure_SYSV_inner
...

Additional information

No response

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    ffiIssues and PRs related to experimental Foreign Function Interface support.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions