TxSemaphore
Coordinates access to limited resources inside transactions.
A TxSemaphore has a fixed capacity and stores its available permit count in
a TxRef. Acquiring or releasing permits can therefore commit atomically
with other transactional state changes. This module includes operations for
creating semaphores, checking capacity and availability, acquiring or
releasing permits, and running effects while permits are held.
Combinators
Acquires a single permit from the semaphore. If no permits are available, the effect will block until one becomes available.
When to use
Use to manually acquire one permit transactionally, waiting until one is available.
See
- tryAcquire for a non-blocking single-permit attempt
- release for returning one permit
- withPermit for automatic acquire and release around an effect
Signature
declare function acquire(self: TxSemaphore): Effect<void>Example
(Acquiring a permit)
import { Effect, TxSemaphore } from "effect"
const program = Effect.gen(function*() { const semaphore = yield* TxSemaphore.make(2)
yield* TxSemaphore.acquire(semaphore)
yield* TxSemaphore.acquire(semaphore)
return yield* TxSemaphore.available(semaphore)})
await Effect.runPromise(program) // => 0Acquires the specified number of permits from the semaphore.
When to use
Use to manually acquire multiple permits transactionally, waiting until all requested permits are available.
Details
If fewer than n permits are available, the transaction retries until enough
permits are released.
Gotchas
Passing a non-positive n dies with a defect. Passing a value greater than
the semaphore capacity can wait forever because the capacity is fixed.
See
- tryAcquireN for a non-blocking multi-permit attempt
- releaseN for returning multiple permits
- withPermits for automatic acquire and release around an effect
Signature
declare function acquireN(self: TxSemaphore, n: number): Effect<void>Example
(Acquiring multiple permits)
import { Effect, TxSemaphore } from "effect"
const program = Effect.gen(function*() { const semaphore = yield* TxSemaphore.make(5)
yield* TxSemaphore.acquireN(semaphore, 3)
return yield* TxSemaphore.available(semaphore)})
await Effect.runPromise(program) // => 2Gets the current number of available permits in the semaphore.
When to use
Use to inspect how many permits are currently available.
See
- capacity for reading the fixed total permit count
Signature
declare function available(self: TxSemaphore): Effect<number>Example
(Checking available permits)
import { Effect, TxSemaphore } from "effect"
const program = Effect.gen(function*() { const semaphore = yield* TxSemaphore.make(5)
// Check available permits before acquiring const before = yield* TxSemaphore.available(semaphore)
// Acquire some permits yield* TxSemaphore.acquire(semaphore) yield* TxSemaphore.acquire(semaphore)
// Check available permits after acquiring const after = yield* TxSemaphore.available(semaphore) return [before, after] as const})
await Effect.runPromise(program) // => [5, 3]Gets the maximum capacity (total permits) of the semaphore.
When to use
Use to inspect the fixed total number of permits managed by the semaphore.
See
- available for reading the current available permit count
Signature
declare function capacity(self: TxSemaphore): Effect<number>Example
(Checking semaphore capacity)
import { Effect, TxSemaphore } from "effect"
const program = Effect.gen(function*() { const semaphore = yield* TxSemaphore.make(10)
const capacity = yield* TxSemaphore.capacity(semaphore)
// Capacity remains constant regardless of current permits yield* TxSemaphore.acquire(semaphore) const stillSame = yield* TxSemaphore.capacity(semaphore) return [capacity, stillSame] as const})
await Effect.runPromise(program) // => [10, 10]Releases one permit back to the semaphore, making it available for acquisition.
When to use
Use to manually return one permit after a transactional acquire.
Details
If the semaphore is already at capacity, this operation leaves the permit count unchanged.
See
Signature
declare function release(self: TxSemaphore): Effect<void>Example
(Releasing a permit)
import { Effect, TxSemaphore } from "effect"
const program = Effect.gen(function*() { const semaphore = yield* TxSemaphore.make(2)
// Acquire a permit yield* TxSemaphore.acquire(semaphore) const afterAcquire = yield* TxSemaphore.available(semaphore)
// Release the permit yield* TxSemaphore.release(semaphore) const afterRelease = yield* TxSemaphore.available(semaphore) return [afterAcquire, afterRelease] as const})
await Effect.runPromise(program) // => [1, 2]Releases the specified number of permits back to the semaphore.
When to use
Use to manually return multiple permits after a transactional acquire.
Details
The available permit count is capped at the semaphore capacity.
Gotchas
Passing a non-positive n dies with a defect.
See
Signature
declare function releaseN(self: TxSemaphore, n: number): Effect<void>Example
(Releasing multiple permits)
import { Effect, TxSemaphore } from "effect"
const program = Effect.gen(function*() { const semaphore = yield* TxSemaphore.make(5)
// Acquire 3 permits yield* TxSemaphore.acquireN(semaphore, 3) const afterAcquire = yield* TxSemaphore.available(semaphore)
// Release 2 permits yield* TxSemaphore.releaseN(semaphore, 2) const afterRelease = yield* TxSemaphore.available(semaphore) return [afterAcquire, afterRelease] as const})
await Effect.runPromise(program) // => [2, 4]tryAcquire
Tries to acquire a single permit from the semaphore without blocking,
returning true if successful or false if no permits are available.
When to use
Use to attempt a single-permit acquisition without retrying when no permit is available.
See
- acquire for waiting until one permit is available
- tryAcquireN for attempting to acquire multiple permits without blocking
Signature
declare function tryAcquire(self: TxSemaphore): Effect<boolean>Example
(Trying to acquire a permit)
import { Effect, TxSemaphore } from "effect"
const program = Effect.gen(function*() { const semaphore = yield* TxSemaphore.make(1)
// First try should succeed const first = yield* TxSemaphore.tryAcquire(semaphore)
// Second try should fail (no permits left) const second = yield* TxSemaphore.tryAcquire(semaphore) return [first, second] as const})
await Effect.runPromise(program) // => [true, false]tryAcquireN
Tries to acquire the specified number of permits from the semaphore without
blocking, returning true if successful or false if not enough permits are
available.
When to use
Use to attempt a multi-permit acquisition without retrying when not enough permits are available.
See
- acquireN for waiting until all requested permits are available
- tryAcquire for attempting to acquire one permit without blocking
Signature
declare function tryAcquireN(self: TxSemaphore, n: number): Effect<boolean>Example
(Trying to acquire multiple permits)
import { Effect, TxSemaphore } from "effect"
const program = Effect.gen(function*() { const semaphore = yield* TxSemaphore.make(3)
// Try to acquire 2 permits (should succeed) const first = yield* TxSemaphore.tryAcquireN(semaphore, 2)
// Try to acquire 2 more permits (should fail, only 1 left) const second = yield* TxSemaphore.tryAcquireN(semaphore, 2) return [first, second] as const})
await Effect.runPromise(program) // => [true, false]withPermit
Executes an effect with a single permit from the semaphore. The permit is automatically acquired before execution and released afterwards, even if the effect fails or is interrupted.
When to use
Use to run an effect while automatically acquiring and releasing one transactional permit.
Details
The permit acquisition and release operations use atomic semantics to ensure proper resource management with Effect's scoped operations.
See
- withPermits for automatically acquiring and releasing multiple permits
- withPermitScoped for acquiring one permit for the current scope
- acquire for manual single-permit acquisition
Signature
declare const withPermit: { (self: TxSemaphore): <A, E, R>(effect: Effect<A, E, R>) => Effect<A, E, R>; <A, E, R>(self: TxSemaphore, effect: Effect<A, E, R>): Effect<A, E, R>;}Example
(Running an effect with a permit)
import { Effect, TxSemaphore } from "effect"
const program = Effect.gen(function*() { const semaphore = yield* TxSemaphore.make(2) const events: Array<string> = []
// Execute database operation with automatic permit management const result = yield* TxSemaphore.withPermit( semaphore, Effect.gen(function*() { events.push("permit acquired") yield* Effect.yieldNow events.push("operation complete") return "query result" }) )
// Permit is automatically released here const available = yield* TxSemaphore.available(semaphore) return [events, result, available] as const})
await Effect.runPromise(program) // => [["permit acquired", "operation complete"], "query result", 2]withPermits
Runs an effect while holding the specified number of permits from the semaphore.
When to use
Use to run an effect while automatically acquiring and releasing multiple transactional permits.
Details
The permits are acquired before the effect starts and released after it completes, fails, or is interrupted.
Gotchas
Passing a non-positive n dies with a defect. Passing a value greater than
the semaphore capacity can wait forever.
See
- withPermit for automatically acquiring and releasing one permit
- acquireN for manual multi-permit acquisition
Signature
declare const withPermits: { (self: TxSemaphore, n: number): <A, E, R>(effect: Effect<A, E, R>) => Effect<A, E, R>; <A, E, R>(self: TxSemaphore, n: number, effect: Effect<A, E, R>): Effect<A, E, R>;}Example
(Running an effect with multiple permits)
import { Effect, TxSemaphore } from "effect"
const program = Effect.gen(function*() { const semaphore = yield* TxSemaphore.make(5) const events: Array<string> = []
// Execute batch operation with 3 permits const results = yield* TxSemaphore.withPermits( semaphore, 3, Effect.gen(function*() { events.push("3 permits acquired") yield* Effect.yieldNow return ["result1", "result2", "result3"] }) )
// All 3 permits are automatically released here const available = yield* TxSemaphore.available(semaphore) return [events, results, available] as const})
await Effect.runPromise(program) // => [["3 permits acquired"], ["result1", "result2", "result3"], 5]withPermitScoped
Acquires a single permit from the semaphore in a scoped manner. The permit will be automatically released when the scope is closed, even if effects within the scope fail or are interrupted.
When to use
Use to acquire one transactional permit for the lifetime of the current scope.
Details
The permit acquisition and release operations use atomic semantics to ensure proper resource management with Effect's scoped operations.
See
- withPermit for acquiring one permit around a single effect
- acquire for manual single-permit acquisition
Signature
declare function withPermitScoped(self: TxSemaphore): Effect<void, never, Scope>Example
(Acquiring a scoped permit)
import { Effect, TxSemaphore } from "effect"
const program = Effect.gen(function*() { const semaphore = yield* TxSemaphore.make(3) const events: Array<string> = []
yield* Effect.scoped( Effect.gen(function*() { // Acquire permit for the duration of this scope yield* TxSemaphore.withPermitScoped(semaphore) events.push("permit acquired for scope")
// Do work within the scope yield* Effect.yieldNow events.push("work completed")
// Permit will be automatically released when scope closes }) )
const available = yield* TxSemaphore.available(semaphore) return [events, available] as const})
await Effect.runPromise(program) // => [["permit acquired for scope", "work completed"], 3]Constructors
Creates a new TxSemaphore with the specified number of permits.
When to use
Use to create a transactional semaphore with a fixed permit capacity.
See
Signature
declare function make(permits: number): Effect<TxSemaphore>Example
(Creating a semaphore)
import { Effect, TxSemaphore } from "effect"
// Create a semaphore for managing concurrent access to a resource poolconst program = Effect.gen(function*() { // Create a semaphore with 3 permits for a connection pool const connectionSemaphore = yield* TxSemaphore.make(3)
// Check initial state const available = yield* TxSemaphore.available(connectionSemaphore) const capacity = yield* TxSemaphore.capacity(connectionSemaphore) return [capacity, available] as const})
await Effect.runPromise(program) // => [3, 3]Guards
isTxSemaphore
Determines if the provided value is a TxSemaphore.
When to use
Use to narrow an unknown value before treating it as a TxSemaphore.
See
- make for creating a
TxSemaphore
Signature
declare function isTxSemaphore(u: unknown): u is TxSemaphoreExample
(Checking semaphore values)
import { Effect, TxSemaphore } from "effect"
const program = Effect.gen(function*() { const semaphore = yield* TxSemaphore.make(5) const notSemaphore = { some: "object" }
const semaphoreResult = TxSemaphore.isTxSemaphore(semaphore) const objectResult = TxSemaphore.isTxSemaphore(notSemaphore)
// Useful for runtime type checking in generic functions if (TxSemaphore.isTxSemaphore(semaphore)) { const available = yield* TxSemaphore.available(semaphore) return [semaphoreResult, objectResult, available] as const } return [semaphoreResult, objectResult, 0] as const})
await Effect.runPromise(program) // => [true, false, 5]Models
TxSemaphore interface
A transactional semaphore that manages permits using Software Transactional Memory (STM) semantics, providing atomic permit acquisition and release operations within Effect transactions for concurrency control over limited resources.
When to use
Use to coordinate permit accounting atomically with other transactional state changes.
See
- make for creating a transactional semaphore
- withPermit for automatically acquiring and releasing one permit
- acquire for manually acquiring one permit transactionally
Signature
interface TxSemaphore extends Inspectable, Pipeable { readonly "~effect/transactions/TxSemaphore": "~effect/transactions/TxSemaphore"; readonly capacity: number; readonly permitsRef: TxRef<number>;}Example
(Managing permits transactionally)
import { Effect, TxSemaphore } from "effect"
// Create a semaphore with 3 permits for managing concurrent database connectionsconst program = Effect.gen(function*() { const dbSemaphore = yield* TxSemaphore.make(3)
// Acquire a permit before accessing the database yield* TxSemaphore.acquire(dbSemaphore) const acquired = yield* TxSemaphore.available(dbSemaphore)
// Perform database operations...
// Release the permit when done yield* TxSemaphore.release(dbSemaphore) const released = yield* TxSemaphore.available(dbSemaphore) return [acquired, released] as const})
await Effect.runPromise(program) // => [2, 3]