# Fiber Collections

`Effect.forkChild` is perfect for a fixed handful of fibers, but real services
spawn fibers *dynamically* — one per WebSocket connection, one per scheduled job,
one per in-flight subscription. You need somewhere to keep those handles so they
can be interrupted when the owning [scope](https://effect.plants.sh/resource-management/scope-and-finalizers/)
closes, and so a fiber is forgotten once it finishes. That's what the fiber
collections do:

- **`FiberHandle`** — holds **at most one** fiber. Starting a new one interrupts
  the previous. Great for "latest wins" work like a debounced refresh.
- **`FiberMap`** — holds fibers **keyed** by some value. Ideal for one fiber per
  entity (connection id, user id, job id).
- **`FiberSet`** — holds an **unkeyed set** of fibers. Use it for a pool of
  workers you start and forget.

All three are created within a `Scope`. When that scope closes, *every* fiber in
the collection is interrupted automatically — so they fit naturally inside a
service's layer, and you never leak a background fiber.

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

// A service that runs one background subscription fiber per topic.
export class Subscriptions extends Context.Service<Subscriptions, {
  subscribe(topic: string): Effect.Effect<void>
  unsubscribe(topic: string): Effect.Effect<void>
}>()("app/Subscriptions") {
  static readonly layer = Layer.effect(
    Subscriptions,
    Effect.gen(function*() {
      // The FiberMap is tied to this layer's Scope: when the layer is
      // released, every subscription fiber is interrupted for us.
      const fibers = yield* FiberMap.make<string>()

      const subscribe = Effect.fn("Subscriptions.subscribe")(
        function*(topic: string) {
          // `run` forks the effect and stores it under `topic`. If a fiber
          // already exists for that key it is interrupted and replaced.
          yield* FiberMap.run(
            fibers,
            topic,
            Effect.forever(
              Effect.log(`polling ${topic}`).pipe(Effect.delay("1 second"))
            )
          )
        }
      )

      const unsubscribe = Effect.fn("Subscriptions.unsubscribe")(
        function*(topic: string) {
          // Interrupt and drop just this key's fiber.
          yield* FiberMap.remove(fibers, topic)
        }
      )

      return Subscriptions.of({ subscribe, unsubscribe })
    })
  )
}
```

## Why not a plain array of fibers?

You *could* keep fibers in a `Set` yourself, but you'd have to remove each one
when it completes, interrupt every survivor on shutdown, and handle the race
between a fiber finishing and you removing it. The fiber collections do all of
this correctly:

- a fiber is **removed automatically** when it terminates, so the collection
  never holds dead handles;
- closing the owning scope **interrupts all** remaining fibers;
- failures can be **propagated** to the collection so one bad fiber can surface
  an error instead of failing silently.

## `FiberHandle` — at most one fiber

A `FiberHandle` stores a single fiber. `FiberHandle.run` forks an effect into it;
if it already holds a running fiber, that one is interrupted first. This is the
"only the latest matters" pattern — re-running a search as the user types, or
restarting a watcher when config changes.

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

const program = Effect.scoped(
  Effect.gen(function*() {
    const handle = yield* FiberHandle.make<string>()

    // Start a task...
    yield* FiberHandle.run(handle, Effect.succeed("v1").pipe(Effect.delay("1 second")))

    // ...then start a newer one. The previous fiber is interrupted.
    yield* FiberHandle.run(handle, Effect.succeed("v2"))

    // `join` waits for the current fiber and re-raises its failure, if any.
    yield* FiberHandle.join(handle)
  })
)
```

Pass `{ onlyIfMissing: true }` to `run` if you'd rather keep the existing fiber
and skip starting a new one when one is already present.

## `FiberMap` — one fiber per key

A `FiberMap<K>` indexes fibers by a key. `FiberMap.run(map, key, effect)` forks
under that key (replacing any existing fiber for it), `FiberMap.remove(map, key)`
interrupts and drops one, and `FiberMap.has` / `FiberMap.size` let you inspect
the collection. This is the workhorse for "one background fiber per entity" — see
the `Subscriptions` service above.

## `FiberSet` — an unkeyed pool

A `FiberSet` is the simplest of the three: an unkeyed bag of fibers. Use it to
fork a fluctuating number of tasks and have them all cleaned up together.

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

const program = Effect.scoped(
  Effect.gen(function*() {
    const set = yield* FiberSet.make<void>()

    // Fork some work into the set; each fiber removes itself when it finishes.
    yield* FiberSet.run(set, Effect.log("job a").pipe(Effect.delay("100 millis")))
    yield* FiberSet.run(set, Effect.log("job b").pipe(Effect.delay("200 millis")))

    // Wait until every fiber in the set has completed.
    yield* FiberSet.awaitEmpty(set)
  })
)
// Leaving the scope would interrupt any fibers still running.
```

## Shared patterns

All three modules speak the same vocabulary, so once you learn one the others
follow:

- **Scoped construction.** `make` is an `Effect` that requires a `Scope`. It
  acquires the collection with `acquireRelease`, and its finalizer interrupts
  every fiber still inside when the scope closes. Run it inside `Effect.scoped`,
  a `Layer`, or any other scope owner.
- **Forking helpers.** `run` forks an effect into the collection inheriting the
  *current* fiber's services. `runtime` and `runtimePromise` instead **capture**
  the surrounding services once and hand back a plain (non-`Effect`) function you
  can call from callbacks, event handlers, or `Promise`-based glue. The
  `makeRuntime*` constructors fuse `make` + the captured runner in one step.
- **Tracking & replacement.** `set`/`add` store an *already-forked* fiber.
  `FiberHandle.set` replaces (and interrupts) the held fiber; `FiberMap.set`
  replaces the fiber for that key; `FiberSet.add` simply tracks one more fiber.
- **Self-removal.** A tracked fiber removes itself from the collection when it
  completes, so the collection never accumulates dead handles.
- **`join` vs `awaitEmpty`.** `join` waits for the collection's first
  *non-ignored failure* (interrupts are ignored by default; opt in with
  `propagateInterruption: true`). `awaitEmpty` waits until all currently tracked
  fibers have finished, regardless of outcome.
- **`Unsafe` variants.** `setUnsafe` / `addUnsafe` / `getUnsafe` / `hasUnsafe`
  mutate or read synchronously, outside of `Effect`. Use them only when you
  already control the surrounding execution context.
**Tip:** `FiberMap` and `FiberSet` are `Iterable` — you can spread or `for...of` over
  their live fibers (entries `[key, fiber]` for a map, bare fibers for a set). A
  closed collection iterates as empty.

---

## Choosing between them

| Need | Use |
| --- | --- |
| Only the most recent task should run | `FiberHandle` |
| One fiber per id / entity, addressable later | `FiberMap` |
| A pool of interchangeable background tasks | `FiberSet` |
| A fixed, known number of fibers in a `gen` block | plain [`forkChild`](https://effect.plants.sh/concurrency/fibers/) + `Fiber.join` |
| Map a collection with a parallelism cap | the [`concurrency` option](https://effect.plants.sh/concurrency/concurrency-options/) |
**Note:** Each collection is a [scoped resource](https://effect.plants.sh/resource-management/scope-and-finalizers/):
  `make` requires a `Scope`, and the surrounding scope's closure is what
  guarantees no fiber outlives the collection. Place them in a `Layer` for a
  service or wrap a `gen` block in `Effect.scoped` for ad-hoc use.

---

## FiberHandle reference

A `FiberHandle<A, E>` holds at most one `Fiber<A, E>`. Installing a new fiber
interrupts the previous one (unless `onlyIfMissing` is set), and a completed
fiber removes itself so the handle can be reused.

### make

Creates a scoped `FiberHandle`; the held fiber is interrupted when the scope
closes.

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

Effect.gen(function*() {
  const handle = yield* FiberHandle.make<string>()
  yield* FiberHandle.run(handle, Effect.succeed("hi"))
}).pipe(Effect.scoped)
```

### makeRuntime

Creates a scoped handle and immediately returns a captured run function that
forks effects into it, returning the forked `Fiber`.

```ts
import { Effect, Fiber, FiberHandle } from "effect"

Effect.gen(function*() {
  const run = yield* FiberHandle.makeRuntime<never>()

  const fiberA = run(Effect.succeed("first"))
  const fiberB = run(Effect.succeed("second")) // interrupts fiberA

  yield* Fiber.await(fiberB) // => Exit.succeed("second")
}).pipe(Effect.scoped)
```

### makeRuntimePromise

Like `makeRuntime`, but the returned runner resolves a `Promise` with the
effect's success value (or rejects with the squashed cause).

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

Effect.gen(function*() {
  const run = yield* FiberHandle.makeRuntimePromise()

  const result = yield* Effect.promise(() => run(Effect.succeed("hello")))
  console.log(result) // => "hello"
}).pipe(Effect.scoped)
```

### set

Stores an already-forked fiber in the handle, interrupting the previously held
fiber unless `onlyIfMissing` is set. Returns an `Effect`.

```ts
import { Effect, Fiber, FiberHandle } from "effect"

Effect.gen(function*() {
  const handle = yield* FiberHandle.make<string>()
  const fiber = Effect.runFork(Effect.succeed("hello"))

  yield* FiberHandle.set(handle, fiber)
  console.log(yield* Fiber.await(fiber)) // => Exit.succeed("hello")
}).pipe(Effect.scoped)
```

### setUnsafe

Synchronous, non-`Effect` version of `set`. Use when you already control the
execution context.

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

Effect.gen(function*() {
  const handle = yield* FiberHandle.make<string>()
  const fiber = Effect.runFork(Effect.succeed("hello"))

  FiberHandle.setUnsafe(handle, fiber) // mutates synchronously, returns void
}).pipe(Effect.scoped)
```

### get

Returns the current fiber as an `Option`, wrapped in `Effect`.

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

Effect.gen(function*() {
  const handle = yield* FiberHandle.make<string>()
  yield* FiberHandle.run(handle, Effect.never)

  const current = yield* FiberHandle.get(handle)
  console.log(current._tag) // => "Some"
}).pipe(Effect.scoped)
```

### getUnsafe

Synchronous version of `get`: returns `Option<Fiber<A, E>>` directly.

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

Effect.gen(function*() {
  const handle = yield* FiberHandle.make<string>()

  console.log(FiberHandle.getUnsafe(handle)._tag) // => "None"
}).pipe(Effect.scoped)
```

### clear

Interrupts the currently held fiber (if any) and leaves the handle empty and
reusable.

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

Effect.gen(function*() {
  const handle = yield* FiberHandle.make()
  yield* FiberHandle.run(handle, Effect.never)

  yield* FiberHandle.clear(handle)
  console.log(FiberHandle.getUnsafe(handle)._tag) // => "None"
}).pipe(Effect.scoped)
```

### run

Forks an effect into the handle (inheriting the current fiber's services) and
returns the forked `Fiber`. Running a new effect interrupts the previous fiber
unless `onlyIfMissing` is set.

```ts
import { Effect, Fiber, FiberHandle } from "effect"

Effect.gen(function*() {
  const handle = yield* FiberHandle.make<string>()

  const f1 = yield* FiberHandle.run(handle, Effect.succeed("hello"))
  console.log(yield* Fiber.await(f1)) // => Exit.succeed("hello")

  // Skip if a fiber is already present:
  yield* FiberHandle.run(handle, Effect.succeed("world"), { onlyIfMissing: true })
}).pipe(Effect.scoped)
```

### runtime

Captures the surrounding services and returns a plain function for forking
effects into an existing handle. Handy when forking from callbacks.

```ts
import { Context, Effect, FiberHandle } from "effect"

interface Users {}
const Users = Context.Service<Users, {
  readonly getAll: Effect.Effect<ReadonlyArray<unknown>>
}>("Users")

Effect.gen(function*() {
  const handle = yield* FiberHandle.make()
  const run = yield* FiberHandle.runtime(handle)<Users>()

  run(Effect.flatMap(Users, (_) => _.getAll))
  run(Effect.flatMap(Users, (_) => _.getAll)) // interrupts the previous fiber
}).pipe(Effect.scoped)
```

### runtimePromise

Captures the runtime and returns a runner that yields a `Promise` per effect.

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

Effect.gen(function*() {
  const handle = yield* FiberHandle.make()
  const run = yield* FiberHandle.runtimePromise(handle)<never>()

  const result = yield* Effect.promise(() => run(Effect.succeed("hello")))
  console.log(result) // => "hello"
}).pipe(Effect.scoped)
```

### join

Waits for the handle to fail or close, re-raising the first non-ignored failure.
Successful completion of the held fiber only empties the handle.

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

Effect.gen(function*() {
  const handle = yield* FiberHandle.make()
  yield* FiberHandle.set(handle, Effect.runFork(Effect.fail("boom")))

  yield* FiberHandle.join(handle) // => fails with "boom"
}).pipe(Effect.scoped)
```

### awaitEmpty

Waits for the fiber that is current when the call starts to complete (success or
failure), then resolves.

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

Effect.gen(function*() {
  const handle = yield* FiberHandle.make()
  yield* FiberHandle.run(handle, Effect.sleep("100 millis"))

  yield* FiberHandle.awaitEmpty(handle)
  console.log("done") // => "done"
}).pipe(Effect.scoped)
```

### isFiberHandle

Type guard that returns `true` for `FiberHandle` values.

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

Effect.gen(function*() {
  const handle = yield* FiberHandle.make()
  console.log(FiberHandle.isFiberHandle(handle)) // => true
  console.log(FiberHandle.isFiberHandle("nope")) // => false
}).pipe(Effect.scoped)
```

### interface FiberHandle

`FiberHandle<out A = unknown, out E = unknown>` is `Pipeable` and `Inspectable`.
Its `state` is either `{ _tag: "Open", fiber: Fiber<A, E> | undefined }` or
`{ _tag: "Closed" }`; a `deferred` field backs `join`. Type parameters constrain
what fibers the handle accepts: `A` is the success type, `E` the error type.

---

## FiberMap reference

A `FiberMap<K, A, E>` is a keyed registry of fibers. Adding under an existing key
interrupts the previous fiber for that key (unless `onlyIfMissing`), completed
fibers remove themselves, and closing the scope interrupts everything.

### make

Creates a scoped `FiberMap`; all entries are interrupted when the scope closes.

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

Effect.gen(function*() {
  const map = yield* FiberMap.make<string>()
  yield* FiberMap.run(map, "a", Effect.never)
}).pipe(Effect.scoped)
```

### makeRuntime

Creates a scoped map and returns a captured `(key, effect, options?) => Fiber`
runner in one step.

```ts
import { Effect, Fiber, FiberMap } from "effect"

Effect.gen(function*() {
  const run = yield* FiberMap.makeRuntime<never, string>()

  const f1 = run("task1", Effect.succeed("Hello"))
  const f2 = run("task2", Effect.succeed("World"))

  console.log(yield* Fiber.join(f1), yield* Fiber.join(f2)) // => "Hello" "World"
}).pipe(Effect.scoped)
```

### makeRuntimePromise

Like `makeRuntime`, but each call returns a `Promise`.

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

Effect.gen(function*() {
  const run = yield* FiberMap.makeRuntimePromise<never, string>()

  const result = yield* Effect.promise(() => run("task1", Effect.succeed("Hello")))
  console.log(result) // => "Hello"
}).pipe(Effect.scoped)
```

### set

Stores an already-forked fiber under a key, interrupting the previous fiber for
that key unless `onlyIfMissing` is set.

```ts
import { Effect, Fiber, FiberMap } from "effect"

Effect.gen(function*() {
  const map = yield* FiberMap.make<string>()
  const fiber = yield* Effect.forkChild(Effect.succeed("Hello"))

  yield* FiberMap.set(map, "greeting", fiber)
  console.log(yield* Fiber.join(fiber)) // => "Hello"
}).pipe(Effect.scoped)
```

### setUnsafe

Synchronous, non-`Effect` version of `set`.

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

Effect.gen(function*() {
  const map = yield* FiberMap.make<string>()
  const fiber = Effect.runFork(Effect.succeed("Hello"))

  FiberMap.setUnsafe(map, "greeting", fiber) // returns void
}).pipe(Effect.scoped)
```

### get

Returns the fiber for a key as `Option`, wrapped in `Effect`.

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

Effect.gen(function*() {
  const map = yield* FiberMap.make<string>()
  yield* FiberMap.run(map, "a", Effect.never)

  const fiber = yield* FiberMap.get(map, "a")
  console.log(fiber._tag) // => "Some"
}).pipe(Effect.scoped)
```

### getUnsafe

Synchronous version of `get`.

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

Effect.gen(function*() {
  const map = yield* FiberMap.make<string>()
  console.log(FiberMap.getUnsafe(map, "missing")._tag) // => "None"
}).pipe(Effect.scoped)
```

### has

Returns whether a key currently has a fiber, wrapped in `Effect`.

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

Effect.gen(function*() {
  const map = yield* FiberMap.make<string>()
  yield* FiberMap.run(map, "task1", Effect.never)

  console.log(yield* FiberMap.has(map, "task1")) // => true
  console.log(yield* FiberMap.has(map, "task2")) // => false
}).pipe(Effect.scoped)
```

### hasUnsafe

Synchronous version of `has`.

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

Effect.gen(function*() {
  const map = yield* FiberMap.make<string>()
  yield* FiberMap.run(map, "task1", Effect.never)

  console.log(FiberMap.hasUnsafe(map, "task1")) // => true
}).pipe(Effect.scoped)
```

### remove

Interrupts and removes the fiber for a single key.

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

Effect.gen(function*() {
  const map = yield* FiberMap.make<string>()
  yield* FiberMap.run(map, "task1", Effect.never)
  yield* FiberMap.run(map, "task2", Effect.never)

  yield* FiberMap.remove(map, "task1")
  console.log(yield* FiberMap.size(map)) // => 1
}).pipe(Effect.scoped)
```

### clear

Interrupts every fiber in the map but keeps the map open and reusable.

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

Effect.gen(function*() {
  const map = yield* FiberMap.make<string>()
  yield* FiberMap.run(map, "a", Effect.never)
  yield* FiberMap.run(map, "b", Effect.never)

  yield* FiberMap.clear(map)
  console.log(yield* FiberMap.size(map)) // => 0
}).pipe(Effect.scoped)
```

### run

Forks an effect into the map under a key (inheriting current services) and
returns the forked `Fiber`, replacing any existing fiber for that key unless
`onlyIfMissing` is set.

```ts
import { Effect, Fiber, FiberMap } from "effect"

Effect.gen(function*() {
  const map = yield* FiberMap.make<string>()

  const f1 = yield* FiberMap.run(map, "task1", Effect.succeed("Hello"))
  console.log(yield* Fiber.join(f1)) // => "Hello"
  console.log(yield* FiberMap.size(map)) // => 0 (removed on completion)
}).pipe(Effect.scoped)
```

### runtime

Captures the surrounding services and returns a plain
`(key, effect, options?) => Fiber` runner for an existing map.

```ts
import { Context, Effect, FiberMap } from "effect"

interface Users {}
const Users = Context.Service<Users, {
  readonly getAll: Effect.Effect<ReadonlyArray<unknown>>
}>("Users")

Effect.gen(function*() {
  const map = yield* FiberMap.make<string>()
  const run = yield* FiberMap.runtime(map)<Users>()

  run("a", Effect.flatMap(Users, (_) => _.getAll))
  run("b", Effect.flatMap(Users, (_) => _.getAll))
}).pipe(Effect.scoped)
```

### runtimePromise

Captures the runtime and returns a runner that yields a `Promise` per effect.

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

Effect.gen(function*() {
  const map = yield* FiberMap.make<string>()
  const run = yield* FiberMap.runtimePromise(map)<never>()

  const result = yield* Effect.promise(() => run("task1", Effect.succeed("Hello")))
  console.log(result) // => "Hello"
}).pipe(Effect.scoped)
```

### size

Returns the number of fibers currently in the map.

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

Effect.gen(function*() {
  const map = yield* FiberMap.make<string>()
  yield* FiberMap.run(map, "a", Effect.never)
  yield* FiberMap.run(map, "b", Effect.never)

  console.log(yield* FiberMap.size(map)) // => 2
}).pipe(Effect.scoped)
```

### join

Waits for the map to fail or close, re-raising the first non-ignored failure.

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

Effect.gen(function*() {
  const map = yield* FiberMap.make()
  yield* FiberMap.set(map, "a", Effect.runFork(Effect.fail("boom")))

  yield* FiberMap.join(map) // => fails with "boom"
}).pipe(Effect.scoped)
```

### awaitEmpty

Waits until the map has no fibers — i.e. all currently tracked fibers have
completed.

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

Effect.gen(function*() {
  const map = yield* FiberMap.make<string>()
  yield* FiberMap.run(map, "a", Effect.sleep("100 millis"))
  yield* FiberMap.run(map, "b", Effect.sleep("200 millis"))

  yield* FiberMap.awaitEmpty(map)
  console.log(yield* FiberMap.size(map)) // => 0
}).pipe(Effect.scoped)
```

### isFiberMap

Type guard that returns `true` for `FiberMap` values.

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

Effect.gen(function*() {
  const map = yield* FiberMap.make<string>()
  console.log(FiberMap.isFiberMap(map)) // => true
  console.log(FiberMap.isFiberMap({})) // => false
}).pipe(Effect.scoped)
```

### interface FiberMap

`FiberMap<in out K, out A = unknown, out E = unknown>` is `Pipeable`,
`Inspectable`, and `Iterable<[K, Fiber<A, E>]>`. Its `state` is either
`{ _tag: "Open", backing: MutableHashMap<K, Fiber<A, E>> }` or
`{ _tag: "Closed" }`. `K` is the key type, `A`/`E` the fibers' success/error
types.

---

## FiberSet reference

A `FiberSet<A, E>` tracks an unkeyed set of fibers. Completed fibers are removed
automatically, and closing the scope (or calling `clear`) interrupts the rest.

### make

Creates a scoped `FiberSet`; all members are interrupted when the scope closes.

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

Effect.gen(function*() {
  const set = yield* FiberSet.make()
  yield* FiberSet.run(set, Effect.never)
}).pipe(Effect.scoped)
```

### makeRuntime

Creates a scoped set and returns a captured `(effect, options?) => Fiber` runner.

```ts
import { Effect, Fiber, FiberSet } from "effect"

Effect.gen(function*() {
  const run = yield* FiberSet.makeRuntime()

  const fiber = run(Effect.succeed("hello"))
  console.log(yield* Fiber.await(fiber)) // => Exit.succeed("hello")
}).pipe(Effect.scoped)
```

### makeRuntimePromise

Like `makeRuntime`, but each call returns a `Promise`.

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

Effect.gen(function*() {
  const run = yield* FiberSet.makeRuntimePromise()

  const result = yield* Effect.promise(() => run(Effect.succeed("hello")))
  console.log(result) // => "hello"
}).pipe(Effect.scoped)
```

### add

Adds an already-forked fiber to the set; it removes itself on completion.

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

Effect.gen(function*() {
  const set = yield* FiberSet.make()
  const fiber = yield* Effect.forkChild(Effect.succeed("hello"))

  yield* FiberSet.add(set, fiber)
  console.log(yield* FiberSet.size(set)) // => 1
}).pipe(Effect.scoped)
```

### addUnsafe

Synchronous, non-`Effect` version of `add`.

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

Effect.gen(function*() {
  const set = yield* FiberSet.make()
  const fiber = Effect.runFork(Effect.never)

  FiberSet.addUnsafe(set, fiber) // returns void
  console.log(yield* FiberSet.size(set)) // => 1
}).pipe(Effect.scoped)
```

### clear

Interrupts every fiber in the set but keeps the set open and reusable.

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

Effect.gen(function*() {
  const set = yield* FiberSet.make()
  yield* FiberSet.run(set, Effect.never)
  yield* FiberSet.run(set, Effect.never)

  yield* FiberSet.clear(set)
  console.log(yield* FiberSet.size(set)) // => 0
}).pipe(Effect.scoped)
```

### run

Forks an effect into the set (inheriting current services) and returns the
forked `Fiber`.

```ts
import { Effect, Fiber, FiberSet } from "effect"

Effect.gen(function*() {
  const set = yield* FiberSet.make()

  const fiber = yield* FiberSet.run(set, Effect.succeed("hello"))
  console.log(yield* Fiber.await(fiber)) // => Exit.succeed("hello")
}).pipe(Effect.scoped)
```

### runtime

Captures the surrounding services and returns a plain `(effect, options?) => Fiber`
runner for an existing set.

```ts
import { Context, Effect, FiberSet } from "effect"

interface Users {}
const Users = Context.Service<Users, {
  readonly getAll: Effect.Effect<ReadonlyArray<unknown>>
}>("Users")

Effect.gen(function*() {
  const set = yield* FiberSet.make()
  const run = yield* FiberSet.runtime(set)<Users>()

  run(Effect.flatMap(Users, (_) => _.getAll))
}).pipe(Effect.scoped)
```

### runtimePromise

Captures the runtime and returns a runner that yields a `Promise` per effect.

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

Effect.gen(function*() {
  const set = yield* FiberSet.make()
  const run = yield* FiberSet.runtimePromise(set)()

  const result = yield* Effect.promise(() => run(Effect.succeed("hello")))
  console.log(result) // => "hello"
}).pipe(Effect.scoped)
```

### size

Returns the number of fibers currently in the set.

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

Effect.gen(function*() {
  const set = yield* FiberSet.make()
  yield* FiberSet.run(set, Effect.never)
  yield* FiberSet.run(set, Effect.never)

  console.log(yield* FiberSet.size(set)) // => 2
}).pipe(Effect.scoped)
```

### join

Waits for the set to fail, re-raising the first non-ignored failure.

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

Effect.gen(function*() {
  const set = yield* FiberSet.make()
  yield* FiberSet.add(set, Effect.runFork(Effect.fail("boom")))

  yield* FiberSet.join(set) // => fails with "boom"
}).pipe(Effect.scoped)
```

### awaitEmpty

Waits until the set is empty — all currently tracked fibers have completed.

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

Effect.gen(function*() {
  const set = yield* FiberSet.make()
  yield* FiberSet.run(set, Effect.sleep("100 millis"))
  yield* FiberSet.run(set, Effect.sleep("200 millis"))

  yield* FiberSet.awaitEmpty(set)
  console.log(yield* FiberSet.size(set)) // => 0
}).pipe(Effect.scoped)
```

### isFiberSet

Type guard that returns `true` for `FiberSet` values.

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

Effect.gen(function*() {
  const set = yield* FiberSet.make()
  console.log(FiberSet.isFiberSet(set)) // => true
  console.log(FiberSet.isFiberSet({})) // => false
}).pipe(Effect.scoped)
```

### interface FiberSet

`FiberSet<out A = unknown, out E = unknown>` is `Pipeable`, `Inspectable`, and
`Iterable<Fiber<A, E>>`. Its `state` is either
`{ _tag: "Open", backing: Set<Fiber<A, E>> }` or `{ _tag: "Closed" }`. `A`/`E`
constrain the success/error types of the fibers it accepts.