7612ce44
docs: deinit comment takes both review nits; roadmap strikes the reorder
a73x 2026-08-13 09:43
Commit message
docs/roadmap.md
| Old | New | ||
|---|---|---|---|
| @@ -190,10 +190,12 @@ next one that arrives outranks all of it. | |||
| 190 | - ~~**The transport recipe as a `union(enum)`**~~ — paid by M15 (Task 6). | 190 | - ~~**The transport recipe as a `union(enum)`**~~ — paid by M15 (Task 6). |
| 191 | `Transport.open` takes `union(enum) { sock, via, quic, hand }`, and the | 191 | `Transport.open` takes `union(enum) { sock, via, quic, hand }`, and the |
| 192 | dispatch decision it makes got its first pin in the same task. | 192 | dispatch decision it makes got its first pin in the same task. |
| 193 | - **The stat-after-close reorder in `deinit`** — created by M15 (Task | 193 | - ~~**The stat-after-close reorder in `deinit`**~~ — paid (M16-b, |
| 194 | 10). Narrows the window in which a departing daemon's cleanup could | 194 | c492eb2). Review found the old order was worse than vestigial: a |
| 195 | unlink a successor's socket. The analysis is already written, in the | 195 | successor cannot pass `claim()` while our listener fd is open, so the |
| 196 | `sockpath` comment; only the reorder is owed. | 196 | stat-before-close guard could never fire in the very race it guarded. |
| 197 | The residual stat→unlink gap is the floor — Linux has no | ||
| 198 | inode-predicated unlink. | ||
| 197 | - **`egress_cap` ↔ transport-params, one owner** — also M15. `256*1024` | 199 | - **`egress_cap` ↔ transport-params, one owner** — also M15. `256*1024` |
| 198 | is spelled three times and the tie between the copies is prose only. | 200 | is spelled three times and the tie between the copies is prose only. |
| 199 | - **Fold `Transport`'s `alloc`/`qout` into the `.quic` `Link` payload** — | 201 | - **Fold `Transport`'s `alloc`/`qout` into the `.quic` `Link` payload** — |
src/server.zig
| Old | New | ||
|---|---|---|---|
| @@ -336,12 +336,15 @@ pub const Server = struct { | |||
| 336 | // The stat comes AFTER the close. Statting first was vestigial: | 336 | // The stat comes AFTER the close. Statting first was vestigial: |
| 337 | // 8a82225 fstat'd the live descriptor and so needed the fd open, | 337 | // 8a82225 fstat'd the live descriptor and so needed the fd open, |
| 338 | // 6090604 switched to statting the path and killed that constraint. | 338 | // 6090604 switched to statting the path and killed that constraint. |
| 339 | // A successor only claims the path after concluding we are gone, so | 339 | // A successor only claims the path after concluding nothing is |
| 340 | // asking on this side of our close is the correct side of that event, | 340 | // listening here — claim()'s probe cannot refuse while our fd is |
| 341 | // and it shrinks the window in which one can claim the path between | 341 | // open — so asking on this side of our close is the correct side of |
| 342 | // our answer and our unlink down to the stat→unlink gap. The close | 342 | // that event, and it shrinks the window in which one can claim the |
| 343 | // itself stays unconditional: the fd is ours whoever owns the path, | 343 | // path between our answer and our unlink down to the stat→unlink |
| 344 | // and only the unlink touches the shared namespace. | 344 | // gap. That gap is the floor for deleting by name: Linux has no |
| 345 | // inode-predicated unlink. The close itself stays unconditional: | ||
| 346 | // the fd is ours whoever owns the path, and only the unlink touches | ||
| 347 | // the shared namespace. | ||
| 345 | self.listener.deinit(); | 348 | self.listener.deinit(); |
| 346 | if (self.path_id.stillAt(self.sock_path)) { | 349 | if (self.path_id.stillAt(self.sock_path)) { |
| 347 | std.fs.cwd().deleteFile(self.sock_path) catch {}; | 350 | std.fs.cwd().deleteFile(self.sock_path) catch {}; |