summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
Diffstat (limited to 'docs')
-rw-r--r--docs/design.md47
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.