Skip to content

trananhtung/mutexkit

Repository files navigation

mutexkit

All Contributors

Fair async Mutex and counting Semaphore — weighted permits, runExclusive, and AbortSignal support. Zero dependencies.

CI npm version bundle size types license

JavaScript is single-threaded, but async still interleaves — two handlers can read-modify-write the same resource between awaits and corrupt it. mutexkit gives you a lock to serialize a critical section, and a semaphore to cap how many things touch a resource at once.

import { Mutex } from "mutexkit";

const mutex = new Mutex();

// No matter how many callers, the file is appended to one at a time, in order.
await mutex.runExclusive(() => appendLine(file, line));

Why mutexkit?

  • Mutex & Semaphore in one tiny package. Mutex for one-at-a-time; Semaphore(n) for at-most-n.
  • runExclusive does the right thing. Acquires, runs your function, and always releases — even if it throws.
  • Weighted permits. Charge more than one permit per acquire (e.g. memory-sized jobs against a budget).
  • Fair & cancellable. FIFO ordering so nobody starves; pass an AbortSignal to give up waiting. release() is idempotent — double-calling never over-releases.
  • Zero dependencies, ESM + CJS + types.

Install

npm install mutexkit
# or: pnpm add mutexkit  /  yarn add mutexkit  /  bun add mutexkit

API

Mutex

const mutex = new Mutex();

await mutex.runExclusive(async () => { /* critical section */ });

const release = await mutex.acquire();   // manual form
try { /* ... */ } finally { release(); }

mutex.tryAcquire();  // Release | null (null if held)
mutex.isLocked;      // boolean
mutex.pending;       // queued waiters

acquire / runExclusive accept { signal } to cancel a wait.

Semaphore

import { Semaphore } from "mutexkit";

const sem = new Semaphore(5); // at most 5 concurrent

await Promise.all(tasks.map((t) => sem.runExclusive(() => t())));

const release = await sem.acquire({ weight: 2 }); // take 2 permits
release();

sem.tryAcquire(2); // Release | null
sem.available;     // free permits
sem.pending;       // queued waiters

acquire / runExclusive accept { signal, weight }.

When to reach for which

Need Use
Only one execution of a critical section at a time Mutex
At most N concurrent Semaphore(N)
Cap concurrency while mapping a list runpool
Cap rate (per second) ratebucket

Contributors ✨

This project follows the all-contributors specification. Contributions of any kind are welcome — code, docs, bug reports, ideas, reviews! See the emoji key for how each contribution is recognized, and open a PR or issue to get involved.

Thanks goes to these wonderful people:

Tung Tran
Tung Tran

💻 🚧

License

MIT © Tung Tran

About

Fair async Mutex and counting Semaphore with weighted permits, runExclusive, and AbortSignal support. Zero dependencies.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages