1# Fiber Keep-Alive: Automatic Process Lifetime Management
2
3In v3, the core `effect` runtime did **not** keep the Node.js process alive while
4fibers were suspended on certain asynchronous operations. If a fiber was waiting on
5something like `Deferred.await` and there was no other work scheduled on the
6event loop, the process would exit immediately — the fiber's suspension did not
7register as pending work from Node.js's perspective.
8
9The only way to prevent this was to use `runMain` from `@effect/platform-node`
10(or `@effect/platform-bun`), which installed a long-lived `setInterval` timer
11to hold the process open until the root fiber completed.
12
13In v4, **the keep-alive mechanism is built into the core runtime**.
14
15## The Problem in v3
16
17Consider the following program:
18
19```ts
20import { Deferred, Effect } from "effect"
21
22const program = Effect.gen(function*() {
23 const deferred = yield* Deferred.make<string>()
24
25 yield* Deferred.await(deferred)
26})
27
28Effect.runPromise(program)
29```
30
31In v3, when the main fiber reached `yield* Deferred.await(deferred)`, it suspended
32while waiting for the worker fiber to complete the deferred. However, from the
33JavaScript runtime's perspective, the event loop had no more work to do. Thus,
34the process would exit.
35
36The workaround was to use `runMain` from the platform package, which installs
37a timer that holds the process open until the root fiber completes:
38
39```ts
40import { NodeRuntime } from "@effect/platform-node"
41
42NodeRuntime.runMain(program)
43```
44
45## What Changed in v4
46
47In v4, the Effect fiber runtime automatically manages a reference-counted
48keep-alive timer.
49
50This means the following program works in v4 **without** `runMain`:
51
52```ts
53import { Deferred, Effect, Fiber } from "effect"
54
55const program = Effect.gen(function*() {
56 const deferred = yield* Deferred.make<string>()
57
58 // The process stays alive while waiting — no runMain needed
59 yield* Deferred.await(deferred)
60})
61
62Effect.runPromise(program)
63```
64
65## `runMain` Is Still Recommended
66
67Even though the core runtime now handles keep-alive, `runMain` from the platform
68packages is still the recommended way to run Effect programs. It provides:
69
70- **Signal handling** — listens for `SIGINT` / `SIGTERM` and interrupts the
71 root fiber gracefully.
72- **Exit code management** — calls `process.exit(code)` when the program fails
73 or receives a signal.
74- **Error reporting** — reports unhandled errors to the console.