diff --git a/src/lib/locks.ts b/src/lib/locks.ts index 90765a79..40f91e14 100644 --- a/src/lib/locks.ts +++ b/src/lib/locks.ts @@ -2,6 +2,9 @@ import {messageAllTabs} from "@src/lib/messaging" import {v1 as uuid} from "uuid" +import * as Logging from "@src/lib/logging" + +const logger = new Logging.Logger("locks") export const OWNED_LOCKS = new Set() @@ -11,24 +14,41 @@ export const ID = uuid() const now = () => (new Date()).getTime() + Math.random() // getTime is accurate only to ms, so fake microseconds with random -export async function acquire(lockname: string, timeout= 2000) { +export async function acquire(lockname: string) { if (OWNED_LOCKS.has(lockname) || DESIRED_LOCKS.hasOwnProperty(lockname)) return; const time = now() DESIRED_LOCKS[lockname] = time - await Promise.race([ - Promise.all([ - browser.runtime.sendMessage({type: "lock", command: "acquire", args: [lockname, time, ID]}), - messageAllTabs("lock", "acquire", [lockname, time, ID]), - ]), - new Promise(resolve => setTimeout(resolve, timeout)), // Take lock anyway after timeout + await Promise.all([ + browser.runtime.sendMessage({type: "lock", command: "acquire", args: [lockname, time, ID]}), + messageAllTabs("lock", "acquire", [lockname, time, ID]), ]) delete DESIRED_LOCKS[lockname] OWNED_LOCKS.add(lockname) } +/** + * Execute func after acquiring a named lock. If func takes longer than timeout, release the lock early. + * Returns the value of func. + */ +export async function withlock(lockname: string, func, timeout= 2000) { + await acquire(lockname) + const p = (async () => func())() // Ensure function is promisified + const a = await Promise.race([ + p, + new Promise(resolve => setTimeout( + () => { + resolve("ERROR: LOST THE RACE") + }, + timeout)), // Release the lock anyway after timeout + ]) + if (a === "ERROR: LOST THE RACE") {logger.warning("lock " + lockname + " was released early")} + release(lockname) + return p +} + export async function release(lockname: string) { OWNED_LOCKS.delete(lockname) }