Page MenuHomePhabricator

[web] Introduce useWebLock
ClosedPublic

Authored by michal on Feb 19 2024, 9:59 AM.
Tags
None
Referenced Files
F3359010: D11113.diff
Sun, Nov 24, 6:42 AM
Unknown Object (File)
Sat, Nov 16, 5:45 PM
Unknown Object (File)
Sat, Nov 16, 5:45 PM
Unknown Object (File)
Sat, Nov 16, 5:45 PM
Unknown Object (File)
Sat, Nov 16, 5:45 PM
Unknown Object (File)
Sat, Nov 16, 5:45 PM
Unknown Object (File)
Oct 14 2024, 9:46 PM
Unknown Object (File)
Oct 14 2024, 9:46 PM
Subscribers

Details

Summary

ENG-6667 : Implement "primary" tab lock

We want to only have a singular tunnelbroker connection across all tabs on web. One way to achieve this is to use the WebLockAPI. The useWebLock hook exposes a nicer interface to it. It returns lock status and releaseLock function. When the status is equal to 'should-release' the caller is expected to run cleanup (e.g. disconnecting from the tunnelbroker) and call releaseLock.

Only the foreground tabs will try and acquire a lock. After a tab is backgrounded it should release the lock if it has one.

Some information about the web lock api:

  • It allows to request a lock with a specific name
  • The lock can be held only "in one place"/"one call to the request method" across all tabs of the same origin
    • If we make sure to only call it once in the app, we can get the required locking behaviour between tabs
    • (this is assuming the exclusive mode, which is the default)
  • If the lock is already taken, the request call goes into a queue, and will wait until the lock is released
    • Request in queue can be aborted with an AbortSignall
  • Call to request returns a promise and expects an async callback as an argument. When it acquired the lock it calls the provided callback. The promise returned by the request call resolves when the the promise returned from the callback resolves.
  • Locks are released automatically after the tab is closed

Additionally our version of flow doesn't type the LockManager (this was also the case for e.g. service worker and shared worker types) so I had to do it myself.

Test Plan

Add this code:

const { lockStatus, releaseLock } = useWebLock('lock');
React.useEffect(() => {
   document.title = lockStatus;

   if(lockStatus === 'should-release') {
     // Add some delay to simulate closing the tunnelbroker socket
     setTimeout(releaseLock, 1000);
   }
 }, [lockStatus]);

Try switching between different tabs quickly, try displaying two tabs at the same time. Check that at all times the only tab holding the lock is one of the tabs in the foreground (except for the 1s delay before call to releaseLock). Check that if no tab is selected the lock is released.

Diff Detail

Repository
rCOMM Comm
Lint
Lint Not Applicable
Unit
Tests Not Applicable

Event Timeline

michal edited the test plan for this revision. (Show Details)
web/web-lock.js
94–120 ↗(On Diff #37337)

Should these 3 types be read-only?

kamil added inline comments.
web/web-lock.js
14 ↗(On Diff #37337)

I would improve naming here, because this state is set when we have lock or lock request, now it's a bit confusing

19 ↗(On Diff #37337)
This revision is now accepted and ready to land.Mar 4 2024, 5:55 AM
web/web-lock.js
7 ↗(On Diff #37337)

Looking at usage in D11118 I think it's better to wrap this in Object.freeze({ })