[HN Gopher] Web Locks API
___________________________________________________________________
Web Locks API
Author : mooreds
Score : 95 points
Date : 2024-11-10 17:46 UTC (5 hours ago)
(HTM) web link (developer.mozilla.org)
(TXT) w3m dump (developer.mozilla.org)
| aabhay wrote:
| I've been using this for a while now. But one thing I recently
| worked on required these locks to be extremely efficient. Does
| anyone have any benchmarks on usage of these locks? Preferably
| compared to the use of rust's tokio mutex from a wasm context.
| orf wrote:
| If you've been using them for a while, don't you have any
| benchmarks?
| tantalor wrote:
| Weird API to release the lock. What if you want to hold on to it?
| Then you need to do some silly promise wrapper. Would be better
| if there was a matching release() function.
| rockwotj wrote:
| Probably because there is no RAII semantics in JS and they
| don't want to allow forgetting releasing the lock. Although the
| promise workaround is explicitly opting into this behavior
| slimsag wrote:
| Javascript in browsers already has a full atomics API:
|
| https://developer.mozilla.org/en-
| US/docs/Web/JavaScript/Refe...
|
| I'm not sure why Web Locks is useful TBH. I guess if you
| don't understand atomics it's a friendlier API?
| whilenot-dev wrote:
| You mean Atomics + SharedArrayBuffer, otherwise it won't be
| shared across agents. I can imagine all the _postMessage_
| calls and _message_ handlers swirling around in all agents
| to approximate something like the Web Locks API for a
| simple lock, but tbh I 'd take the Web Locks API any day.
| pavlov wrote:
| The Web Locks documentation explains that it works across
| tabs, i.e. separate processes:
|
| _"[...] allows a web app running in multiple tabs or
| workers to coordinate work and the use of resources"_
|
| A locking API is much more natural and less error-prone for
| this use case than using shared memory and atomics.
| jauntywundrkind wrote:
| The ergonomics here for 99.999% of uses seem great to me.
| Whatever async function you have can run for as long as it
| needs. That's using the language, not adding more userland
| craft. It's a good move.
| maxmcd wrote:
| class Lock { #release: () => void | undefined;
| constructor(public name: string) {} acquire() {
| if (this.#release !== undefined) throw new Error("Already
| locked"); navigator.locks.request(this.name, async
| (lock) => { await new Promise<void>(resolve => {
| this.#release = resolve; }); });
| } release() { if (this.#release ===
| undefined) throw new Error("Not locked");
| this.#release(); } }
| whilenot-dev wrote:
| You're not treating the aquire phase correctly:
| class Lock { #resolve: (() => void) | undefined;
| #releasePromise: Promise<void> | undefined;
| constructor(public name: string) {} acquire():
| Promise<void> { if (this.#releasePromise !==
| undefined) { throw new Error("Already aquired");
| } return new Promise((resolveAquire,
| _rejectAquire) => { this.#releasePromise = new
| Promise((resolveRelease, _rejectRelease) => {
| navigator.locks.request(this.name, async (lock) => {
| await new Promise<void>((resolve, _reject) => {
| this.#resolve = resolve; resolveAquire();
| }); resolveRelease(); });
| }); }); } async release():
| Promise<void> { if (this.#releasePromise ===
| undefined) { throw new Error("Already released");
| } this.#resolve(); await
| this.#releasePromise; this.#resolve =
| undefined; this.#releasePromise = undefined;
| } }
|
| ...the release phase still feels off without a Promise, but
| maybe somebody else can tackle that :D
|
| EDIT: think I fixed it, untested though
| jauntywundrkind wrote:
| Not that it's super important, but Web Locks API is getting some
| circulation after a question about how you keep multiple pages
| from trying to use a (single-use only) oauth refresh token at the
| same time. Which is a pretty good use case for this feature!
| https://bsky.app/profile/ambarvm.bsky.social/post/3lakznzipt...
| nitwit005 wrote:
| How does a shared memory space work if you have different
| versions of scripts for the same domain?
| unilynx wrote:
| > navigator.locks.request("my_resource", async (lock) => {
|
| This would be so much more readable with `using`
| { using lock = navigator.lock('my_resource');
| await do_something(); await do_something_else(); }
|
| (https://github.com/tc39/proposal-explicit-resource-managemen...)
| sureIy wrote:
| You can probably wrap it to have that API
| yoavm wrote:
| It doesn't actually feel more readable to me. I find the idea
| that the lock declaration sits at the same level as the lock
| content confusing.
| zeroxfe wrote:
| It's readable if you're familiar with the RAII pattern which
| is used in languages like C++ and Go.
| chrisfosterelli wrote:
| Why can't we just `await` the lock call?
|
| Edit: Nevermind, release of the lock is automatic when the
| callback resolves its promise. I get it now.
| IshKebab wrote:
| Using global names for this seems like a bad idea.
| koolba wrote:
| Using global for bad ideas is kind of par for the course for
| all things JavaScript.
| sureIy wrote:
| What's the problem? The whole concept is that it's locking a
| resource super-globally, not only in the current tab, but
| across tabs.
| IshKebab wrote:
| Ah I misread. The intro made it sound like this was for
| locking within a tab, but it's within a origin.
| RedShift1 wrote:
| How many layers of namespacing do you want in between? 1? 2?
| 10? Perhaps we should go the SNMP route and start with
| 1.3.6.1.4.1?
| gabrieledarrigo wrote:
| Ciao guys, what could be the use case for such API?
| sureIy wrote:
| Click the link, read the words. There's a list in it.
| 1oooqooq wrote:
| if a crypto miner infects a site you have several tabs open
| they won't fight for cpu at the same time.
| mooreds wrote:
| This was the use case[0] that brought it to my attention:
|
| - single page application using access and refresh tokens to
| interact with an API
|
| - refresh is one time use, as recommended by the OAuth security
| best practices[1]
|
| - SPA is open in more than one tab
|
| - two tabs try to refresh the token at the same time, second
| one fails because refresh token is used up
|
| 0:
| https://bsky.app/profile/ambarvm.bsky.social/post/3lakznzipt...
|
| 1: https://datatracker.ietf.org/doc/html/draft-ietf-oauth-
| secur...
| loevborg wrote:
| Besides what sibling has written, write access to IndexedDB
| also often needs to be guarded by a mutex
| https://gist.github.com/pesterhazy/4de96193af89a6dd5ce682ce2...
| rdsubhas wrote:
| A lease is usually a better choice than a lock.
|
| A lease has a time limit. A lock does not. Clearing stale locks
| manually is a PITA. I still assume, being a Web-scale contract,
| the lock would be automatically cleared if the browser is
| restarted or something. But honestly a lease makes users do
| better design from the get-go.
| mooreds wrote:
| Is there a native browser API for leases?
| naasking wrote:
| Time limits are a recipe for non-determinism. Non-determinism
| is generally not what you want.
| soulofmischief wrote:
| Put another way, combining side effects and timers invariably
| causes race conditions.
| hn_throwaway_99 wrote:
| I might agree in other contexts, but not with the use case here
| and how the API is designed.
|
| It looks like the only potential for a "stale lock" is if
| somehow the async function passed to the request method hangs
| forever. But in web contexts I think that would be extremely
| unlikely for everyday use cases (e.g. most of the time I could
| imagine the async callback making remote calls using fetch, but
| normally that fetch has its own timeout). In contexts where it
| _could_ happen, I 'd argue it's better to make the caller
| explicitly handle that case (e.g. by using `steal`) than
| potentially leave things in an indeterminate state because a
| lease timeout expired.
| yazzku wrote:
| Expect deadlocks in web applications now? I wouldn't necessarily
| trust a JS programmer with a lock, sorry. They are hard enough in
| C++ or other languages that generally require a lot more
| discipline.
| ukuina wrote:
| > deadlocks only affect the locks themselves and code depending
| on them; the browser, other tabs, and other script in the page
| is not affected.
|
| Also, you don't need to use this API to lock up your web app.
___________________________________________________________________
(page generated 2024-11-10 23:00 UTC)