'Is it possible to pause/resume a web worker externally?

I've seen that web workers have a terminate() function, but is there a way to pause and resume web workers from the host thread similar to interrupts?

The critical factor with this question is that using the terminate() call on a web worker will break the state internal to the worker (and any operations taking place may in turn be corrupted in a half-complete state.) I'm looking for a mechanism by which to switch between hundreds-thousands of tasks held in however many web workers equal the total cores on the machine, without losing state within those tasks or relying on cooperative multithreading styles like fibers.



Solution 1:[1]

Is it possible to pause/resume a web worker externally?

No. Browser javascript does not have a means of externally pausing web workers without some cooperation from the webWorker itself.

A work-around (with cooperation from the webWorker) would involve the webWorker running a bit of code, then allowing a message to arrive from the outside world that tells it to pause and then the webWorker itself not calling the next unit of work until it receives a message to resume processing. But, because even webWorkers are event driven, you can't receive a message from the outside world until you finish what you were doing and return back to the event system.

This would probably involve executing a chunk of work, setting a timer for only a few ms from now which allows messages to arrive, then when a message arrives, you store the new pause/resume state and then when the timer fires, you check the state and if its pause, you don't call the next unit of work. If it's not the pause state, you execute the next unit of work and when it finishes, you again do setTimeout() with a really short time (allowing any pending messages to process) and keep going over and over again.

Unfortunately, this technique requires you to chunk your work which negates some of the benefits of webWorkers in the first place (though you still retain the benefit of using more than one CPU to process work).

I'm looking for a mechanism by which to switch between hundreds-thousands of tasks held in however many web workers equal the total cores on the machine, without losing state within those tasks

You would need to have each currently running webWorker cooperate and allow a message from the outside world to get processed so they could see when the outside world wants them to stop doing more work and then have them cooperatively not call the next unit of work so they wouldn't be doing anything until the next message arrives telling them to start doing their work again.

If you're going to have thousands of these, you probably don't want them all to be individual webWorkers anyway as that's a bit heavy weight of a system. You probably want a set of webWorkers that are working on a work queue. You queue up work units to be processed and each webWorker grabs the next unit of work from the queue, processes it, grabs the next unit and so on. Your main process then just keeps the queue fed with whatever work you want the webWorkers to be doing. This also requires chunking the work into proper units of work.

Solution 2:[2]

Considering you are targeting recent browsers, with access to SharedArrayBuffer and Atomics. The simplest solution is to pass SAB reference to the worker:

main.js

const sab = new SharedArrayBuffer(4);
const int32 = new Int32Array(sab);

const start = () => {
    const worker = new Worker("worker.js");
    worker.postMessage({kind:"init", sab});
}

const pause = () => {
    Atomics.store(int32, 0, 1);
    Atomics.notify(int32, 0);
}

const resume = () => {
    Atomics.store(int32, 0, 0);
    Atomics.notify(int32, 0);
}

...worker loop will wait for the byte to have particular value 1/0.

worker.js

onmessage = event => {
    if(event.data.kind === "init"){
        while(true) {
            Atomics.wait(new Int32Array(event.data.sab), 0, 1);
            ... your bitcoin miner goes here
        }
    }
}

In case your worker does not contain synchronous main loop but rather consist of async operations, you can intercept the event loop (block any other line of code to be executed) as follows:

main.js

const sab = new SharedArrayBuffer(4);
const int32 = new Int32Array(sab);

const start = () => {
    const worker = new Worker("worker.js");
}

const pause = () => {
    Atomics.store(int32, 0, 1);
    Atomics.notify(int32, 0);
    worker.postMessage({kind:"pause", sab});
}

const resume = () => {
    Atomics.store(int32, 0, 0);
    Atomics.notify(int32, 0);
}

worker.js

... async/await, fetch(), setTimeout()
onmessage = event => {
    if(event.data.kind === "pause")
        Atomics.wait(new Int32Array(event.data.sab), 0, 1);
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Yoz