ConcurrencyTypeScriptverifiedVerified

Mutex / Lock Pattern in TypeScript

Guarantee that only one thread at a time can access a shared resource by requiring threads to acquire an exclusive lock before proceeding.

How to Implement the Mutex / Lock Pattern in TypeScript

1Step 1: Implement the Mutex with acquire and release

class Mutex {
  private locked = false;
  private queue: Array<() => void> = [];

  acquire(): Promise<void> {
    if (!this.locked) {
      this.locked = true;
      return Promise.resolve();
    }
    return new Promise<void>(resolve => this.queue.push(resolve));
  }

  release(): void {
    if (this.queue.length > 0) {
      const next = this.queue.shift()!;
      next(); // pass lock to next waiter
    } else {
      this.locked = false;
    }
  }

  async withLock<T>(fn: () => Promise<T>): Promise<T> {
    await this.acquire();
    try {
      return await fn();
    } finally {
      this.release();
    }
  }
}

2Step 2: Protect a shared counter with the mutex

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

async function increment(): Promise<void> {
  await mutex.withLock(async () => {
    const current = counter;
    await new Promise(r => setTimeout(r, 1)); // simulate async work
    counter = current + 1;
  });
}

await Promise.all(Array.from({ length: 10 }, increment));
console.log(counter); // always 10

Mutex / Lock Pattern Architecture

hourglass_empty

Rendering diagram...

lightbulb

Mutex / Lock Pattern in the Real World

A single-occupancy public restroom with a door latch is a perfect mutex. The latch (lock) ensures only one person (thread) occupies the restroom (critical section) at a time. Anyone who finds the door locked must wait outside; when the occupant leaves and unlatches the door, one waiting person may enter.