Repository navigation
Every server admits its clients through one table, toyos::admission - #1242
Conversation
97b4139 to
4d1fad6
Compare
Review of #1242 at 4d1fad6ArchitectureAdmission belongs in the userland SDK. It is a mechanism shared by servers, not a kernel or ABI concern, and Net size: the module adds about 415 production lines and the servers lose 210, so production grows by about 200 lines. Tests grow by 249 module test lines, against about 40 removed server test lines ( The design is right. One property the PR claims is not true for the supervisor, and its documented contract is what misled it. Details are below. Required findings
Suggestions (optional)
The questions in the briefThe module.
Each conversion. Beyond findings 1 and 2, I found no behaviour lost.
The badge lookup. The panic is sound.
The four records left open. The reasoning is right for all four, and each would be wrong to close.
Per-client shares on unbadged ports. The record is accurate about which tables are protected by holder, which by grant and which have one share for everyone, and about the minting gap. It assigns the badge decision to the owner. The Overlap with #1239. #1239 makes
The six deleted methods. Confirmed. Evidence
The fixes for findings 1 and 2 touch only behaviour at a full table, which no row or boot reaches. If they are limited to that guard, the docs and the records, a compile at the new head is enough, and the queued guest run and the metal boots remain valid evidence. Landing preconditions.
VerdictSEND BACK. The four required findings are each a small change. Once they are fixed and the guest run and metal boots show the above, this is ready to land. |
…s are said once a limit A watch on an acceptor stays armed until it fires, so a server that stopped watching its acceptor when its table filled still heard the watch it had armed before, accepted, and closed the connection for the full table: the supervisor, across its five acceptors and handed-over launches, and filepicker across its two. `Admission::accept` now takes a connection only while the table has room and leaves it in the port's queue otherwise, and every server accepts through it. The compositor parks each acceptor whose table was full and watches it again once there is room, where it had done so for `compositor` alone; its `prompt`, `clipboard` and `display` would otherwise have been watched again at once and fired again. Every line a server said of a key's refused or let-go connection was one a connection, at the client's rate, which spends the server's allowance in logkeeper and counts its own lines that second rather than writing them. The table now says one line a key a `LIMIT` (`Refused::say`, `Event::Left`'s `say`) carrying how many went unsaid since, remembers at most `slots` keys, and never says a hang-up. What a server says of a request it then refuses has no such bound: `issues/a-client-writes-its-servers-log-at-its-own-rate-through-refused-requests.md`. diskserver held 16 drivers named plus 16 naming themselves where its comment says 16 connected at once; the accept now counts both against `MAX_DRIVERS` (`Admission::held`), and its poller is sized for that bound again. `Policy::limit` goes: every table's limit is `LIMIT`, as the manual has it, and `Left::Silent` carries nothing. The records: the let-go record no longer claims a run since the 20 s limit, and names which servers refuse for a share; the table record keys `login` by its row alone, and its exit is the outcome rather than a mechanism the owner has not chosen. The manual's admission section says both rules. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Both benches are down. The `testcases` (with `testcases-watchdog`) and `shared-debug` boots that #1242's servers were staged for are recorded as owed on the T14, with what main must show there, in the record the PR already narrows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review r2 of #1242 at 602db29ArchitectureThe round-2 changes fit the design. Size: the module's production code grows by 95 lines (415 to 510) and its tests by 97 (249 to 346). The servers change by +194/-147. The compositor removes the Round-1 findings
FindingsB1. BLOCKER: the line bound fails past
B2. BLOCKER: logkeeper's new comment gives a false reason, and the code drops a line the table chose to say.
B3. BLOCKER: the metal checks this PR owes are not recorded.
For the orchestrator (PR description)
EvidenceMy runs on a detached worktree at 602db29:
The six servers' clippy reported 11 findings. Negative controls. They ran at d09c3e470. Guest run 43066328896. Its checkout fetched
No row reaches a full table, the line bound or the driver bound. Those rest on the module's host tests and on inspection, which is the right place for them. After the fixes. B1 and B2 change code unless both are fixed in comments and docs only. The ruling then asks for green rows at a code-identical head, so one dispatch of the same six rows at the new head would meet it. No row reaches the changed behaviour. B3 is a record and needs no run. VerdictSEND BACK. Three small blockers remain: B1, the line bound past |
… a limit The table forgot the key it had said of longest ago to remember a new one, even within that key's limit, so with more keys cycling than it has slots every refusal was said again, at the clients' combined rate; its own test asserted that. A table now remembers only the keys it said of within the last `LIMIT`, at most `slots` of them, forgets none early, and says one line a limit for all the keys past those. One count of the lines it did not say rides on the next it does, so it says at most `slots + 1` lines a limit however many keys arrive and accounts for every one. The count names no key: one count and a time replace a count per key and the eviction choice. logkeeper now says every reader it lets go but one that hung up, as the other servers do. It had discarded a malformed reader's line after the table counted it as said, which hushed a silent reader's line in the same limit, and its comment gave a reason the bound had already removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review r3 of #1242 at 5d1abdaArchitectureThe new representation is simpler than the per-key counts it replaces. The table now keeps a vector of the keys it said of within the last limit, holding at most Round-2 findingsB1. The bound holds; the accounting claim does not (see R1). By inspection, for any non-decreasing
I checked this with a random-sequence model of B2. Resolved. B3. Resolved. The narrowed record carries the owed-on-metal bullet (bc262f8). FindingsR1. BLOCKER: a count that no later line carries is never written, yet three texts say every refusal is accounted for.
Suggestions
For the orchestrator (PR description)
EvidenceMy runs, in a detached worktree at 5d1abda:
The author's negative controls (evidence If R1 is fixed by (a), it needs the host test and its negative control, a compile of every server (each VerdictSEND BACK. One blocker remains, R1: a count no later line carries is never written, while the header, the manual and the PR say every refusal is accounted for. It needs either the pending count written within a limit or the decider's acceptance of the weaker property, stated where the bound is. B1's bound, B2 and B3 are resolved. |
…o key twice a limit A table counted each refusal or let-go connection it did not say into the next line it said, and wrote the count nowhere else, so on a quiet table a count could wait for good. Now, while a count is owed, `left` covers the moment a limit after the table's last line, when every line it said has run its limit, and `next` ends that pass with `Event::Unsaid` and spends the shared line on it. The bound stays `slots + 1` lines a limit, and every refusal and connection let go is said or counted in a line within a limit of the one before. Each server says the count in its own voice; the module writes no line itself, since a host build's log discards it and the servers name their tables their own way. The shared line remembers its key, so a key said there is not said again in a slot freed within its limit: no key is said twice in a limit. The test past the slots refuses `slots + 2` keys in the next limit and expects `slots + 1` lines, so a shared line that never renews fails it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review r4 of #1242 at a7078c8Scope: R1, S1 and S2 from review r3, and the code changed for them. Architecture
Size of review r3's change:
Round-3 findingsR1. Resolved. I checked each property by inspection and then with a model.
S1. Resolved. S2. Resolved. At S3. Resolved. FindingsNo blockers. S4. S5. S6. EvidenceMy runs, in a detached worktree at a7078c8:
The author's evidence (
Guest rows:
For the orchestratorUnder the existing ruling, landing waits on the same six rows passing at a7078c8 or a code-identical head. That condition is not met yet. VerdictLAND. R1, S1 and S2 are resolved, and no blocker remains in the code or the texts. The existing ruling's guest condition, above, is still open. |
…able The comment where a pass says a count gives the reason the shared line is free: `last` is the latest line, the shared line's included. `Unsaid`'s doc no longer ties it to a connection, since `Event::Unsaid` carries one of no connection's. The compositor's count line names which of its two tables it is of, as the other servers with two do, and a count of one reads "1 more connection". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Orchestrator verification of 833fd60 (S4–S6 after review r4 LAND): against a7078c8 only a comment, |
Eight servers each kept the connections they had accepted and not yet served in a table of their own, with their own copy of a 2 s handshake limit: the compositor, diskserver, filepicker, fileserver, logkeeper, netstack, the supervisor and usbd. Six of them let a connection go on that limit before reading what the same wait reported, two waited a fresh whole limit each pass, six accepted a connection only to close it when the table was full, and only the file server gave a client a share. `toyos::admission` is that table once. A connection enters with `admit` under the key its server reads off its badge and leaves `next` as a `Client` holding its first whole request, or as the reason it was let go; a request is read from nothing else. One key holds at most `Policy::share` of `Policy::slots`; a connection is read before it is let go on its limit, so a request that is whole is served however late the pass; the server's wait is what is left of the oldest limit; a full table takes no more and the port's queue holds the next. The limit is 20 s, Apache httpd's `RequestReadTimeout` header default, where each copy had 2 s. Each server supplies its policy and its key and keeps none of the mechanism: - fileserver: hellos, keyed by its grant's share, a quarter each as before (`bounds::HANDSHAKES`); `bounds::handshake` and its test go, and a client is in the served table only once it lent its window or was exported, so `Client::waiting` and `since` go. - netstack: requests keyed by the row and session its connector was minted for, a quarter each, refused `ERR_RESOURCE_EXHAUSTED` past it; its NIC drivers' hellos through a second table. - the supervisor: `launcher`, `grants`, `login`, `admin` and `swap` in one table keyed by badge, a quarter each, and `power` in a table of its own, so a stop never waits behind launches. - diskserver: clients keyed by their grant, a quarter each, refused `Exhausted` past it; a driver is in the driver table only once it has said its name, through a table of one connection per driver badge. Both drops are now said in its log. - the compositor: `compositor`'s first frames and a copy's commit in one table, and one connection each of `prompt`, `clipboard` and `display` in another, which replaces `toyos_desktop::pending_admits`. - filepicker: half the table each for the picker's callers and the supervisor's questions. - logkeeper and usbd: their `inspect` readers; logkeeper now says a drop on the limit, which happens at the table's rate and not a reader's. The per-request path of a served client is unchanged: the file server still pumps its own `FrameRx` on its own `Connection`, and the table's frame buffer is inline, as each server's was, so no connection costs an allocation it did not. A table whose port carries no badge naming its holder has one share for all holders (`Policy::unshared`): the compositor, filepicker, logkeeper, usbd, netstack's `link`, the supervisor's `swap` and `power`. `issues/one-program-that-fills-a-servers-table-holds-every-other-client-out.md` says so and what closes it; soundserver's control plane and the surface hosts, which keep a client past its first request and have no limit, are there too. `issues/servers-let-a-client-go-on-the-handshake-limit-with-its-request-unread.md` is closed. What it asked that this does not do is filed on its own: `issues/a-client-its-server-let-go-reads-that-as-no-server.md` and `issues/netstacks-serve-module-has-no-tests.md`. The manual's services page gains the design of admission. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s are said once a limit A watch on an acceptor stays armed until it fires, so a server that stopped watching its acceptor when its table filled still heard the watch it had armed before, accepted, and closed the connection for the full table: the supervisor, across its five acceptors and handed-over launches, and filepicker across its two. `Admission::accept` now takes a connection only while the table has room and leaves it in the port's queue otherwise, and every server accepts through it. The compositor parks each acceptor whose table was full and watches it again once there is room, where it had done so for `compositor` alone; its `prompt`, `clipboard` and `display` would otherwise have been watched again at once and fired again. Every line a server said of a key's refused or let-go connection was one a connection, at the client's rate, which spends the server's allowance in logkeeper and counts its own lines that second rather than writing them. The table now says one line a key a `LIMIT` (`Refused::say`, `Event::Left`'s `say`) carrying how many went unsaid since, remembers at most `slots` keys, and never says a hang-up. What a server says of a request it then refuses has no such bound: `issues/a-client-writes-its-servers-log-at-its-own-rate-through-refused-requests.md`. diskserver held 16 drivers named plus 16 naming themselves where its comment says 16 connected at once; the accept now counts both against `MAX_DRIVERS` (`Admission::held`), and its poller is sized for that bound again. `Policy::limit` goes: every table's limit is `LIMIT`, as the manual has it, and `Left::Silent` carries nothing. The records: the let-go record no longer claims a run since the 20 s limit, and names which servers refuse for a share; the table record keys `login` by its row alone, and its exit is the outcome rather than a mechanism the owner has not chosen. The manual's admission section says both rules. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Both benches are down. The `testcases` (with `testcases-watchdog`) and `shared-debug` boots that #1242's servers were staged for are recorded as owed on the T14, with what main must show there, in the record the PR already narrows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… a limit The table forgot the key it had said of longest ago to remember a new one, even within that key's limit, so with more keys cycling than it has slots every refusal was said again, at the clients' combined rate; its own test asserted that. A table now remembers only the keys it said of within the last `LIMIT`, at most `slots` of them, forgets none early, and says one line a limit for all the keys past those. One count of the lines it did not say rides on the next it does, so it says at most `slots + 1` lines a limit however many keys arrive and accounts for every one. The count names no key: one count and a time replace a count per key and the eviction choice. logkeeper now says every reader it lets go but one that hung up, as the other servers do. It had discarded a malformed reader's line after the table counted it as said, which hushed a silent reader's line in the same limit, and its comment gave a reason the bound had already removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…o key twice a limit A table counted each refusal or let-go connection it did not say into the next line it said, and wrote the count nowhere else, so on a quiet table a count could wait for good. Now, while a count is owed, `left` covers the moment a limit after the table's last line, when every line it said has run its limit, and `next` ends that pass with `Event::Unsaid` and spends the shared line on it. The bound stays `slots + 1` lines a limit, and every refusal and connection let go is said or counted in a line within a limit of the one before. Each server says the count in its own voice; the module writes no line itself, since a host build's log discards it and the servers name their tables their own way. The shared line remembers its key, so a key said there is not said again in a slot freed within its limit: no key is said twice in a limit. The test past the slots refuses `slots + 2` keys in the next limit and expects `slots + 1` lines, so a shared line that never renews fails it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…able The comment where a pass says a count gives the reason the shared line is free: `last` is the latest line, the shared line's included. `Unsaid`'s doc no longer ties it to a connection, since `Event::Unsaid` carries one of no connection's. The compositor's count line names which of its two tables it is of, as the other servers with two do, and a count of one reads "1 more connection". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
833fd60 to
d5efd6c
Compare
|
Orchestrator verification of the rebase onto 2c8d8bb (d5efd6c): one textual conflict in diskserver's |
Problem
Eight servers each kept the connections they had accepted and not yet served in a table of their own, with their own copy of a 2 s
HANDSHAKE_TIMEOUT: the compositor, diskserver, filepicker, fileserver, logkeeper, netstack, the supervisor and usbd.issues/servers-let-a-client-go-on-the-handshake-limit-with-its-request-unread.mdrecorded what the copies got wrong: six let a connection go on the limit before reading what the same wait reported, two waited a fresh whole limit each pass, six accepted a connection only to close it at a full table, two said nothing of a drop, and the supervisor's stop waited in the launchers' table. Only the file server gave a client a share of its table.What this does
toyos::admission(sdk/toyos/src/admission.rs) is that table once, and the eight servers keep none of the mechanism.Admission::accept, enters withAdmission::admitunder the key its server reads off its badge, and leavesAdmission::nextas aClientholding its first whole request or as the reason it was let go. A first request is read from nothing but aClient, and aClienthas no call that waits on its peer:next_requesttakes a whole frame or nothing, the replies aretry_send*.Policy::shareofPolicy::slots; its next connection is refused by name (Refused) while another key's is taken.left: what remains of the oldest limit.accepttakes a connection only while the table has room and otherwise leaves it in the port's queue. A watch on an acceptor stays armed until it fires, so leaving the acceptor unwatched is not enough; the rule is the module's, not each server's.slotskeys, and one more line a limit shared by all the keys past those (Refused::say,Event::Left'ssay); no key is said twice in a limit. Each refusal or connection let go that is not said is counted into the next line said. A count no line has carried within a limit of the last is said on its own (Event::Unsaid, in the server's voice) on the shared line, andleftwakes the server for it. So a table says at mostslots + 1lines a limit however many keys arrive, says or counts every refusal and connection let go in a line within a limit of the line before, and never says a hang-up.admission::LIMIT, 20 s: Apache httpd'sRequestReadTimeoutheader default, cited there with nginx's 60 s. It is noPolicyfield: every table's is the same. The compositor's display-output driver's hello deadline took the same constant, so it too went from 2 s to 20 s.Per server, policy and key are data:
bounds::HANDSHAKES, 32)ERR_RESOURCE_EXHAUSTEDlauncher,grants,login,admin,swap(32)login's the row alone;swap's nonepower(8), a table of its ownExhaustedMAX_DRIVERS)compositorfirst frames and a copy's commit (32)prompt,clipboard,display(3)toyos_desktop::pending_admits)log's readers (8)inspect's readers (8)What a client can and cannot do afterwards
a_key_at_its_share_is_refused_while_another_key_is_taken).a_client_that_never_sends_its_request_…,a_frame_no_protocol_describes_and_a_hang_up_each_free_their_slot).a_client_that_reads_no_reply_is_refused_it_and_holds_nobody).left; the table itself waits on nothing.a_full_table_accepts_nothing_and_its_port_keeps_the_connection).a_key_s_refusals_are_said_once_a_limit_with_the_count_between,a_key_s_malformed_frames_are_said_once_a_limit_and_hang_ups_never,a_key_said_on_the_shared_line_is_said_once_a_limit,past_its_slots_keys_a_table_says_one_more_line_a_limit_and_counts_the_rest,a_count_no_line_carries_is_said_on_its_own_a_limit_after_the_last_line,a_count_said_on_its_own_spends_the_shared_line). What a server says of a request it then refuses has no such bound yet:issues/a-client-writes-its-servers-log-at-its-own-rate-through-refused-requests.md.Policy::unsharedtables (the compositor'scompositorport, logkeeper'slog, usbd'sinspect, netstack'slink, the supervisor'spower) one holder can hold the whole table for a limit at a time, and keep it full by reconnecting; on filepicker's, its port's half; on the supervisor's shared table, the quarter thatswap's holders share. A program that holds several keys (sessions, or rows it launches) holds as many shares. Both are recorded, below.The per-request path
Unchanged, by reading: the file server's
pump/replystill read its ownFrameRxon its ownConnectionand answer with onetry_send; the diff touches neither.sdk/toyos/src/ipc.rsgains one#[cfg(test)]function. The table's frame buffer is inline in its entry, as each server'sPendingheld it, so a connection costs no allocation it did not;Client::next_requestisFrameRx::pumpand two stores. What does change is per connection, for the servers whose every request is a connection of its own (netstack, the supervisor, diskserver, filepicker, logkeeper's and usbd's readers):admitcounts its key's entries (a walk of the entries) and finds a free slot (slots × entries, at most 32 × 32 comparisons), andwokefinds the entry its token names (a walk of the entries), where the copies pushed onto aVecand searched the ready list once per entry. A refusal or a let-go connection also walks the at mostslotskeys the table remembers lines of. That is an inference from the code, not a measurement; no metal A/B was run.Records
issues/servers-let-a-client-go-on-the-handshake-limit-with-its-request-unread.md. Four of its exit items are not this change's and live on:issues/a-client-its-server-let-go-reads-that-as-no-server.md(the client libraries' word for a hang-up) andissues/netstacks-serve-module-has-no-tests.md, new; and soundserver's and the surface hosts' (read before letting go, and stop accepting at a full table), moved into the narrowed record below.issues/a-client-writes-its-servers-log-at-its-own-rate-through-refused-requests.md, the lines a server says of a request it refuses, which the admission bound does not reach.issues/one-program-that-fills-a-servers-table-holds-every-other-client-out.mdnow says which tables are shared by holder (loginby its row alone), which by grant, and which have one share for all holders because their connector carries no badge, and that soundserver's control plane and the surface hosts are not on the mechanism: both keep a client past its first request, so what bounds them is a share of their served clients. Its exit is the outcome, no holder past its share of any table, and leaves the mechanism to the owner's decision below.four-shares-take-a-file-server: unchanged; four shares at their part still take a table.netstack-lookup-slots-have-no-per-client-share,netstacks-places-are-one-number-for-every-client: shares of the node's lookups and places, not of the admission table. The key they ask for now reaches netstack on every connection (the badge); the node does not yet count by it.one-diskserver-session-can-hold-every-device-slot:toyos_blockserve::Sessions::pull's device slots, after admission; the block session's track.a-guest-close-after-a-refused-handshake-can-leave-its-peer-waiting: a TCP close during a TLS handshake with a host peer; not a server's admission at all.Decision returned to the owner
Giving the unbadged ports a per-holder share means the supervisor mints every connector it hands out with its holder's row and session (and console and terminal mint their surface's), which changes what a badge says: the manual has "the kernel vouches for what was granted, never for who connects". Not done here; the record carries it.
Manual
docs/src/architecture/services.mdgains Admission, which says the full-table rule and the bound on a client's lines.Review r1
Review sent this back with four findings, each addressed in 602db29:
Admission::acceptnow asks the room at every accept, every server accepts through it, the compositor parks each acceptor whose table was full, and the module docs say the rule.MAX_DRIVERS, at the watch and at the accept, as its comment says, and its poller is sized for that again.From the suggestions:
Policy::limitis gone; the file server's two HELLO arms already share their logic,bounds::hello, and differ in how they answer, so they stay; the description's corrections are made above and in Risks.Review r2
Review sent this back with three findings, addressed in 5d1abda and bc262f8:
slotskeys: the table evicted the key it had said of longest ago even within its limit, and its own test asserted the second line. It now remembers only keys said of within the last limit, at mostslots, forgetting none early, and says one more line a limit for all keys past those. One table-wide count of the lines it did not say rides on the next it does. That is simpler than a count per key plus an overflow count: there is no eviction choice and no count left pending on a forgotten key. It keeps both properties, at mostslots + 1lines a limit and every line accounted for. The cost is that a count names no key. The module header, the manual and Risks say so, and the test is nowpast_its_slots_keys_a_table_says_one_more_line_a_limit_and_counts_the_rest.Review r3
Review sent this back on one finding, addressed in a7078c8 with the suggestions:
leftnow covers the moment a limit after the table's last line, when every line it said has run its limit.nextends that pass withEvent::Unsaidand spends the shared line on it, so the bound staysslots + 1.Eventrather than a line the module writes, for three reasons. A host build's log discards what the module would write, so no host test could see it. The servers log throughsay!,println!oreprintln!under names of their own. And a new variant makes each of the elevenEventmatches handle it.slots + 2keys in the next limit and expectsslots + 1lines.Review r4
Review: LAND. Its three optional suggestions are taken in 833fd60:
Unsaid's doc no longer ties it to a connection.Rebase onto main 2c8d8bb
The branch is rebased onto main 2c8d8bb, which brought #1235's block client sessions, #1216 and #1239's typed received handles.
asked: this branch takes the connection from itsClient, and A receive reports what each handle names, and a typed handle is made of a received one only where the kernel said so #1239 types the open's region (region.into()). Both are kept.FrameRx::take_handlesnow returnsReceivedHandle, soadmission::Client::take_handlespasses the kernel's kind through. Its callers already accept that: the file server's hello, netstack's link hello and diskserver's open.handed_overmerged as A receive reports what each handle names, and a typed handle is made of a received one only where the kernel said so #1239 wrote it (handle.typed::<Connection>()), returning this branch's(Connection, Caller).system/diskserver/src/session.rsanddisk.rs, which this branch leaves as main has them. The admission table and its driver bound are inmain.rs, which main touched only atasked.At the head d5efd6c, with a clean worktree (logs), all exit 0:
cargo test -p toyos --lib admission, 18 of 18, andtoyos's two clippy shapes with-D warnings.inspect.rs.cargo check --all-targetsof every host-buildable converted server andtoyos-desktop.cargo run -- --build-only(log), which compiles every program for the guest.Guest run 43125079920, the same six rows at d5efd6c, is queued. Before the rebase, run 43099711330 passed all six at 833fd60.
Checks before the rebase
The evidence is on
ToyOSOrg/evidence, branchpr-1242, last at135f8e711cb1, and each link below names the commit its log was published in.The pre-rebase head was 833fd60. It differs from a7078c8 only in one comment and one doc in
sdk/toyos/src/admission.rs, the count's own-line text, and the compositor's count line. At the head (logs):cargo test -p toyos --lib admissionpassed 18 of 18;toyos's clippy shapes exited 0;cargo run -- --build-onlyexited 0 (log).The checks below ran at a7078c8 and hold for the head. Its negative controls mutate code 833fd60 does not change.
Review r3 changed:
sdk/toyos/src/admission.rs: the owed count andEvent::Unsaid, and the keyed shared line;Event::Unsaidarm in each server'sEventmatch;At a7078c8
All exit 0 (logs):
cargo clippy -p toyos --all-targetsandcargo clippy -p toyos --lib --features fd-table, with CI's adopted lints and-D warnings.cargo test -p toyos, which includesadmission's 18 tests.cargo clippy --all-targetsof the six converted servers that build for a host, with the adopted lints as warnings. Every finding is older than this branch.cargo run -- --build-onlyat the head exited 0 (log). It compiles every program for the guest, so every server's new arm compiles, the compositor's and the supervisor's included.Negative controls: eleven checked patches of
sdk/toyos/src/admission.rs, run at the head (patches and logs). Each was applied, built and run againstcargo test -p toyos --lib admission, then restored in the same run. The baseline passes 18 of 18, and each patch fails at least the tests named:m1-no-sharelets a key past its share.a_key_at_its_share_is_refused_while_another_key_is_takenfails.m2-drop-before-readlets a connection go on its limit unread.a_request_that_is_whole_is_served_on_a_pass_past_its_limitfails.m3-fresh-limitwaits a fresh limit.the_wait_is_what_is_left_of_the_oldest_limitfails.m4-accept-past-a-full-tableaccepts without asking the room.a_full_table_accepts_nothing_and_its_port_keeps_the_connectionfails.m5-a-known-key-said-againsays a key held in a slot again within its limit. The refusal-bound and malformed-frame tests fail.m6-every-key-past-the-slots-said. The test past the slots fails.m7-a-count-never-said-alonenever says the count on its own.a_count_no_line_carries_is_said_on_its_own_a_limit_after_the_last_linefails.m8-no-wake-for-a-countgives no wake for an owed count. The same test fails, onleft.m9-the-shared-line-forgets-its-key.a_key_said_on_the_shared_line_is_said_once_a_limitfails.m10-the-shared-line-never-renews, review r3's S2 mutation. The test past the slots fails.m11-a-count-spends-no-shared-line.a_count_said_on_its_own_spends_the_shared_linefails.Guest rows
startup_failurewhile GitHub Actions was degraded.The six rows cover:
netstack_holders_exported_to_children;a_link_ends_under_a_held_stream_and_comes_back;loginandlauncher:session_lifecycleanda_launch_gives_each_unsent_stdio_slot_a_null;a_window_is_drawn_at_the_scale_and_relaid_at_the_nextandthe_opening_exchange_with_qemus_agent_completes.No row reaches a full table or the line bound, which rest on the module's tests.
A green run keeps no serial logs, and its job log carries no server output. So whether a server said a let-go or refusal line for a test's own client cannot be read from a guest run; the owed metal boots' logs can show it.
No row or metal job runs filepicker. Its conversion rests on the module's tests, its host compile and lint, and the image build.
What still holds from 602db29, and why
cargo clippy -p toyos-desktop --all-targets(-D warnings) andcargo test -p toyos-desktop(logs): since then nothing toyos-desktop builds or calls has changed.The host tests of diskserver, fileserver and netstack last ran at 602db29 (logs). Since then each server has gained only log arms in its admission loops, which compile and lint at the head.
The full
cargo run -- --ci hostat 942e9fa (log) still covers what this branch does not touch. Its one red outside the branch,llvm::tests::keyed_and_built, is recorded inissues/the-llvm-environment-test-breaks-git-on-macos.md, andbuild/is unchanged.Owed on metal
Both metal benches are down, and metal is no landing condition for this PR. What is owed is recorded in
issues/one-program-that-fills-a-servers-table-holds-every-other-client-out.md:boot:testcases(itstestcasesandtestcases-watchdogimages) andboot:shared-debug.disk driver … (label) connectedfor each driver.The images were staged at 602db29 and are deleted (staging log).
Risks
slotskeys within a limit, all newer keys share one line a limit, so all newcomers but one are counted rather than named. A count says how many went unsaid, not whose, and is said at the latest a limit after the line before it.🤖 Generated with Claude Code