(
scope: Scope.Scope,
opts?: {
onIdle?: Effect.Effect<void>
onBusy?: Effect.Effect<void>
onInterrupt?: Effect.Effect<A, E>
},
)
| 37 | | { readonly _tag: "ShellThenRun"; readonly shell: ShellHandle<A, E>; readonly run: PendingHandle<A, E> } |
| 38 | |
| 39 | export const make = <A, E = never>( |
| 40 | scope: Scope.Scope, |
| 41 | opts?: { |
| 42 | onIdle?: Effect.Effect<void> |
| 43 | onBusy?: Effect.Effect<void> |
| 44 | onInterrupt?: Effect.Effect<A, E> |
| 45 | }, |
| 46 | ): Runner<A, E> => { |
| 47 | const ref = SynchronizedRef.makeUnsafe<State<A, E>>({ _tag: "Idle" }) |
| 48 | const idle = opts?.onIdle ?? Effect.void |
| 49 | const onBusy = opts?.onBusy ?? Effect.void |
| 50 | const onInterrupt = opts?.onInterrupt |
| 51 | let ids = 0 |
| 52 | |
| 53 | const state = () => SynchronizedRef.getUnsafe(ref) |
| 54 | const next = () => { |
| 55 | ids += 1 |
| 56 | return ids |
| 57 | } |
| 58 | |
| 59 | const complete = (done: Deferred.Deferred<A, E | Cancelled>, exit: Exit.Exit<A, E>) => |
| 60 | Exit.isFailure(exit) && Cause.hasInterruptsOnly(exit.cause) |
| 61 | ? Deferred.fail(done, new Cancelled()).pipe(Effect.asVoid) |
| 62 | : Deferred.done(done, exit).pipe(Effect.asVoid) |
| 63 | |
| 64 | const awaitDone = (done: Deferred.Deferred<A, E | Cancelled>) => |
| 65 | Deferred.await(done).pipe(Effect.catchTag("RunnerCancelled", (e) => onInterrupt ?? Effect.die(e))) |
| 66 | |
| 67 | const idleIfCurrent = () => |
| 68 | SynchronizedRef.modify(ref, (st) => [st._tag === "Idle" ? idle : Effect.void, st] as const).pipe(Effect.flatten) |
| 69 | |
| 70 | const finishRun = (id: number, done: Deferred.Deferred<A, E | Cancelled>, exit: Exit.Exit<A, E>) => |
| 71 | SynchronizedRef.modify( |
| 72 | ref, |
| 73 | (st) => |
| 74 | [ |
| 75 | Effect.gen(function* () { |
| 76 | if (st._tag === "Running" && st.run.id === id) yield* idle |
| 77 | yield* complete(done, exit) |
| 78 | }), |
| 79 | st._tag === "Running" && st.run.id === id ? ({ _tag: "Idle" } as const) : st, |
| 80 | ] as const, |
| 81 | ).pipe(Effect.flatten) |
| 82 | |
| 83 | const startRun = (work: Effect.Effect<A, E>, done: Deferred.Deferred<A, E | Cancelled>) => |
| 84 | Effect.gen(function* () { |
| 85 | const id = next() |
| 86 | const fiber = yield* work.pipe( |
| 87 | Effect.onExit((exit) => finishRun(id, done, exit)), |
| 88 | Effect.forkIn(scope), |
| 89 | ) |
| 90 | return { id, done, fiber } satisfies RunHandle<A, E> |
| 91 | }) |
| 92 | |
| 93 | const finishShell = (id: number) => |
| 94 | SynchronizedRef.modifyEffect( |
| 95 | ref, |
| 96 | Effect.fnUntraced(function* (st) { |
nothing calls this directly
no test coverage detected