Semaphore
A synchronization primitive for limiting concurrent usage
Usage
A semaphore is a synchronization primitive that allows a limited number of concurrent operations to proceed.
import { Semaphore } from 'radashi'
const semaphore = new Semaphore(2)
const permit = await semaphore.acquire()
permit.release()When the semaphore’s capacity is reached, subsequent calls to semaphore.acquire() will block until a permit is released.
import { Semaphore } from 'radashi'
const semaphore = new Semaphore(1)
const permit = await semaphore.acquire() // Acquires the only permit
// This next acquire call will block until the first permit is releasedconst blockingAcquire = semaphore.acquire()
// ... later ...permit.release() // Releasing the permit allows blockingAcquire to resolveYou can acquire permits with a specific weight. The semaphore’s capacity is reduced by this weight. If the remaining capacity is less than the requested weight, the acquisition will block.
import { Semaphore } from 'radashi'
const semaphore = new Semaphore(4)
// Acquire a permit with weight 2const permit = await semaphore.acquire({ weight: 2 })
// Capacity is now 2. Acquiring with weight 1 or 2 is possible.await semaphore.acquire({ weight: 1 })
// Acquiring with weight 2 would block now as capacity is only 1.A permit can only be released once. Subsequent calls to permit.release() on the same permit instance will have no effect.
import { Semaphore } from 'radashi'
const semaphore = new Semaphore(1)const permit = await semaphore.acquire()
permit.release() // Releases the permitpermit.release() // Has no effectThe semaphore constructor requires a maxCapacity of more than 0. Acquiring a permit requires a weight of more than 0 and not exceeding the semaphore’s maxCapacity. Invalid options will result in errors.
import { Semaphore } from 'radashi'
// Invalid constructor// new Semaphore(0) // Throws Error: maxCapacity must be > 0
const semaphore = new Semaphore(2)
// Invalid acquire options// await semaphore.acquire({ weight: 0 }) // Throws Error: weight must be > 0// await semaphore.acquire({ weight: 3 }) // Throws Error: weight must be ≤ maxCapacityYou can abort a pending acquisition using an AbortController and its signal. If the signal is aborted before the permit is acquired, the acquire promise will reject with an AbortError.
import { Semaphore } from 'radashi'
const semaphore = new Semaphore(1)await semaphore.acquire() // Occupy the only permit
const controller = new AbortController()const acquirePromise = semaphore.acquire({ signal: controller.signal })
// Abort the acquisition before it can completecontroller.abort()
// The acquirePromise will now rejectawait expect(acquirePromise).rejects.toThrow('This operation was aborted')You can reject all pending and future acquisition requests by calling semaphore.reject(). All promises returned by pending and future acquire calls will reject with the provided error.
import { Semaphore } from 'radashi'
const semaphore = new Semaphore(1)await semaphore.acquire() // Occupy the only permit
const acquirePromise = semaphore.acquire() // This will block
const rejectionError = new Error('Operation cancelled')semaphore.reject(rejectionError)
// The acquirePromise will now reject with the specified errorawait expect(acquirePromise).rejects.toThrow('Operation cancelled')
// Future acquisition requests will also be rejectedawait expect(() => semaphore.acquire()).rejects.toThrow('Operation cancelled')