[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)