5 ms·
Two lesser known really cool things about Web Workers: 1. They kind of allow for stopping synchronous operations, example: some regexes have "catastrophic back
by fabiospampinato 5y ago
Two lesser known really cool things about Web Workers:
1. They kind of allow for stopping synchronous operations, example: some regexes have "catastrophic backtracking", executing them will take a really long time, so what do you do if you have to execute user-provided regexes, especially if on the server? Detecting potentially catastrophic regexes is tough, reimplementing the regex engine in order to make it yield to the main thread frequently so that you can stop it is super tough, so what's the solution? You can execute the regex in a Web Worker, and if you haven't received a response within some set amount of time you can just kill the Web Worker, effectively stopping the regex execution, cool!
2. They kind of allow for blocking on promises, example: normally you can't block the event loop while you are waiting for a promise to be resolved, in other words you can't make an asynchronous function synchronous, except if you use Web Workers, you can execute the asynchronous function you need on a worker, and then use Atomics.wait on the main thread to block (without melting the computer) until that function resolves, super cool!
- domenicd 5y agoAtomics.wait() cannot be called on the main thread (i.e. when your global is a Window object).
- fabiospampinato 5y agoIt works fine both in Node and Deno. I haven't tested this out on browsers though, is that supposed to throw even if SharedArrayBuffer is enabled?
- azakai 5y agoYes, browsers will not allow you to block on the main thread. Atomics.waitAsync is supposed to be used instead. This is a fairly difficult aspect of multithreading on the web, and it makes things more complicated than other platforms (like Node and Deno, as you mentioned). For example in emscripten's pthreads support layer there is code dedicated to do a sort of careful busy-wait when we have no other option, and all that is only for the case of the main thread. But your point is still very relevant, just not on the main thread: if you can run your application in a worker, then you can block on Promises using another worker that does the async operation while the first worker is synchronous. And that's really useful!
- no_way 5y agoThere is Atomics.waitAsync which can be used on the main thread and just returns a promise. It is shipped at least in chrome.
- chrismorgan 5y ago> and then use Atomics.wait on the main thread to block The main thread is not allowed to use Atomics.wait. I’m not certain what the implementation status of this is because I’ve never used it and I have a vague feeling I heard that one browser shipped it without that restriction, but at the very least you may get a TypeError in some user agents and you can expect to in all user agents at some point in the future when they become sterner about not blocking the main thread. As for stopping synchronous operations, I’m dubious that would actually work; without actually testing it (and I don’t have time to test it now, though I’d be interested in the result, including across various platforms), I think it’s more likely that the regexp match would be uninterruptable, and that it would just go on munching your CPU until it finished, and then terminate once it returned from the native code to the JavaScript.
- fabiospampinato 5y agoI should check what's the status with Atomics.wait on Chrome, currently it seems to work fine under both Node and Deno. I'll test out the regex thing in the following days as I need it for that exact use case, I just assumed killing the web worker would... work, hopefully that's the case otherwise I'm back to square 0 :D --- Edit: MDN says: "The terminate() method of the Worker interface immediately terminates the Worker. This does not offer the worker an opportunity to finish its operations; it is stopped at once." so if that's not the case either the engine is wrong or the docs are wrong.
- chrismorgan 5y agoWorker.terminate() aborts the currently running script evaluation, see https://html.spec.whatwg.org/multipage/workers.html#dom-worker-terminate-dev https://html.spec.whatwg.org/multipage/workers.html#dom-work... → https://html.spec.whatwg.org/multipage/workers.html#terminate-a-worker https://html.spec.whatwg.org/multipage/workers.html#terminat... → https://html.spec.whatwg.org/multipage/webappapis.html#abort-a-running-script https://html.spec.whatwg.org/multipage/webappapis.html#abort..., but that’s a fairly fuzzy definition, and it’s not generally reasonable to expect that to interrupt a currently executing piece of native code, because interrupting that (e.g. by sending a signal to the thread and forcibly starting unwinding) could leave data structures in a memory-unsafe state (that is, in the improbable worst case this could be a vector for escaping the sandbox). It’s possible they’ve come up with some way of working around this, but it’s going to be considerably easier and safer to just treat native code as uninterruptible by default, and possibly get known-slow blocking operations to manually periodically check if they’re being asked to stop. So I say my gut feeling is that the match operation won’t actually be interrupted, and I wouldn’t be inclined to depend on it actually being interrupted without explicit documentation of what aborting does, even if all environments I cared about did abort it immediately.
- wffurr 5y agoIn #2 you use Atomics.wait in the Worker instead and then it can signal the main thread when done. We use this to convert async browser APIs to service synchronous callbacks that our C library compiled to WebAssembly expects.
- jefftk 5y agoIn the specific case of regular expressions on the server, re2 was written to handle exactly this.