# Semaphore, Latch, and PartitionedSemaphore

Once you have many fibers, you need ways to *coordinate* them. Three small,
composable primitives cover most needs:

- A **`Semaphore`** limits how many fibers may hold a resource at once — perfect
  for capping concurrency against a connection pool, an API quota, or any shared
  resource that can't take unlimited load.
- A **`Latch`** is a gate: fibers wait on it until something opens it, then all
  waiters proceed. Use it to hold work back until initialization is done, or to
  pause and resume a pipeline.
- A **`PartitionedSemaphore`** shares one bounded permit pool across *keys* (e.g.
  one tenant, customer, or host per key) so no single key can starve the others.

## Semaphore

A semaphore holds a fixed number of **permits**. `withPermits(n)` acquires `n`
permits before running an effect and releases them when the effect finishes —
success, failure, *or* interruption. If not enough permits are free, the fiber
suspends until they are. A semaphore with one permit is a mutex.

```ts
import { Effect, Semaphore } from "effect"

const program = Effect.gen(function*() {
  // Create a semaphore that allows at most 2 holders at a time.
  const semaphore = yield* Semaphore.make(2)

  const task = (n: number) =>
    // `withPermit` takes exactly one permit for the duration of the effect.
    semaphore.withPermit(
      Effect.gen(function*() {
        yield* Effect.log(`task ${n} running`)
        yield* Effect.sleep("500 millis")
        yield* Effect.log(`task ${n} done`)
      })
    )

  // Six tasks are forked, but the semaphore lets only 2 run concurrently.
  yield* Effect.forEach([1, 2, 3, 4, 5, 6], task, {
    concurrency: "unbounded"
  })
})

Effect.runFork(program)
```

Here `Effect.forEach` with `concurrency: "unbounded"` *wants* to run all six
tasks at once, but each task wraps itself in `withPermit`, so the semaphore caps
the effective concurrency at two. The permit is released automatically when each
task's effect exits, so the next waiting fiber is admitted immediately.
**Tip:** If all you need is a static parallelism cap over a single collection, the
  [`concurrency` option](https://effect.plants.sh/concurrency/concurrency-options/) is simpler. Reach for
  a `Semaphore` when the *same* limit must be shared across several independent
  call sites — for example, a service that throttles every outbound request it
  makes, no matter who calls it.

### Sharing a semaphore from a service

The real power of a semaphore is sharing one instance everywhere. Hold it in a
service so every consumer goes through the same gate:

```ts
import { Context, Effect, Layer, Semaphore } from "effect"

// A client that allows at most 5 concurrent outbound requests, process-wide.
export class RateLimitedApi extends Context.Service<RateLimitedApi, {
  request<A, E, R>(effect: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
}>()("app/RateLimitedApi") {
  static readonly layer = Layer.effect(
    RateLimitedApi,
    Effect.gen(function*() {
      // One shared semaphore for the lifetime of the layer.
      const semaphore = yield* Semaphore.make(5)

      return RateLimitedApi.of({
        // Every request, from anywhere, competes for the same 5 permits.
        request: (effect) => Semaphore.withPermit(semaphore, effect)
      })
    })
  )
}
```

`Semaphore.make(n)` and the standalone `Semaphore.withPermit` / `withPermits`
functions are the data-first equivalents of the methods on the semaphore object —
use whichever style reads better. You can also acquire several permits at once
with `withPermits(n)`, try without blocking via `withPermitsIfAvailable`, or
adjust the limit at runtime with `Semaphore.resize`.

### Resizing at runtime

`resize` changes the *future* capacity while leaving currently-held permits
untouched. Shrinking below the number of taken permits doesn't revoke anything —
new acquisitions simply wait until releases bring availability back up.

```ts
import { Effect, Semaphore } from "effect"

const program = Effect.gen(function*() {
  const semaphore = yield* Semaphore.make(2)

  // Bump the limit to 10 (e.g. after a config change).
  yield* Semaphore.resize(semaphore, 10)

  // Later, throttle back down to 1.
  yield* Semaphore.resize(semaphore, 1)
})
```

## Latch

A `Latch` is a one-bit gate that fibers can wait on. Its `await` effect suspends
the calling fiber while the latch is **closed**, and resumes once it is
**opened**. Opening is sticky: every fiber that waits after the latch opens
proceeds immediately.

```ts
import { Effect, Latch } from "effect"

const program = Effect.gen(function*() {
  // Start closed: waiters will suspend until we open it.
  const latch = yield* Latch.make(false)

  // Fork a worker that must wait for the green light before doing anything.
  yield* Effect.forkChild(
    Effect.gen(function*() {
      yield* Effect.log("worker: waiting for the latch")
      yield* latch.await
      yield* Effect.log("worker: go!")
    })
  )

  // Do some setup, then open the latch to release the worker.
  yield* Effect.sleep("500 millis")
  yield* Effect.log("main: setup done, opening latch")
  yield* latch.open
})

Effect.runFork(program)
// worker: waiting for the latch
// main: setup done, opening latch
// worker: go!
```

### Open, release, and close

A latch gives you three ways to control waiters, which matter when work arrives
continuously:

- **`open`** — release all current waiters *and* let future waiters pass
  immediately. The gate stays open.
- **`release`** — wake the fibers currently waiting, but leave the latch closed
  so later waiters suspend again. A one-shot pulse.
- **`close`** — re-close an open latch so future `await`s suspend once more.

```ts
import { Effect, Latch } from "effect"

const program = Effect.gen(function*() {
  const latch = yield* Latch.make(false)

  // Pause a pipeline: close the latch and have stages await it.
  yield* Latch.close(latch)

  // ...later, resume just the in-flight waiters without re-opening...
  yield* Latch.release(latch)

  // ...or open it for good so everything flows.
  yield* Latch.open(latch)
})
```

This open/release/close trio makes a latch a natural pause/resume switch for a
[streaming](https://effect.plants.sh/streaming/) pipeline or a pool of workers: close to pause, open to
resume. For a *single* one-time hand-off of a value between fibers — a producer
computing a result that one consumer awaits — reach for a
[`Deferred`](https://effect.plants.sh/state-management/deferred/) instead, which carries the value (or failure)
along with the signal.

### Gating an effect with `whenOpen`

`whenOpen` is `await` followed by an effect, in one step: the effect runs only
once the latch lets the fiber through.

```ts
import { Effect, Latch } from "effect"

const program = Effect.gen(function*() {
  const latch = yield* Latch.make(false)

  // This effect is held back until the latch opens, then runs.
  yield* Effect.forkChild(
    latch.whenOpen(Effect.log("ready — running gated work"))
  )

  yield* Effect.sleep("200 millis")
  yield* latch.open
})
```
**Note:** Latches power the backpressure inside higher-level primitives — Effect's
  [`Queue`](https://effect.plants.sh/concurrency/queue-and-pubsub/) and `Pool` use latches internally to
  suspend producers/consumers until capacity frees up.

## PartitionedSemaphore

A plain `Semaphore` shares one global pool of permits. That's a problem when a
single noisy key — a tenant flooding requests, one slow host — grabs every
released permit and starves everyone else. A `PartitionedSemaphore<K>` keeps the
same fixed *shared* capacity, but tracks waiters per key and hands released
permits back to waiting partitions in **round-robin** order, so each key keeps
making progress.

```ts
import { Effect, PartitionedSemaphore } from "effect"

const program = Effect.gen(function*() {
  // A shared pool of 4 permits, partitioned by tenant id (a string key).
  const sem = yield* PartitionedSemaphore.make<string>({ permits: 4 })

  const handle = (tenant: string, job: number) =>
    // Take one permit *for this tenant*. If tenant "a" floods the pool,
    // released permits still rotate to tenant "b" rather than being
    // re-grabbed entirely by "a".
    sem.withPermit(tenant)(
      Effect.gen(function*() {
        yield* Effect.log(`${tenant}: job ${job} start`)
        yield* Effect.sleep("300 millis")
        yield* Effect.log(`${tenant}: job ${job} done`)
      })
    )

  yield* Effect.forEach(
    [
      handle("a", 1), handle("a", 2), handle("a", 3),
      handle("b", 1), handle("b", 2)
    ],
    (eff) => eff,
    { concurrency: "unbounded" }
  )
})

Effect.runFork(program)
```

The key (`tenant` here) only affects how *waiting* permits are distributed —
the total in-flight count never exceeds the shared `permits` capacity. Use
`withPermits(key, n)` to weight a partition's request by more than one permit.
**Caution:** A few sharp edges from the implementation: acquiring more permits than
  `capacity` **never** completes; requesting zero or fewer permits runs the
  effect without acquiring anything; and a non-finite `permits` value creates an
  unbounded semaphore whose acquire/release are no-ops.
**Tip:** All three primitives here are *fiber-level*, not transactional. If you need
  permits or gates that commit atomically alongside other transactional state
  (`TxRef`, `TxQueue`, …), use the `Tx*` variants such as `TxSemaphore` — see
  [Transactions › Coordination](https://effect.plants.sh/transactions/coordination/).

## Semaphore reference

### `make`

Creates a `Semaphore` with `permits` total permits, inside `Effect`.

```ts
import { Effect, Semaphore } from "effect"

const program = Effect.gen(function*() {
  const semaphore = yield* Semaphore.make(3)
  // => Semaphore with 3 permits
})
```

### `makeUnsafe`

Creates a `Semaphore` synchronously, for when you need the value outside an
Effect workflow (e.g. a module-level shared limiter).

```ts
import { Semaphore } from "effect"

const semaphore = Semaphore.makeUnsafe(3)
// => Semaphore (constructed synchronously)
```

### `withPermits`

Acquires `n` permits around an effect, waiting until they're available, and
releases them when the effect exits (success, failure, or interruption).

```ts
import { Effect, Semaphore } from "effect"

const program = Effect.gen(function*() {
  const sem = yield* Semaphore.make(5)
  // Reserve 3 permits for this heavier piece of work.
  yield* Semaphore.withPermits(sem, 3)(Effect.log("running with 3 permits"))
  // => "running with 3 permits", then 3 permits released
})
```

### `withPermit`

The single-permit form of `withPermits` — a mutex when the semaphore has one
permit.

```ts
import { Effect, Semaphore } from "effect"

const program = Effect.gen(function*() {
  const sem = yield* Semaphore.make(1)
  yield* Semaphore.withPermit(sem, Effect.log("exclusive section"))
  // => "exclusive section"
})
```

### `withPermitsIfAvailable`

Runs the effect only if `n` permits are free *right now*; never waits. Returns
`Option.some(result)` if it ran, `Option.none()` if permits weren't available.

```ts
import { Effect, Option, Semaphore } from "effect"

const program = Effect.gen(function*() {
  const sem = yield* Semaphore.make(1)

  // Manually take the only permit and never release it, draining the pool.
  yield* Semaphore.take(sem, 1)

  const result = yield* Semaphore.withPermitsIfAvailable(sem, 1)(
    Effect.succeed("did the work")
  )
  // => Option.none()  (no permit was available)

  return Option.isNone(result)
})
```

### `take`

Lower-level: acquires `n` permits, waiting if needed, and returns the number
acquired. Pending `take`s are served first-in, first-out. Pair with `release`.

```ts
import { Effect, Semaphore } from "effect"

const program = Effect.gen(function*() {
  const sem = yield* Semaphore.make(2)
  const got = yield* Semaphore.take(sem, 2)
  // => 2
})
```

### `release`

Returns `n` permits to the pool (waking waiters as needed) and yields the
resulting number of available permits. You must keep `take`/`release` balanced
yourself — prefer the scoped `withPermit(s)` helpers when you can.

```ts
import { Effect, Semaphore } from "effect"

const program = Effect.gen(function*() {
  const sem = yield* Semaphore.make(2)
  yield* Semaphore.take(sem, 2)
  const available = yield* Semaphore.release(sem, 1)
  // => 1
})
```

### `releaseAll`

Returns *every* currently-taken permit to the semaphore at once, yielding the
resulting available count. Handy for cleanup of manual `take`/`release`
protocols.

```ts
import { Effect, Semaphore } from "effect"

const program = Effect.gen(function*() {
  const sem = yield* Semaphore.make(3)
  yield* Semaphore.take(sem, 3)
  const available = yield* Semaphore.releaseAll(sem)
  // => 3
})
```

### `resize`

Sets the total permit count of an existing semaphore. Existing acquisitions stay
taken; if the new total is below the taken count, new acquisitions wait.

```ts
import { Effect, Semaphore } from "effect"

const program = Effect.gen(function*() {
  const sem = yield* Semaphore.make(2)
  yield* Semaphore.resize(sem, 8)
  // => future acquisitions can now take up to 8 permits
})
```

### `interface Semaphore`

The semaphore value itself exposes the same operations as methods, so you can
call `semaphore.withPermit(eff)` instead of `Semaphore.withPermit(semaphore, eff)`:
`resize(permits)`, `withPermits(permits)(effect)`, `withPermit(effect)`,
`withPermitsIfAvailable(permits)(effect)`, `take(permits)`, `release(permits)`,
and the `releaseAll` effect property.

```ts
import { Effect, Semaphore } from "effect"

const program = Effect.gen(function*() {
  const sem = yield* Semaphore.make(2)
  // Method form, equivalent to Semaphore.withPermit(sem, ...).
  yield* sem.withPermit(Effect.log("via method"))
  // => "via method"
})
```

## Latch reference

### `make`

Creates a `Latch` inside `Effect`. Starts closed by default; pass `true` to
start open.

```ts
import { Effect, Latch } from "effect"

const program = Effect.gen(function*() {
  const latch = yield* Latch.make(false)
  // => closed Latch
})
```

### `makeUnsafe`

Creates a `Latch` synchronously, outside `Effect`. Starts closed unless `true`
is passed.

```ts
import { Latch } from "effect"

const latch = Latch.makeUnsafe(false)
// => closed Latch (constructed synchronously)
```

### `open`

Opens the latch, releasing current *and* future waiters. Succeeds with `true` if
this call changed the state (closed → open), `false` if it was already open.

```ts
import { Effect, Latch } from "effect"

const program = Effect.gen(function*() {
  const latch = yield* Latch.make(false)
  const changed = yield* Latch.open(latch)
  // => true
})
```

### `openUnsafe`

Synchronous `open`: opens the latch immediately and returns whether the state
changed.

```ts
import { Latch } from "effect"

const latch = Latch.makeUnsafe(false)
const changed = Latch.openUnsafe(latch)
// => true
```

### `release`

Wakes the fibers *currently* waiting without opening the latch — future waiters
still suspend. Succeeds with `true` if it ran while closed, `false` if already
open.

```ts
import { Effect, Latch } from "effect"

const program = Effect.gen(function*() {
  const latch = yield* Latch.make(false)
  const released = yield* Latch.release(latch)
  // => true (current waiters released; latch stays closed)
})
```

### `close`

Re-closes an open latch so future `await`/`whenOpen` calls suspend again.
Succeeds with `true` if it changed the state (open → closed), `false` otherwise.

```ts
import { Effect, Latch } from "effect"

const program = Effect.gen(function*() {
  const latch = yield* Latch.make(true)
  const changed = yield* Latch.close(latch)
  // => true
})
```

### `closeUnsafe`

Synchronous `close`: closes the latch immediately and returns whether the state
changed.

```ts
import { Latch } from "effect"

const latch = Latch.makeUnsafe(true)
const changed = Latch.closeUnsafe(latch)
// => true
```

### `whenOpen`

Waits on the latch, then runs the given effect, preserving its success, failure,
and requirements. If the latch is open it runs immediately.

```ts
import { Effect, Latch } from "effect"

const program = Effect.gen(function*() {
  const latch = yield* Latch.make(true)
  const result = yield* Latch.whenOpen(latch, Effect.succeed(42))
  // => 42
})
```

### `interface Latch`

The latch value exposes its operations directly: the `open`, `release`,
`await`, and `close` effect properties, the `openUnsafe()` / `closeUnsafe()`
synchronous methods, and `whenOpen(effect)`. Every operation also has a
standalone function form (`Latch.open`, `Latch.await`, `Latch.whenOpen`, …) —
use whichever style reads better.

```ts
import { Effect, Latch } from "effect"

const program = Effect.gen(function*() {
  const latch = yield* Latch.make(true)
  // Method form; `Latch.await(latch)` is the equivalent standalone function.
  yield* latch.await
  // => proceeds immediately (latch is open)
})
```

## PartitionedSemaphore reference

### `make`

Creates a `PartitionedSemaphore<K>` inside `Effect` with a shared `permits`
capacity, tracking waiters by key `K`.

```ts
import { Effect, PartitionedSemaphore } from "effect"

const program = Effect.gen(function*() {
  const sem = yield* PartitionedSemaphore.make<string>({ permits: 4 })
  // => PartitionedSemaphore keyed by string, capacity 4
})
```

### `makeUnsafe`

Synchronous construction. Negative `permits` are clamped to `0`; a non-finite
value makes an unbounded (no-op) semaphore.

```ts
import { PartitionedSemaphore } from "effect"

const sem = PartitionedSemaphore.makeUnsafe<number>({ permits: 4 })
// => PartitionedSemaphore keyed by number, capacity 4
```

### `available`

Reads a snapshot of how many permits are currently free (does not reserve any).

```ts
import { Effect, PartitionedSemaphore } from "effect"

const program = Effect.gen(function*() {
  const sem = yield* PartitionedSemaphore.make<string>({ permits: 4 })
  const free = yield* PartitionedSemaphore.available(sem)
  // => 4
})
```

### `capacity`

The fixed total capacity configured at construction. A plain number, not an
effect — it never changes.

```ts
import { PartitionedSemaphore } from "effect"

const sem = PartitionedSemaphore.makeUnsafe<string>({ permits: 4 })
const cap = PartitionedSemaphore.capacity(sem)
// => 4
```

### `take`

Manually acquires `permits` for `key`, waiting until released permits are
assigned to this partition. The effect succeeds with `void` once the permits are
held. Requests above `capacity` never complete; zero or negative requests
complete without acquiring.

```ts
import { Effect, PartitionedSemaphore } from "effect"

const program = Effect.gen(function*() {
  const sem = yield* PartitionedSemaphore.make<string>({ permits: 4 })
  yield* PartitionedSemaphore.take(sem, "tenant-a", 2)
  // => void (2 permits now held for "tenant-a")
})
```

### `release`

Returns `permits` to the shared pool and yields the resulting available count.
Released permits go first to waiting partitions (round-robin); leftovers raise
availability, capped at `capacity`.

```ts
import { Effect, PartitionedSemaphore } from "effect"

const program = Effect.gen(function*() {
  const sem = yield* PartitionedSemaphore.make<string>({ permits: 4 })
  yield* PartitionedSemaphore.take(sem, "tenant-a", 2)
  const available = yield* PartitionedSemaphore.release(sem, 2)
  // => 4
})
```

### `withPermits`

Acquires `permits` for `key` around an effect, releasing them when it exits
(success, failure, or interruption). The weighted, keyed equivalent of
`Semaphore.withPermits`.

```ts
import { Effect, PartitionedSemaphore } from "effect"

const program = Effect.gen(function*() {
  const sem = yield* PartitionedSemaphore.make<string>({ permits: 4 })
  yield* PartitionedSemaphore.withPermits(sem, "tenant-a", 2)(
    Effect.log("heavy work for tenant-a")
  )
  // => "heavy work for tenant-a", then 2 permits released
})
```

### `withPermit`

Single-permit form of `withPermits`, keyed by partition.

```ts
import { Effect, PartitionedSemaphore } from "effect"

const program = Effect.gen(function*() {
  const sem = yield* PartitionedSemaphore.make<string>({ permits: 4 })
  yield* PartitionedSemaphore.withPermit(sem, "tenant-b")(
    Effect.log("one unit of work for tenant-b")
  )
  // => "one unit of work for tenant-b"
})
```

### `withPermitsIfAvailable`

Runs the effect only if `permits` can be taken from the shared pool *immediately*
— note this variant takes **no key**, since it never waits. Returns
`Option.some` if it ran, `Option.none()` otherwise.

```ts
import { Effect, Option, PartitionedSemaphore } from "effect"

const program = Effect.gen(function*() {
  const sem = yield* PartitionedSemaphore.make<string>({ permits: 1 })
  yield* PartitionedSemaphore.take(sem, "tenant-a", 1) // drain the pool

  const result = yield* PartitionedSemaphore.withPermitsIfAvailable(sem, 1)(
    Effect.succeed("ran")
  )
  // => Option.none()

  return Option.isNone(result)
})
```

### `interface PartitionedSemaphore` and `Partitioned`

`PartitionedSemaphore<K>` is the model; its members mirror the functions above
(`capacity`, the `available` effect, `take(key, permits)`, `release(permits)`,
`withPermits(key, permits)`, `withPermit(key)`, `withPermitsIfAvailable(permits)`),
plus the `PartitionedTypeId` brand. `Partitioned<K>` is an alias interface with
no extra members — an alternate exported name for the same shape.

```ts
import { Effect, PartitionedSemaphore } from "effect"

const program = Effect.gen(function*() {
  const sem = yield* PartitionedSemaphore.make<string>({ permits: 4 })
  // Method form, equivalent to PartitionedSemaphore.withPermit(sem, "k").
  yield* sem.withPermit("tenant-a")(Effect.log("via method"))
  // => "via method"
})
```

### `PartitionedTypeId`

The runtime brand (`"~effect/PartitionedSemaphore"`) stamped onto partitioned
semaphore values; `PartitionedTypeId` is also exported as a type for fields that
must hold that exact marker.

```ts
import { PartitionedSemaphore } from "effect"

const sem = PartitionedSemaphore.makeUnsafe<string>({ permits: 1 })
const brand = sem[PartitionedSemaphore.PartitionedTypeId]
// => "~effect/PartitionedSemaphore"
```