Semaphore.ts
Semaphore.ts overview
Section titled “Semaphore.ts overview”Limits how many effects can use a shared resource at the same time.
A Semaphore owns a number of permits. Work can run only after acquiring the
permits it needs, and those permits are returned when the work finishes. This
module includes constructors, automatic wrappers that acquire and release
permits around an effect, manual permit operations, a non-waiting variant for
work that should only run immediately, and resizing support for an existing
semaphore.
Since v4.0.0
Exports Grouped by Category
Section titled “Exports Grouped by Category”combinators
Section titled “combinators”release
Section titled “release”Releases the specified number of permits and returns the resulting available permits.
When to use
Use when you need to return permits acquired with take in a lower-level
permit protocol with explicit release control.
Details
Running the effect releases the requested permits, wakes waiting acquirers when permits become available, and returns the current available permit count.
Gotchas
Manual take / release usage must keep permit counts balanced. Prefer
withPermit or withPermits when the acquisition can be scoped to one
effect.
See
takefor manually acquiring permitsreleaseAllfor returning every currently taken permitwithPermitsfor automatic acquire and release around an effect
Signature
declare const release: { (permits: number): (self: Semaphore) => Effect.Effect<number> (self: Semaphore, permits: number): Effect.Effect<number>}Since v4.0.0
releaseAll
Section titled “releaseAll”Releases all permits held by this semaphore and returns the resulting available permits.
When to use
Use to return every currently taken permit to a semaphore at once, typically
during cleanup of manual take / release protocols.
See
releasefor releasing a known permit countwithPermitsfor automatic acquire and release around an effect
Signature
declare const releaseAll: (self: Semaphore) => Effect.Effect<number>Since v4.0.0
resize
Section titled “resize”Sets the total number of permits managed by the semaphore.
When to use
Use to change the concurrency limit of an existing semaphore while keeping current acquisitions in place.
Details
Existing acquisitions remain taken after resizing. If the new total is less than the currently taken permit count, new acquisitions wait until enough permits are released.
See
makefor creating a semaphore with an initial permit countreleasefor returning permits without changing semaphore capacity
Signature
declare const resize: { (permits: number): (self: Semaphore) => Effect.Effect<void> (self: Semaphore, permits: number): Effect.Effect<void>}Since v4.0.0
Acquires the specified number of permits and returns the acquired permit count.
When to use
Use when you need manual permit acquisition for a lower-level protocol with explicit acquisition and release control.
Details
The effect waits until enough permits are available.
See
withPermitfor automatically acquiring and releasing one permit around an effectwithPermitsfor automatically acquiring and releasing multiple permits around an effectreleasefor returning manually acquired permits
Signature
declare const take: { (permits: number): (self: Semaphore) => Effect.Effect<number> (self: Semaphore, permits: number): Effect.Effect<number>}Since v4.0.0
withPermit
Section titled “withPermit”Runs an effect with a single permit and releases the permit when the effect completes.
When to use
Use to guard an effect with exactly one semaphore permit while automatically releasing that permit when the effect exits.
See
withPermitsfor acquiring more than one permitwithPermitsIfAvailablefor running only when permits are immediately availabletakefor manually acquiring permitsreleasefor manually returning permits
Signature
declare const withPermit: { (self: Semaphore): <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R> <A, E, R>(self: Semaphore, effect: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>}Since v4.0.0
withPermits
Section titled “withPermits”Runs an effect with the given number of permits and releases the permits when the effect completes.
When to use
Use to run an effect while holding a specified number of semaphore permits for the duration of that effect.
Details
The effect waits until enough permits are available. Acquired permits are released when the wrapped effect exits.
See
withPermitfor acquiring exactly one permitwithPermitsIfAvailablefor running only when permits are immediately availabletakefor manually acquiring permitsreleasefor manually returning permits
Signature
declare const withPermits: { (self: Semaphore, permits: number): <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R> <A, E, R>(self: Semaphore, permits: number, effect: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>}Since v4.0.0
withPermitsIfAvailable
Section titled “withPermitsIfAvailable”Runs an effect only if the specified number of permits are immediately available.
When to use
Use when guarded work should run only if the requested permits are immediately available.
Details
When the permits are unavailable, the effect is not run and the result is
Option.none. When permits are available, the effect is run, its result is
wrapped in Option.some, and the acquired permits are released when the
effect exits.
See
withPermitsfor the variant that waits until permits are available
Signature
declare const withPermitsIfAvailable: { (self: Semaphore, permits: number): <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<Option.Option<A>, E, R> <A, E, R>(self: Semaphore, permits: number, effect: Effect.Effect<A, E, R>): Effect.Effect<Option.Option<A>, E, R>}Since v4.0.0
constructors
Section titled “constructors”Creates a Semaphore initialized with the specified total number of permits.
When to use
Use to create a semaphore inside Effect code for bounding concurrency with automatic or manual permit management.
Example (Creating a semaphore)
import { Effect, Semaphore } from "effect"
const program = Effect.gen(function* () { const semaphore = yield* Semaphore.make(2)
const task = (id: number) => semaphore.withPermits(1)( Effect.gen(function* () { yield* Effect.log(`Task ${id} acquired permit`) yield* Effect.sleep("1 second") yield* Effect.log(`Task ${id} releasing permit`) }) )
// Run 4 tasks, but only 2 can run concurrently yield* Effect.all([task(1), task(2), task(3), task(4)])})Signature
declare const make: (permits: number) => Effect.Effect<Semaphore>Since v4.0.0
makeUnsafe
Section titled “makeUnsafe”Creates a Semaphore synchronously with the specified total
number of permits.
When to use
Use to construct a semaphore synchronously when an immediate value is required outside an Effect workflow.
Example (Creating an unsafe semaphore)
import { Effect, Semaphore } from "effect"
const semaphore = Semaphore.makeUnsafe(3)
const task = (id: number) => semaphore.withPermits(1)( Effect.gen(function* () { yield* Effect.log(`Task ${id} started`) yield* Effect.sleep("1 second") yield* Effect.log(`Task ${id} completed`) }) )
// Only 3 tasks can run concurrentlyconst program = Effect.all([task(1), task(2), task(3), task(4), task(5)], { concurrency: "unbounded" })Signature
declare const makeUnsafe: (permits: number) => SemaphoreSince v4.0.0
models
Section titled “models”Semaphore (interface)
Section titled “Semaphore (interface)”A counting semaphore that coordinates concurrent access with permits.
When to use
Use to coordinate concurrent effects that need bounded access to a shared resource.
Details
Effects can acquire permits, wait until enough permits are available, release permits, or run with permits that are automatically released when the effect exits.
Example (Controlling concurrent access)
import { Effect, Semaphore } from "effect"
// Create and use a semaphore for controlling concurrent accessconst program = Effect.gen(function* () { const semaphore = yield* Semaphore.make(2)
return yield* semaphore.withPermits(1)(Effect.succeed("Resource accessed"))})See
makefor creating a semaphore inside Effect codemakeUnsafefor creating a semaphore synchronously
Signature
export interface Semaphore { /** * Adjusts the number of permits available in the semaphore. * * **When to use** * * Use to change the total permit count of an existing semaphore. */ resize(this: Semaphore, permits: number): Effect.Effect<void>
/** * Runs an effect with the given number of permits and releases the permits * when the effect completes. * * **When to use** * * Use to run an effect while holding a specified number of semaphore permits. * * **Details** * * This function acquires the specified number of permits before executing * the provided effect. Once the effect finishes, the permits are released. * If insufficient permits are available, the function will wait until they * are released by other tasks. */ withPermits(this: Semaphore, permits: number): <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>
/** * Runs an effect with the given number of permits and releases the permits * when the effect completes. * * **When to use** * * Use to run an effect while holding exactly one semaphore permit. * * **Details** * * This function acquires the specified number of permits before executing * the provided effect. Once the effect finishes, the permits are released. * If insufficient permits are available, the function will wait until they * are released by other tasks. */ withPermit<A, E, R>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
/** * Runs an effect only if the specified number of permits are immediately * available. * * **When to use** * * Use when guarded work should run only if the requested permits are * immediately available. * * **Details** * * This function attempts to acquire the specified number of permits. If they * are available, it runs the effect and releases the permits after the effect * completes. If permits are not available, the effect does not execute, and * the result is `Option.none`. */ withPermitsIfAvailable( this: Semaphore, permits: number ): <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<Option.Option<A>, E, R>
/** * Acquires the specified number of permits and returns the resulting * available permits, suspending the task if they are not yet available. * Concurrent pending `take` calls are processed in a first-in, first-out manner. * * **When to use** * * Use to manually acquire permits for lower-level coordination protocols. */ take(this: Semaphore, permits: number): Effect.Effect<number>
/** * Releases the specified number of permits and returns the resulting * available permits. * * **When to use** * * Use to manually return permits acquired by a lower-level coordination * protocol. */ release(this: Semaphore, permits: number): Effect.Effect<number>
/** * Releases all permits held by this semaphore and returns the resulting available permits. * * **When to use** * * Use to return every currently taken permit to the semaphore at once. */ readonly releaseAll: Effect.Effect<number>}Since v4.0.0