TxSemaphore.ts
TxSemaphore.ts overview
Section titled “TxSemaphore.ts overview”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.
Since v4.0.0
Exports Grouped by Category
Section titled “Exports Grouped by Category”combinators
Section titled “combinators”acquire
Section titled “acquire”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.
Example (Acquiring a permit)
import { Console, Effect, TxSemaphore } from "effect"
const program = Effect.gen(function* () { const semaphore = yield* TxSemaphore.make(2)
yield* Console.log("Acquiring first permit...") yield* TxSemaphore.acquire(semaphore) yield* Console.log("First permit acquired")
yield* Console.log("Acquiring second permit...") yield* TxSemaphore.acquire(semaphore) yield* Console.log("Second permit acquired")
const available = yield* TxSemaphore.available(semaphore) yield* Console.log(`Available permits: ${available}`) // 0})See
tryAcquirefor a non-blocking single-permit attemptreleasefor returning one permitwithPermitfor automatic acquire and release around an effect
Signature
declare const acquire: (self: TxSemaphore) => Effect.Effect<void>Since v2.0.0
acquireN
Section titled “acquireN”Acquires 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.
Example (Acquiring multiple permits)
import { Console, Effect, TxSemaphore } from "effect"
const program = Effect.gen(function* () { const semaphore = yield* TxSemaphore.make(5)
yield* Console.log("Acquiring 3 permits...") yield* TxSemaphore.acquireN(semaphore, 3) yield* Console.log("3 permits acquired")
const available = yield* TxSemaphore.available(semaphore) yield* Console.log(`Available permits: ${available}`) // 2})See
tryAcquireNfor a non-blocking multi-permit attemptreleaseNfor returning multiple permitswithPermitsfor automatic acquire and release around an effect
Signature
declare const acquireN: (self: TxSemaphore, n: number) => Effect.Effect<void>Since v2.0.0
available
Section titled “available”Gets the current number of available permits in the semaphore.
When to use
Use to inspect how many permits are currently available.
Example (Checking available permits)
import { Console, 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) yield* Console.log(`Available permits: ${before}`) // 5
// Acquire some permits yield* TxSemaphore.acquire(semaphore) yield* TxSemaphore.acquire(semaphore)
// Check available permits after acquiring const after = yield* TxSemaphore.available(semaphore) yield* Console.log(`Available permits: ${after}`) // 3})See
capacityfor reading the fixed total permit count
Signature
declare const available: (self: TxSemaphore) => Effect.Effect<number>Since v2.0.0
capacity
Section titled “capacity”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.
Example (Checking semaphore capacity)
import { Console, Effect, TxSemaphore } from "effect"
const program = Effect.gen(function* () { const semaphore = yield* TxSemaphore.make(10)
const capacity = yield* TxSemaphore.capacity(semaphore) yield* Console.log(`Semaphore capacity: ${capacity}`) // 10
// Capacity remains constant regardless of current permits yield* TxSemaphore.acquire(semaphore) const stillSame = yield* TxSemaphore.capacity(semaphore) yield* Console.log(`Capacity after acquire: ${stillSame}`) // 10})See
availablefor reading the current available permit count
Signature
declare const capacity: (self: TxSemaphore) => Effect.Effect<number>Since v4.0.0
release
Section titled “release”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.
Example (Releasing a permit)
import { Console, Effect, TxSemaphore } from "effect"
const program = Effect.gen(function* () { const semaphore = yield* TxSemaphore.make(2)
// Acquire a permit yield* TxSemaphore.acquire(semaphore) let available = yield* TxSemaphore.available(semaphore) yield* Console.log(`After acquire: ${available}`) // 1
// Release the permit yield* TxSemaphore.release(semaphore) available = yield* TxSemaphore.available(semaphore) yield* Console.log(`After release: ${available}`) // 2})See
acquirefor manually acquiring one permitreleaseNfor returning multiple permits
Signature
declare const release: (self: TxSemaphore) => Effect.Effect<void>Since v2.0.0
releaseN
Section titled “releaseN”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.
Example (Releasing multiple permits)
import { Console, Effect, TxSemaphore } from "effect"
const program = Effect.gen(function* () { const semaphore = yield* TxSemaphore.make(5)
// Acquire 3 permits yield* TxSemaphore.acquireN(semaphore, 3) let available = yield* TxSemaphore.available(semaphore) yield* Console.log(`After acquire: ${available}`) // 2
// Release 2 permits yield* TxSemaphore.releaseN(semaphore, 2) available = yield* TxSemaphore.available(semaphore) yield* Console.log(`After release: ${available}`) // 4})See
acquireNfor manually acquiring multiple permitsreleasefor returning one permit
Signature
declare const releaseN: (self: TxSemaphore, n: number) => Effect.Effect<void>Since v2.0.0
tryAcquire
Section titled “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.
Example (Trying to acquire a permit)
import { Console, 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) yield* Console.log(`First try: ${first}`) // true
// Second try should fail (no permits left) const second = yield* TxSemaphore.tryAcquire(semaphore) yield* Console.log(`Second try: ${second}`) // false})See
acquirefor waiting until one permit is availabletryAcquireNfor attempting to acquire multiple permits without blocking
Signature
declare const tryAcquire: (self: TxSemaphore) => Effect.Effect<boolean>Since v4.0.0
tryAcquireN
Section titled “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.
Example (Trying to acquire multiple permits)
import { Console, 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) yield* Console.log(`First try (2 permits): ${first}`) // true
// Try to acquire 2 more permits (should fail, only 1 left) const second = yield* TxSemaphore.tryAcquireN(semaphore, 2) yield* Console.log(`Second try (2 permits): ${second}`) // false})See
acquireNfor waiting until all requested permits are availabletryAcquirefor attempting to acquire one permit without blocking
Signature
declare const tryAcquireN: (self: TxSemaphore, n: number) => Effect.Effect<boolean>Since v4.0.0
withPermit
Section titled “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.
Example (Running an effect with a permit)
import { Console, Effect, TxSemaphore } from "effect"
const program = Effect.gen(function* () { const semaphore = yield* TxSemaphore.make(2)
// Execute database operation with automatic permit management const result = yield* TxSemaphore.withPermit( semaphore, Effect.gen(function* () { yield* Console.log("Permit acquired, accessing database...") yield* Effect.sleep("100 millis") // Simulate database work yield* Console.log("Database operation complete") return "query result" }) )
yield* Console.log(`Result: ${result}`) // Permit is automatically released here})See
withPermitsfor automatically acquiring and releasing multiple permitswithPermitScopedfor acquiring one permit for the current scopeacquirefor manual single-permit acquisition
Signature
declare const withPermit: { (self: TxSemaphore): <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R> <A, E, R>(self: TxSemaphore, effect: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>}Since v2.0.0
withPermitScoped
Section titled “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.
Example (Acquiring a scoped permit)
import { Console, Effect, TxSemaphore } from "effect"
const program = Effect.gen(function* () { const semaphore = yield* TxSemaphore.make(3)
yield* Effect.scoped( Effect.gen(function* () { // Acquire permit for the duration of this scope yield* TxSemaphore.withPermitScoped(semaphore) yield* Console.log("Permit acquired for scope")
// Do work within the scope yield* Effect.sleep("500 millis") yield* Console.log("Work completed")
// Permit will be automatically released when scope closes }) )
yield* Console.log("Scope closed, permit released")})See
withPermitfor acquiring one permit around a single effectacquirefor manual single-permit acquisition
Signature
declare const withPermitScoped: (self: TxSemaphore) => Effect.Effect<void, never, Scope.Scope>Since v2.0.0
withPermits
Section titled “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.
Example (Running an effect with multiple permits)
import { Console, Effect, TxSemaphore } from "effect"
const program = Effect.gen(function* () { const semaphore = yield* TxSemaphore.make(5)
// Execute batch operation with 3 permits const results = yield* TxSemaphore.withPermits( semaphore, 3, Effect.gen(function* () { yield* Console.log("3 permits acquired, processing batch...") yield* Effect.sleep("200 millis") // Simulate batch processing return ["result1", "result2", "result3"] }) )
yield* Console.log(`Batch results: ${results.join(", ")}`) // All 3 permits are automatically released here})See
withPermitfor automatically acquiring and releasing one permitacquireNfor manual multi-permit acquisition
Signature
declare const withPermits: { (self: TxSemaphore, n: number): <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R> <A, E, R>(self: TxSemaphore, n: number, effect: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>}Since v2.0.0
constructors
Section titled “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.
Example (Creating a semaphore)
import { Console, 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)
yield* Console.log(`Created semaphore with ${capacity} permits, ${available} available`) // Output: "Created semaphore with 3 permits, 3 available"})See
availablefor reading the current available permit countcapacityfor reading the fixed total permit count
Signature
declare const make: (permits: number) => Effect.Effect<TxSemaphore>Since v2.0.0
guards
Section titled “guards”isTxSemaphore
Section titled “isTxSemaphore”Determines if the provided value is a TxSemaphore.
When to use
Use to narrow an unknown value before treating it as a TxSemaphore.
Example (Checking semaphore values)
import { Effect, TxSemaphore } from "effect"
const program = Effect.gen(function* () { const semaphore = yield* TxSemaphore.make(5) const notSemaphore = { some: "object" }
console.log(TxSemaphore.isTxSemaphore(semaphore)) // true console.log(TxSemaphore.isTxSemaphore(notSemaphore)) // false
// Useful for runtime type checking in generic functions if (TxSemaphore.isTxSemaphore(semaphore)) { const available = yield* TxSemaphore.available(semaphore) console.log(`Available permits: ${available}`) }})See
makefor creating aTxSemaphore
Signature
declare const isTxSemaphore: (u: unknown) => u is TxSemaphoreSince v4.0.0
models
Section titled “models”TxSemaphore (interface)
Section titled “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.
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) console.log("Database connection acquired")
// Perform database operations...
// Release the permit when done yield* TxSemaphore.release(dbSemaphore) console.log("Database connection released")})See
makefor creating a transactional semaphorewithPermitfor automatically acquiring and releasing one permitacquirefor manually acquiring one permit transactionally
Signature
export interface TxSemaphore extends Inspectable, Pipeable { readonly [TypeId]: typeof TypeId readonly permitsRef: TxRef.TxRef<number> readonly capacity: number}Since v4.0.0