
Security News
RubyGems Adds Cooldown Feature to Bundler for Newly Published Gems
RubyGems and Bundler 4.0.13 introduced an opt-in cooldown feature that delays newly published gems during dependency resolution.
async-shared-mutex
Advanced tools
Lightweight shared (reader) / exclusive (writer) mutex for TypeScript / ESM. Two flavors:
SharedMutex – low‑level handle based API (you manage the critical section).AsyncSharedMutex – convenience API that runs a function while holding the mutex.Both support shared (concurrent) and exclusive (mutually exclusive) acquisition.
import { AsyncSharedMutex } from 'async-shared-mutex'
const mtx = new AsyncSharedMutex()
// Exclusive (writer)
await mtx.lock(async () => {
// only one task may run here
await doWrite()
})
// Shared (reader) – many may run together
const [a, b, c] = await Promise.all([
mtx.lockShared(() => readValue('a')),
mtx.lockShared(() => readValue('b')),
mtx.lockShared(() => readValue('c')),
])
import { SharedMutex } from 'async-shared-mutex'
const mtx = new SharedMutex()
// Exclusive
const exclusive = await mtx.lock()
try {
await doWrite()
}
finally {
exclusive.unlock()
}
// Shared
const shared = await mtx.lockShared()
try {
const v = await readValue()
console.log(v)
}
finally {
shared.unlock()
}
using (TS 5.2+)import { SharedMutex } from 'async-shared-mutex'
const mtx = new SharedMutex()
async function doStuff() {
using lock = await mtx.lock() // unlocks automatically at end of scope
await mutate()
}
If your runtime lacks native
Symbol.dispose, add a small polyfill (seetest/helpers/patchDisposable.tsfor an example) or keep callingunlock()manually.
Use for coordinating access to a resource where:
try* variants attempt an instantaneous acquisition; they return null if not immediately possible (no waiting side effects).This gives predictable writer progress (no writer starvation) while still batching readers that arrive before the next writer.
class SharedMutexLow level; you get locks you must unlock.
| Method | Returns | Description |
|---|---|---|
lock() | Promise<LockHandle> | Await for an exclusive (writer) handle. |
tryLock() | LockHandle | null | Immediate exclusive attempt. null if busy. |
lockShared() | Promise<LockHandle> | Await for a shared (reader) handle. |
tryLockShared() | LockHandle | null | Immediate shared attempt (fails if an exclusive is active/pending). |
LockHandle:
unlock(): void – idempotent; may be called multiple times.[Symbol.dispose]() – same as unlock() enabling using.class AsyncSharedMutexWraps SharedMutex and runs a function while holding the mutex.
| Method | Returns | Description |
|---|---|---|
lock(task) | Promise<T> | Run task exclusively. |
tryLock(task) | Promise<T> | null | Immediate exclusive attempt. If acquired, runs task; else null. |
lockShared(task) | Promise<T> | Run task under a shared lock. |
tryLockShared(task) | Promise<T> | null | Immediate shared attempt. |
task signature: () => T | PromiseLike<T>
If task throws / rejects, the mutex is unlocked and the error is re-thrown. No additional wrapping.
time →
S S S (queued) E (queued after those S) S S (queued after E)
|<--- overlap --->|<--- exclusive alone --->|<--- overlap --->|
Debounce writes while permitting many simultaneous reads:
const stateMtx = new AsyncSharedMutex()
let state: Data
export const readState = () => stateMtx.lockShared(() => state)
export const updateState = (patch: Partial<Data>) => stateMtx.lock(async () => {
state = { ...state, ...patch }
})
Attempt a fast read path that falls back to waiting if a writer is in flight:
const mtx = new SharedMutex()
export async function getSnapshot(): Promise<Snapshot> {
const h = mtx.tryLockShared() || await mtx.lockShared()
try {
return snapshot()
}
finally {
h.unlock()
}
}
Modern Node / browsers, ES2022.
AbortController in your tasks if required.SharedMutex | AsyncSharedMutex | |
|---|---|---|
| Style | Manual handles | Higher level task runner |
| Cleanup | Call unlock() / using | Automatic around function |
| Overhead | Slightly lower | Wrapper promise per task |
MIT
Feel free to open issues / PRs for ideas (timeouts, cancellation helpers, metrics, etc.).
FAQs
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
RubyGems and Bundler 4.0.13 introduced an opt-in cooldown feature that delays newly published gems during dependency resolution.

Security News
pnpm 11.5 now recognizes npm staged publish approvals in release metadata, preventing those releases from being mistaken for lower-trust package publishes.

Security News
Federal audit finds NIST lacked a plan to clear the NVD backlog, wasted funds on duplicate work, and delayed use of CISA data.