3

I try to get comfortable with Atomics in node.js. For that i created a very simple test with 2 worker threads.

One that waits for a notify, and one that notfies the other.

main.js

const { Worker } = require('node:worker_threads');

const shared = new SharedArrayBuffer(4);
const sync = new Int32Array(shared);
sync[0] = 0;

new Worker('./waiter.js', { workerData: shared });
new Worker('./notifier.js', { workerData: shared });

waiter.js

const { workerData } = require('node:worker_threads');
const sync = new Int32Array(workerData);

let last = 0;

setInterval(() => {
    //while (true) {

    console.log('[waiter] Waiting for', last);

    Atomics.wait(sync, 0, last);
    last = Atomics.load(sync, 0);

    console.log('[waiter] Woke up! New value:', last);
    //}
}, 0);

notifier.js

const { workerData } = require('node:worker_threads');
const sync = new Int32Array(workerData);

let value = 1;

setInterval(() => {

    console.log('[notifier] Stored and notifying');
    Atomics.store(sync, 0, value++);
    Atomics.notify(sync, 0);

}, 1000);

The problem im facing, with while(true) in the waiter, i get no output. No console.log, nothing. With setInterval it prints to stdout.

Not even with a recursive loop i get output/console.log:

const { workerData } = require('node:worker_threads');
const sync = new Int32Array(workerData);

let last = 0;

function loop() {

    console.log('[waiter] Waiting for', last);

    Atomics.wait(sync, 0, last);
    last = Atomics.load(sync, 0);

    console.log('[waiter] Woke up! New value:', last);

    loop();

}

loop();

Can some one tell me why, i get no output on stdout when i use something else than setInterval?

My first guess was that while(true) block io in node.js But then, at least the recursive function approach should work.

10
  • 2
    My first guess was that while(true) block io in node.js But then, at least the recursive function approach should work. Why do you think a recursive function would work different from a loop? Commented Aug 13 at 17:37
  • Because it also does not work with process.nextTick(loop); inside the loop function. Commented Aug 13 at 18:22
  • 1
    In other words the main thread will be completely locked on your hot loop, the fact that it's recursive doesn't matter (unless you run out of stack space due to lack of proper tail recursion). Commented Aug 13 at 18:46
  • 1
    You have a tight (infinite) loop with no asynchronous operations. I'll throw out there that Atomics provides waitAsync though. Commented Aug 13 at 18:52
  • 1
    JS is single-threaded. In order for the async code to run, you have to return to the event loop. The while(true) loop never returns, and neither does the recursive function. Commented Aug 13 at 20:14

1 Answer 1

1

This is because node's worker_thread's console is somehow tied to its calling process and (apparently) waits for a free run of its event-loop.

So with the blocking while loop, it never has the opportunity to push its content to stdout.

What you can do is to use the main thread as the logger by calling parentPort.postMessage() to pass the messages you want to be logged. Since this uses the IPC it's not blocked by the script execution.

main.js

- new Worker('./waiter.js', { workerData: shared });
+ const waiter = new Worker('./waiter.js', { workerData: shared });
+ waiter.on("message", console.log);

waiter.js

const { workerData, parentPort } = require('node:worker_threads');
const sync = new Int32Array(workerData);

let last = 0;


while (true) {

    parentPort.postMessage(['[waiter-through-main] Waiting for', last]);
    console.log('[waiter-direct] Waiting for', last) // won't appear
    Atomics.wait(sync, 0, last);
    last = Atomics.load(sync, 0);
    parentPort.postMessage(['[waiter-through-main] Woke up! New value:', last]);
    console.log('[waiter-direct] Woke up! New value:', last); // won't appear

}
Sign up to request clarification or add additional context in comments.

2 Comments

Interesting. In a worker thread while(true){ console.log("foo"); } prints once "foo". Same code in main "thread" spams stdout... Which is exact the same behavior for your worker. [waiter-direct] Waiting for prints once, and then never. How does this blocking behavior affects other io stuff? e.g. fs.createWriteStream() or a call to a native addon/module that write to a SPI interface on a Raspberry?
I guess you'd have to try every API one by one, but other than the console that'd certainly be a bug if they locked another API that's supposed to communicate with another process/device.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.