diff options
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/design.md | 47 |
1 files changed, 46 insertions, 1 deletions
diff --git a/docs/design.md b/docs/design.md index 8387a4d..3ad9338 100644 --- a/docs/design.md +++ b/docs/design.md @@ -130,6 +130,49 @@ engine allocates nothing and makes no OS calls; a session is `push()`, `retry()`, `next()`, `reply()`, `output()`, `wrote()`; `fidCount()` counts the fids held. +# Io runner + +There are two ways to run the engine. A freestanding target, or an +application with its own event loop, drives it directly: `push()` bytes in, +answer every `retry()` then every `next()` request through `reply()`, drain +`output()` and report `wrote()`, on whatever thread and schedule it likes +(the board firmware and the 9proc probe do this). A hosted target may +instead hand the engine to `serve.Runner(Backend, Options, Limits)`, an +`std.Io` adapter beside `http`: it owns listeners (`listen()` takes a +`transport.Address`, Unix or TCP, up to `Limits.listeners`), a table of +`Limits.connections` slots with their buffers (`Limits.msize` sizes them; a +connection past the table is accepted and closed), and two tasks per +connection in one `Io.Group`, one reading frames with `transport.readFrame` +and pushing them in, one stepping the engine and writing its output with +the stream's writer. The engine and the backend contract are untouched by +it, and it makes the engine no less Io-free. + +The backend is reached through a `Handler`: `serve(ctx, conn, req)` runs on +the connection's task with the engine unlocked and answers with +`Conn.reply()`, at once or later from any task or thread, or takes the +engine over between `Conn.lock()` and `Conn.unlock()`, drives `next()`, +`retry()` and `reply()` itself and calls `Conn.flush()` so the connection +task sends what that produced (the editor answers on its own thread this +way). `opened` and `closed` bracket a slot's life; `Conn.user` is the +application's. + +Wake-up is explicit. Each connection has a flag word and an `Io.Event`; the +reader raises "input", `reply()` and `flush()` raise "output", `Conn.wake()` +(or the runner's `wakeAll()`) raises "retry", and the connection task steps +the engine on every signal, retrying parked requests only for "retry". That +keeps a backend that answers `Status.again` from being polled by its own +replies: it is asked again when it says it has news. Tflush goes through the +engine as always. A connection ends on EOF, a read or write error, a +protocol error (`protocol.dead`), `Conn.close()`, the optional greet +timeout (`InitOptions.greet_timeout_ms`: no Tversion in time) or `stop()`; +the runner then calls `hangup()`, pays the backend every release `next()` +still yields through `serve`, closes the socket and frees the slot. +`stop()` cancels the group (`Io.Group.cancel` interrupts the blocking +accepts and reads), joins every task and closes the listeners; a `serve` +that waits on another thread must stop waiting once `stop()` has begun. +Unix socket paths are the application's: the runner neither unlinks, +chmods nor removes them. + # Related programs Programs built on the library ship from this repository as `cloud9/<name>/`, @@ -151,7 +194,9 @@ Pardes consumes the sibling package through build.zig.zon. Its control tree (`src/ninep/`) is an `fs.Server` backend using the contract types above; its `src/9p.zig` names the editor's and the board's `fs.Options`. `src/9p_io.zig` retains mounting, discovery, the editor's event-loop scheduling, -connection limits, and error presentation. Protocol bytes, session validation, +connection limits, and error presentation; its Unix and TCP listeners run on +`serve.Runner` with a handler that hands each request to the editor's thread, +while QUIC keeps its own poll loop. Protocol bytes, session validation, the file-server engine, and TCP/Unix/QUIC transport implementation come from cloud9. Pardes selects the existing `pardes-9p` ALPN for compatibility. |
