1/**
2 * Operations for handles returned by forking effects. A `Fiber<A, E>` is a
3 * lightweight execution of an `Effect` that may still be running or may already
4 * have completed. This module lets callers await or join fiber results,
5 * interrupt one or many fibers, check unknown values, access the current fiber,
6 * and attach manually managed fibers to a `Scope` for cleanup.
7 *
8 * @since 2.0.0
9 */
10import type * as Arr from "./Array.ts"
11import type * as Context from "./Context.ts"
12import type { Effect } from "./Effect.ts"
13import type { Exit } from "./Exit.ts"
14import * as effect from "./internal/effect.ts"
15import type { LogLevel } from "./LogLevel.ts"
16import type { FiberRuntimeMetricsService } from "./Metric.ts"
17import type { Pipeable } from "./Pipeable.ts"
18import { hasProperty } from "./Predicate.ts"
19import type { StackFrame } from "./References.ts"
20import type { Scheduler, SchedulerDispatcher } from "./Scheduler.ts"
21import type { Scope } from "./Scope.ts"
22import type { AnySpan, Tracer } from "./Tracer.ts"
23import type { Covariant } from "./Types.ts"
24
25const TypeId = "~effect/Fiber"
26
27/**
28 * A runtime fiber is a lightweight thread that executes Effects. Fibers are
29 * the unit of concurrency in Effect. They provide a way to run multiple
30 * Effects concurrently while maintaining structured concurrency and
31 * cancellation safety.
32 *
33 * **When to use**
34 *
35 * Use to observe, join, interrupt, or coordinate work that has already been
36 * forked.
37 *
38 * **Details**
39 *
40 * A fiber exposes both safe Effect-based operations, such as {@link await_ await},
41 * {@link join}, and {@link interrupt}, and low-level runtime fields used by
42 * the scheduler and runtime internals.
43 *
44 * **Gotchas**
45 *
46 * Prefer the exported functions in this module over calling `interruptUnsafe`
47 * or `pollUnsafe` directly. The unsafe methods are immediate runtime hooks and
48 * do not provide the same Effect-based sequencing guarantees.
49 *
50 * **Example** (Awaiting a forked fiber)
51 *
52 * ```ts import.meta.vitest
53 * import { Effect, Exit, Fiber } from "effect"
54 *
55 * const program = Effect.gen(function*() {
56 * // Fork an effect to run in a new fiber
57 * const fiber = yield* Effect.forkChild(Effect.succeed(42))
58 *
59 * // Wait for the fiber to complete and get its result
60 * const result = yield* Fiber.await(fiber)
61 * return result
62 * })
63 *
64 * const actual = await Effect.runPromise(program)
65 * actual // => Exit.succeed(42)
66 * ```
67 *
68 * @category models
69 * @since 2.0.0
70 */
71export interface Fiber<out A, out E = never> extends Pipeable {
72 readonly [TypeId]: Fiber.Variance<A, E>
73
74 readonly id: number
75 readonly currentOpCount: number
76 readonly getRef: <A>(ref: Context.Reference<A>) => A
77 readonly context: Context.Context<never>
78 setContext(context: Context.Context<never>): void
79 readonly cache: Fiber.Cache
80 readonly currentDispatcher: SchedulerDispatcher
81 readonly addObserver: (cb: (exit: Exit<A, E>) => void) => () => void
82 readonly interruptUnsafe: (
83 fiberId?: number | undefined,
84 annotations?: Context.Context<never> | undefined
85 ) => void
86 readonly pollUnsafe: () => Exit<A, E> | undefined
87}
88
89/**
90 * The Fiber namespace contains utility types and functions for working with fibers.
91 * It provides type-level utilities for fiber operations and variance encoding.
92 *
93 * **When to use**
94 *
95 * Use to reference type-level helpers associated with `Fiber`.
96 *
97 * **Details**
98 *
99 * The namespace currently exposes type-level support used by the `Fiber`
100 * interface. Runtime operations are exported as module-level functions.
101 *
102 * **Example** (Working with fiber types)
103 *
104 * ```ts import.meta.vitest
105 * import { Effect, Fiber } from "effect"
106 *
107 * const program = Effect.gen(function*() {
108 * // Create a fiber
109 * const fiber = yield* Effect.forkChild(Effect.succeed(42))
110 *
111 * // Use namespace types for variance
112 * const typedFiber: Fiber.Fiber<number, never> = fiber
113 *
114 * // Join the fiber
115 * return yield* Fiber.join(fiber)
116 * })
117 *
118 * const actual = await Effect.runPromise(program)
119 * actual // => 42
120 * ```
121 *
122 * @since 2.0.0
123 */
124export declare namespace Fiber {
125 /**
126 * Variance encoding for the Fiber type, specifying covariance in both the
127 * success type `A` and the error type `E`.
128 *
129 * **When to use**
130 *
131 * Use to carry the success and error type parameters for `Fiber` in Effect's
132 * type machinery.
133 *
134 * **Example** (Upcasting fibers safely)
135 *
136 * ```ts import.meta.vitest
137 * import { Effect, Fiber } from "effect"
138 *
139 * // Variance allows safe subtyping
140 * const fiber: Fiber.Fiber<number, never> = Effect.runFork(Effect.succeed(1))
141 * const upcast: Fiber.Fiber<unknown, unknown> = fiber
142 * const actual = await Effect.runPromise(Fiber.join(upcast))
143 * actual // => 1
144 * ```
145 *
146 * @category models
147 * @since 2.0.0
148 */
149 export interface Variance<out A, out E = never> {
150 readonly _A: Covariant<A>
151 readonly _E: Covariant<E>
152 }
153
154 /**
155 * Context-derived values cached for the fiber's current `Context`.
156 *
157 * **When to use**
158 *
159 * Use to read runtime services resolved from the fiber's context, such as
160 * the scheduler, current span, or log levels.
161 *
162 * **Details**
163 *
164 * The cache object is computed once per context cache root and shared by
165 * every fiber running with that root, so it must be treated as immutable.
166 *
167 * @category models
168 * @since 4.0.0
169 */
170 export interface Cache {
171 readonly scheduler: Scheduler
172 readonly tracer: Tracer | undefined
173 readonly tracerContext: Tracer["context"] | undefined
174 readonly tracerEnabled: boolean
175 readonly span: AnySpan | undefined
176 readonly logLevel: LogLevel
177 readonly minimumLogLevel: LogLevel
178 readonly stackFrame: StackFrame | undefined
179 readonly runtimeMetrics: FiberRuntimeMetricsService | undefined
180 readonly maxOpsBeforeYield: number
181 readonly preventYield: boolean
182 }
183}
184
185const await_: <A, E>(self: Fiber<A, E>) => Effect<Exit<A, E>> = effect.fiberAwait
186export {
187 /**
188 * Waits for a fiber to complete and returns its exit value.
189 *
190 * **When to use**
191 *
192 * Use when you need to inspect whether the fiber succeeded,
193 * failed, died, or was interrupted without propagating the failure.
194 *
195 * **Details**
196 *
197 * The returned Effect always succeeds with an `Exit` describing the fiber's
198 * outcome.
199 *
200 * **Gotchas**
201 *
202 * This does not flatten the fiber result into the current Effect. Use
203 * {@link join} when you want fiber failures to fail the current Effect.
204 *
205 * **Example** (Awaiting a fiber exit)
206 *
207 * ```ts import.meta.vitest
208 * import { Effect, Exit, Fiber } from "effect"
209 *
210 * const program = Effect.gen(function*() {
211 * const fiber = yield* Effect.forkChild(Effect.succeed(42))
212 * return yield* Fiber.await(fiber)
213 * })
214 *
215 * const actual = await Effect.runPromise(program)
216 * actual // => Exit.succeed(42)
217 * ```
218 *
219 * @category combinators
220 * @since 2.0.0
221 */
222 await_ as await
223}
224/**
225 * Waits for all fibers in the provided iterable to complete and returns
226 * an array of their exit values.
227 *
228 * **When to use**
229 *
230 * Use when you need every fiber outcome as data, including failures and
231 * interruptions.
232 *
233 * **Details**
234 *
235 * The returned array is ordered like the input iterable.
236 *
237 * **Gotchas**
238 *
239 * Failures are captured as `Exit.Failure` values. Use {@link joinAll} when you
240 * want the first failed fiber to fail the returned Effect.
241 *
242 * **Example** (Awaiting multiple fiber exits)
243 *
244 * ```ts import.meta.vitest
245 * import { Effect, Exit, Fiber } from "effect"
246 *
247 * const program = Effect.gen(function*() {
248 * const fiber1 = yield* Effect.forkChild(Effect.succeed(1))
249 * const fiber2 = yield* Effect.forkChild(Effect.succeed(2))
250 * return yield* Fiber.awaitAll([fiber1, fiber2])
251 * })
252 *
253 * const actual = await Effect.runPromise(program)
254 * actual // => [Exit.succeed(1), Exit.succeed(2)]
255 * ```
256 *
257 * @category combinators
258 * @since 2.0.0
259 */
260export const awaitAll: <A extends Fiber<any, any>>(
261 self: Iterable<A>
262) => Effect<
263 Array<
264 Exit<
265 A extends Fiber<infer _A, infer _E> ? _A : never,
266 A extends Fiber<infer _A, infer _E> ? _E : never
267 >
268 >
269> = effect.fiberAwaitAll
270
271/**
272 * Joins a fiber, blocking until it completes. If the fiber succeeds,
273 * returns its value. If it fails, the error is propagated.
274 *
275 * **When to use**
276 *
277 * Use when you need a forked fiber's failure to fail the current Effect because
278 * that fiber is part of the current workflow.
279 *
280 * **Gotchas**
281 *
282 * Joining a failed fiber propagates the fiber's Cause. Use {@link await_ await} when
283 * you need to inspect the `Exit` instead of failing.
284 *
285 * **Example** (Joining a fiber)
286 *
287 * ```ts import.meta.vitest
288 * import { Effect, Fiber } from "effect"
289 *
290 * const program = Effect.gen(function*() {
291 * const fiber = yield* Effect.forkChild(Effect.succeed(42))
292 * return yield* Fiber.join(fiber)
293 * })
294 *
295 * const actual = await Effect.runPromise(program)
296 * actual // => 42
297 * ```
298 *
299 * @see {@link await_ await} for inspecting the fiber outcome as an Exit
300 *
301 * @category combinators
302 * @since 2.0.0
303 */
304export const join: <A, E>(self: Fiber<A, E>) => Effect<A, E> = effect.fiberJoin
305
306/**
307 * Waits for all fibers to succeed and returns their values in input order.
308 *
309 * **When to use**
310 *
311 * Use when you need every fiber to succeed and want the successful values
312 * rather than the `Exit` values.
313 *
314 * **Details**
315 *
316 * If any fiber fails, the returned `Effect` fails with that fiber's cause and
317 * stops waiting for additional results. This does not interrupt the remaining
318 * fibers.
319 *
320 * **Gotchas**
321 *
322 * A failure stops waiting, but it does not interrupt any other fibers. Use
323 * {@link interruptAll} separately when remaining fibers should be stopped.
324 *
325 * @see {@link awaitAll} for collecting every fiber outcome as an Exit
326 *
327 * @category combinators
328 * @since 2.0.0
329 */
330export const joinAll: <A extends Iterable<Fiber<any, any>>>(
331 self: A
332) => Effect<
333 Arr.ReadonlyArray.With<
334 A,
335 A extends Iterable<Fiber<infer _A, infer _E>> ? _A : never
336 >,
337 A extends Iterable<Fiber<infer _A, infer _E>> ? _E : never
338> = effect.fiberJoinAll
339
340/**
341 * Interrupts a fiber, causing it to stop executing and clean up any
342 * acquired resources.
343 *
344 * **When to use**
345 *
346 * Use when you need to cancel a forked fiber and wait for its cleanup to
347 * complete.
348 *
349 * **Details**
350 *
351 * The returned Effect completes only after the interrupted fiber has completed.
352 *
353 * **Gotchas**
354 *
355 * Interruption is cooperative. A fiber can continue running while it is inside
356 * uninterruptible work or finalizers.
357 *
358 * **Example** (Interrupting a fiber)
359 *
360 * ```ts import.meta.vitest
361 * import { Effect, Fiber } from "effect"
362 *
363 * const program = Effect.gen(function*() {
364 * const fiber = yield* Effect.forkChild(
365 * Effect.delay("1 second")(Effect.succeed(42))
366 * )
367 * yield* Fiber.interrupt(fiber)
368 * })
369 *
370 * await Effect.runPromise(program)
371 * ```
372 *
373 * @see {@link interruptAs} for specifying the interrupting fiber ID
374 * @see {@link await_ await} for observing the interrupted fiber's Exit
375 *
376 * @category interruption
377 * @since 2.0.0
378 */
379export const interrupt: <A, E>(self: Fiber<A, E>) => Effect<void> = effect.fiberInterrupt
380
381/**
382 * Interrupts a fiber with a specific fiber ID as the interruptor. This allows
383 * tracking which fiber initiated the interruption.
384 *
385 * **When to use**
386 *
387 * Use when runtime diagnostics or tracing should attribute the interruption to
388 * a specific fiber ID.
389 *
390 * **Details**
391 *
392 * The returned Effect completes only after the interrupted fiber has completed.
393 *
394 * **Gotchas**
395 *
396 * The supplied ID affects the recorded interruptor. It does not make
397 * interruption synchronous or force uninterruptible regions to stop early.
398 *
399 * **Example** (Interrupting a fiber as another fiber)
400 *
401 * ```ts import.meta.vitest
402 * import { Effect, Fiber } from "effect"
403 *
404 * const program = Effect.gen(function*() {
405 * const targetFiber = yield* Effect.forkChild(
406 * Effect.delay("5 seconds")(Effect.succeed("task completed"))
407 * )
408 *
409 * // Interrupt the fiber, specifying fiber ID 123 as the interruptor
410 * yield* Fiber.interruptAs(targetFiber, 123)
411 * })
412 *
413 * await Effect.runPromise(program)
414 * ```
415 *
416 * @see {@link interrupt} for using the current fiber as the interruptor
417 *
418 * @category interruption
419 * @since 2.0.0
420 */
421export const interruptAs: {
422 (
423 fiberId: number | undefined,
424 annotations?: Context.Context<never> | undefined
425 ): <A, E>(self: Fiber<A, E>) => Effect<void>
426 <A, E>(
427 self: Fiber<A, E>,
428 fiberId: number | undefined,
429 annotations?: Context.Context<never> | undefined
430 ): Effect<void>
431} = effect.fiberInterruptAs
432
433/**
434 * Interrupts all fibers in the provided iterable, causing them to stop executing
435 * and clean up any acquired resources.
436 *
437 * **When to use**
438 *
439 * Use when you need to cancel several forked fibers and wait for their cleanup
440 * to complete.
441 *
442 * **Details**
443 *
444 * The current fiber is recorded as the interruptor. The returned Effect
445 * completes only after all interrupted fibers have completed.
446 *
447 * **Gotchas**
448 *
449 * Interruption is cooperative for each fiber. The returned Effect can wait for
450 * uninterruptible work and finalizers in any interrupted fiber.
451 *
452 * **Example** (Interrupting multiple fibers)
453 *
454 * ```ts import.meta.vitest
455 * import { Effect, Fiber } from "effect"
456 *
457 * const program = Effect.gen(function*() {
458 * const fiber1 = yield* Effect.forkChild(Effect.never)
459 * const fiber2 = yield* Effect.forkChild(Effect.never)
460 * const fiber3 = yield* Effect.forkChild(Effect.never)
461 * yield* Fiber.interruptAll([fiber1, fiber2, fiber3])
462 * })
463 *
464 * await Effect.runPromise(program)
465 * ```
466 *
467 * @see {@link interruptAllAs} for specifying the interrupting fiber ID
468 *
469 * @category interruption
470 * @since 2.0.0
471 */
472export const interruptAll: <A extends Iterable<Fiber<any, any>>>(
473 fibers: A
474) => Effect<void> = effect.fiberInterruptAll
475
476/**
477 * Interrupts all fibers in the provided iterable using the specified fiber ID as the
478 * interrupting fiber. This allows you to control which fiber is considered the source
479 * of the interruption, which can be useful for debugging and tracing.
480 *
481 * **When to use**
482 *
483 * Use to interrupt several fibers while recording a specific fiber ID as the
484 * interruptor.
485 *
486 * **Details**
487 *
488 * The returned Effect completes only after all interrupted fibers have
489 * completed.
490 *
491 * **Gotchas**
492 *
493 * The supplied ID affects the recorded interruptor. It does not make
494 * interruption synchronous or force uninterruptible regions to stop early.
495 *
496 * **Example** (Interrupting multiple fibers as another fiber)
497 *
498 * ```ts import.meta.vitest
499 * import { Effect, Fiber } from "effect"
500 *
501 * const program = Effect.gen(function*() {
502 * // Create a controlling fiber
503 * const controllerFiber = yield* Effect.forkChild(Effect.succeed("controller"))
504 *
505 * const worker1 = yield* Effect.forkChild(Effect.never)
506 * const worker2 = yield* Effect.forkChild(Effect.never)
507 *
508 * yield* Fiber.interruptAllAs([worker1, worker2], controllerFiber.id)
509 * })
510 *
511 * await Effect.runPromise(program)
512 * ```
513 *
514 * @see {@link interruptAll} for using the current fiber as the interruptor
515 *
516 * @category interruption
517 * @since 2.0.0
518 */
519export const interruptAllAs: {
520 (fiberId: number): <A extends Iterable<Fiber<any, any>>>(fibers: A) => Effect<void>
521 <A extends Iterable<Fiber<any, any>>>(fibers: A, fiberId: number): Effect<void>
522} = effect.fiberInterruptAllAs
523
524/**
525 * Checks whether a value is a Fiber. This is a type guard that can be used to
526 * determine if an unknown value is a Fiber instance.
527 *
528 * **When to use**
529 *
530 * Use when checking values at boundaries where an unknown value may be a
531 * runtime fiber.
532 *
533 * **Details**
534 *
535 * The check looks for the internal Fiber type ID marker and does not inspect
536 * the fiber's current state.
537 *
538 * **Example** (Checking for fibers)
539 *
540 * ```ts import.meta.vitest
541 * import { Effect, Fiber } from "effect"
542 *
543 * const program = Effect.gen(function*() {
544 * // Create a fiber
545 * const fiber = yield* Effect.forkChild(Effect.succeed(42))
546 *
547 * // Test if values are fibers
548 * return [Fiber.isFiber(fiber), Fiber.isFiber("hello"), Fiber.isFiber(42), Fiber.isFiber(null)]
549 * })
550 *
551 * const actual = await Effect.runPromise(program)
552 * actual // => [true, false, false, false]
553 * ```
554 *
555 * @category guards
556 * @since 2.0.0
557 */
558export const isFiber = (
559 u: unknown
560): u is Fiber<unknown, unknown> => hasProperty(u, effect.FiberTypeId)
561
562/**
563 * Returns the current fiber if called from within a fiber context,
564 * otherwise returns `undefined`.
565 *
566 * **When to use**
567 *
568 * Use when you need low-level runtime integrations that need access to the currently
569 * executing fiber.
570 *
571 * **Gotchas**
572 *
573 * This is a synchronous accessor, not an Effect. It returns `undefined` outside
574 * an active fiber runtime context.
575 *
576 * **Example** (Getting the current fiber)
577 *
578 * ```ts import.meta.vitest
579 * import { Effect, Fiber } from "effect"
580 *
581 * const program = Effect.gen(function*() {
582 * const current = Fiber.getCurrent()
583 * return current !== undefined
584 * })
585 *
586 * const actual = await Effect.runPromise(program)
587 * actual // => true
588 * ```
589 *
590 * @category getters
591 * @since 4.0.0
592 */
593export const getCurrent: () => Fiber<any, any> | undefined = effect.getCurrentFiber
594
595/**
596 * Adds a fiber to a `Scope` and returns the same fiber.
597 *
598 * **When to use**
599 *
600 * Use when a manually managed fiber should be interrupted when a Scope closes.
601 *
602 * **Details**
603 *
604 * When the scope is closed, the fiber is interrupted. If the scope is already
605 * closed, the fiber is interrupted immediately.
606 *
607 * **Gotchas**
608 *
609 * This does not wait for the fiber to complete. It only registers the
610 * interruption finalizer and returns the same fiber.
611 *
612 * @see {@link interrupt} for interrupting and waiting for completion
613 *
614 * @category resource management
615 * @since 4.0.0
616 */
617export const runIn: {
618 (scope: Scope): <A, E>(self: Fiber<A, E>) => Fiber<A, E>
619 <A, E>(self: Fiber<A, E>, scope: Scope): Fiber<A, E>
620} = effect.fiberRunIn