-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Description
At the moment the workers spec allows workers to share execution threads: each worker uses a run-to-completion model and there are few if any means of synchronous inter-worker communication; the worker must return to its event loop periodically to communicate, at which point the thread can be used to run another worker for a while. In particular, N workers can share M < N execution threads in a pool, which helps control system load.
The shared memory proposal adds the ability for workers to communicate safely and synchronously by means of atomic reads and writes on shared memory. Effectively, a worker may spinloop waiting for a cell to take on a value. A primitive mechanism is available to make that waiting efficient: it allows workers to block and to be woken. However, the model is still that the worker is "running" while it is waiting, and an efficient implementation of "blocking" will in fact use an implementation-dependent combination of spinloops, micro-waits, and actual blocking. Anyway, in this model it is not possible for N workers to share M < N execution threads without risking deadlock, since M workers could all go into a wait but, by hogging all the threads, no work to wake the waiters could get done in the remaining N-M workers.
Effectively that situation forces every worker that sends or receives a shared memory object to have a dedicated execution thread, since the implementation can't guess how that worker will be doing communication. If the worker is only going to be synchronizing by postMessage (and using shared memory just to avoid data copying) there is no pressing need for it to have a dedicated thread, however. Arguably, not having a dedicated thread is a reasonable default, but absent a mechanism to override the default the implementation must always pessimize.
@sicking therefore suggested that there might be a way to pass a request to the worker constructor that a dedicated thread is required. The shared memory proposal already has a clause that allows the implementation to prevent a worker from using the built-in blocking mechanism; that clause would apply if the worker does not have a dedicated thread. (The clause already applies to the window's main thread, in practice.)
I'd like to hear whether there is any support for the idea to pass such a hint to workers.
(From an implementation point of view there might be a possibility that there could be some kind of thread switching behind the scenes when a worker blocks, but as that effectively comes down to simulating a separate thread with its own stack and context and so on in the embedding rather than relying on the OS threads, it really is not appealing, and the savings are uncertain. And I expect interaction with the existing threads in the embedding, locks and TLS and so on, will be truly nasty. I expect that an embedding that can choose between that complexity and just eating the cost of many OS threads will choose the latter.
There is also the possibility that it is "good enough" to only give dedicated threads to workers that send or receive shared memory; it's hard to predict, since not much code has been written yet with shared memory and we don't know what the communication patterns will look like.)
The strawman syntax proposal for this is that instead of the Worker constructor taking a URL, it takes an object containing attributes:
let w = new Worker({ url: ..., dedicatedThread: true })The strawman syntax also opens up for the possibility that the url field is absent and is replaced by eg a src field containing the actual code, and probably for other possibilities. See the discussion around Jonas's note, referenced above.