# src/workerd/io/ ## OVERVIEW I/O lifecycle, per-request context, worker/isolate management, actor storage, consistency gates, and compatibility flags. ## KEY CLASSES | Class | File | Role | | ------------------------------------------- | ---------------------- | ------------------------------------------------------------------------------------------- | | `IoContext` | `io-context.{h,c++}` | Per-request god object; thread-local via `IoContext::current()` | | `IoContext::IncomingRequest` | `io-context.h` | Tracks one inbound request for metrics/tracing; actors have many per IoContext | | `Worker` | `worker.{h,c++}` | Ref-counted worker instance; owns Script + Isolate refs | | `Worker::Isolate` | `worker.h:337` | V8 isolate wrapper; shared across workers with same config | | `Worker::Script` | `worker.h:250` | Compiled script bound to an Isolate | | `Worker::Lock` | `worker.h:677` | Synchronous V8 isolate lock (must hold to touch JS heap) | | `Worker::AsyncLock` | `worker.h:799` | Fair async queue for acquiring `Worker::Lock` | | `Worker::Actor` | `worker.h:819` | Durable Object instance; owns gates + cache + hibernation state | | `ActorCache` | `actor-cache.{h,c++}` | LRU write-back cache over RPC storage; `ActorCacheOps` base | | `ActorSqlite` | `actor-sqlite.{h,c++}` | SQLite-backed `ActorCacheOps` implementation | | `InputGate` / `OutputGate` | `io-gate.{h,c++}` | Consistency primitives for DO concurrent request handling | | `IoOwn` / `IoPtr` / `ReverseIoOwn` | `io-own.{h,c++}` | Cross-heap smart pointers preventing KJ↔JS ref leaks | ## WHERE TO LOOK | Task | File(s) | | ------------------------------- | --------------------------------------------------------------------------------- | | Promise bridging KJ↔JS | `io-context.h` — `awaitIo()`, `awaitJs()` | | Request lifecycle / subrequests | `io-context.{h,c++}`, `worker-entrypoint.{h,c++}` | | Actor storage ops | `actor-cache.h` (`ActorCacheOps`), `actor-sqlite.h`, `actor-storage.capnp` | | DO gate semantics | `io-gate.{h,c++}` — `InputGate::CriticalSection`, `OutputGate::lockWhile()` | | Worker/isolate creation | `worker.{h,c++}`, `worker-modules.{h,c++}` | | Metrics/logging hooks | `observer.h` — `RequestObserver`, `IsolateObserver`, `ActorObserver` | | Tracing | `trace.{h,c++,capnp}`, `trace-stream.{h,c++}`, `tracer.{h,c++}` | | Resource limits | `limit-enforcer.h` (abstract interface) | | Timer scheduling | `io-timers.{h,c++}` | | Hibernatable WebSockets | `hibernation-manager.{h,c++}` | | Cap'n Proto schemas | `worker-interface.capnp`, `actor-storage.capnp`, `container.capnp`, `trace.capnp` | ## CONVENTIONS - `IoContext::current()` — ambient thread-local access; only valid inside a request - `awaitIo(js, kjPromise, func)` bridges KJ→JS; `func` runs under V8 lock. `awaitIo(js, kjPromise)` for identity - `awaitJs(js, jsPromise)` bridges JS→KJ - `addObject(kj::Own)` returns `IoOwn` — the **only** safe way to store KJ I/O objects reachable from JS heap - Two-phase locking: `Worker::AsyncLock` (fair queue) → `Worker::Lock` (V8 isolate lock) - `ActorCacheOps` methods return `kj::OneOf>` — sync when cached, async otherwise - `OutputGate::lockWhile(promise)` blocks outgoing responses until the promise resolves - `InputGate::CriticalSection` must succeed or permanently breaks the gate - Observer classes (`RequestObserver`, `IsolateObserver`, etc.) have no-op defaults; all methods optional ## ANTI-PATTERNS - **NEVER** use `awaitIoLegacy()` in new code — use `awaitIo()` with continuation - `awaitIoImpl` parameter ordering (promise by-value, func by-ref) is **critical** for exception safety - `abortWhen()` promises must **never** enter the V8 isolate - Cross-request I/O object access throws by design (IoOwn prevents this)