Class Mutex

Source
Expand description

Non-reentrant mutex

This allows only one context to enter a block at a time in a FIFO manner.

This mutex is non-reentrant. Trying to lock it again while the same context already owns the lock will cause a dead lock.

While a context id can be used to implement reentrant locks, it is very cumbersome to use. https://github.com/tc39/proposal-async-context will allow for a cleaner implementation.

import { Mutex } from "@pistonite/pure/sync";

const mutex = new Mutex();
let counter = 0;

const increment = async () => {
await mutex.scopedLock(async () => {
// only one context can be inside this block at a time,
// so the read-modify-write below is never interleaved
const current = counter;
await new Promise((resolve) => setTimeout(resolve, 10));
counter = current + 1;
});
};

await Promise.all([increment(), increment(), increment()]);
console.log(counter); // 3

The value returned by the closure is returned to the caller, and exceptions thrown by the closure are re-thrown to the caller. The lock is released in both cases.

The lock is handed off directly to the context that has been waiting the longest, so waiters are guaranteed to acquire the lock in the order they called scopedLock. A context that starts waiting while the lock is being released cannot barge in front of the contexts already waiting.

Constructors§

§

new Mutex(): Mutex

Accessors§

Source§

get isLocked(): boolean

Check if the mutex is currently locked by some context

Methods§

Source§

scopedLock<R>(fn: () => R | Promise<R>): Promise<R>

Acquire the lock and call fn. Release the lock when fn returns or throws.