Repository navigation
A withdraw names again every waiting read it takes a page from - #1233
Conversation
`SYS_PAGED_READ` named a reader's absent range in one notice and, while the reader stayed linked, never again for the same range. A page supplied, the reader woken, and the page withdrawn before the reader's retry left the reader asking for the range it had already asked for: no notice went out, the pager believed it answered, and the reader waited for ever, reported overdue. Found reading the paged object for 7b's design review. The request's bookkeeping is now `kernel::paged::Want`, pure and host-tested: a range is named once while its reader waits, again where it moved, and again where a withdraw takes back a page of it after its notice. `withdraw` marks the requests it meets and posts the pager. A withdraw is the one removal whose pages end absent; 7b's revocation will end the same way, through the same mark. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…it go 7b step 2, first part: the paged index's revocation, before any space maps a paged object. A withdraw, failure, shrink or the object's end now marks a page revoking instead of removing it. A revoking page reads as absent, takes no supply (answered present) and no write, keeps its slot and its frame, and its dirty count goes at once. One walk per object visits each mapper (`kernel::paged::Mappers`) by epoch; a removal that finds a walk running is taken by another pass of that walk, known to its caller by the pass's epoch; a mapper linked during a pass maps nothing revoking and is passed by. The walk's end gives each frame out with what its page became: absent inside the object, failed, or removed past the size or with the object. The kernel's removals go through it. No space maps a paged object yet, so each walk ends in the hold of the lock that began it; the re-report of #1233 moves to the walk's end, for the pages that end absent. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review: #1233 at ab4ba05 (round 1)Architecture. The fix is in the right place. The request's bookkeeping becomes a pure
Withdraw interleavings (verified by reading). Every
Both re-report. A reader still parked, which never reached its retry, is covered as well; it stranded the same way on main. Each request owes at most one notice, however many withdraws come before the server reads it. Duplicates do occur, so "no duplicate a pager must tolerate" does not hold. Two cases:
The file server tolerates both: Model coverage. Nothing models the interleaving. The host test covers RequiredB1. The PR and the commit say a withdraw is "the one removal whose pages end absent", so it is "the one re-report". But a waiter leaves only when
Both routes run under a write or a resize, and each moves the object's version. When a holder's read finds the version moved, the loop already answers Fix. End the wait on that event: add
Effect on step 2. This also falsifies design rev 4 §3's sentence "a fail or shrink re-reports nothing … its waiters are woken to be refused or answered". Step 2's fault waiters have no read version, so they need a re-report on a grow, or an epoch the waiter compares. Step 2's model must also let a grow fall between a removal's wake and the waiter's re-check. That corrects a detail of the approved design; the orchestrator should decide whether it goes back to the owner. B2. Suggestions
Evidence, and what the pending boot must show
The queued T14
SEND BACK for B1 and B2. After the fix, LAND once the host paged tests and clippy pass at the new head and the T14 boot shows the above. |
…ion moved A waiter left its wait only when its page was no longer absent. A page becomes absent again by two routes besides a withdraw: a shrink and a grow before the woken reader looks (the file server's truncate), and a dirty supply the ceiling refuses over a failed page. Both run under a write or a resize, which moves the object's version, and a holder's read already answers `WouldBlock` once its version moved. So the wait now ends there too (`Pages::wait_over`); afterwards only a withdraw returns a page to absent without moving the version, and that is the one route re-reported. The re-report moves into the pure withdraw: `Pages::withdraw` takes the object's waiters (`Waiters`, which the kernel implements over its request list) and marks every want its removed range meets in the same hold of the lock, so a slot released and reused meanwhile gets no stray notice, and the kernel cannot withdraw without handing its waiters over. A host model (`paged::tests::wait`) plays one holder's read against its pager and a client that withdraws, fails, shrinks, grows and writes: every sequence of six steps, each drained, ends the read. Without the version exit it strands after a supply, a shrink and a grow; without the marking, after a supply and a withdraw. Duplicate notices remain and are accepted: a withdraw marks a request the server has read and not yet answered, or one whose first page is still awaited, and the server answers a request for a present page as a supply. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Response to review r1 (head 43fa982)
|
…ion moved A waiter left its wait only when its page was no longer absent. A page becomes absent again by two routes besides a withdraw: a shrink and a grow before the woken reader looks (the file server's truncate), and a dirty supply the ceiling refuses over a failed page. Both run under a write or a resize, which moves the object's version, and a holder's read already answers `WouldBlock` once its version moved. So the wait now ends there too (`Pages::wait_over`); afterwards only a withdraw returns a page to absent without moving the version, and that is the one route re-reported. The re-report moves into the pure withdraw: `Pages::withdraw` takes the object's waiters (`Waiters`, which the kernel implements over its request list) and marks every want its removed range meets in the same hold of the lock, so a slot released and reused meanwhile gets no stray notice, and the kernel cannot withdraw without handing its waiters over. A host model (`paged::tests::wait`) plays one holder's read against its pager and a client that withdraws, fails, shrinks, grows and writes: every sequence of six steps, each drained, ends the read. Without the version exit it strands after a supply, a shrink and a grow; without the marking, after a supply and a withdraw. Duplicate notices remain and are accepted: a withdraw marks a request the server has read and not yet answered, or one whose first page is still awaited, and the server answers a request for a present page as a supply. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…it go 7b step 2, first part: the paged index's revocation, before any space maps a paged object. A withdraw, failure, shrink or the object's end now marks a page revoking instead of removing it. A revoking page reads as absent, takes no supply (answered present) and no write, keeps its slot and its frame, and its dirty count goes at once. One walk per object visits each mapper (`kernel::paged::Mappers`) by epoch; a removal that finds a walk running is taken by another pass of that walk, known to its caller by the pass's epoch; a mapper linked during a pass maps nothing revoking and is passed by. The walk's end gives each frame out with what its page became: absent inside the object, failed, or removed past the size or with the object. #1233's re-report follows the pages: the walk's end names again every waiter's want a page that ends absent meets, and a withdraw names them for the failed pages it removes at once, which have no frame to revoke; both take the waiters (`Waiters`). The kernel's removals go through it. No space maps a paged object yet, so each walk ends in the hold of the lock that began it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Design rev 5 §3 (after review r1 of #1233): a waiter's wait ends on a write or resize of its object as well as on its page, and a fault, which has no read to answer `WouldBlock`, then asks for its page afresh. The index gains the two pieces the fault path will use: `Object::version`, where a fault's wait begins, and `Want::anew`, a request named in a notice whatever the old one said. The model's parked faults record the version they parked at, look again whether or not their wake was theirs, and ask afresh where the version moved. `a_fault_answered_then_shrunk_and_grown_asks_afresh` plays the strand rev 5 names: a fault's request read and answered, then a shrink and a grow before it looks. Asking the same want instead leaves it parked for ever. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review: #1233 at 43fa982 (round 2, follow-up to ab4ba05)Architecture. The fix is the one review r1 asked for, and it is simpler than r1's head.
Prior findings
The four questionsQ1. Every route that makes a page absent again ends or re-reports the wait. Enumerated from the call sites of
The marking cannot miss a page. Any removed page inside the want's range lies inside the withdraw's range, so the two ranges meet, and The over-marking that remains is the duplicate case the description accepts. The only notice still lost is a pager's own Q2. The model is adequate.
Q3. Not every version move wakes waiters. The version moves in
Q4. Mostly, with one deviation. Rev 5 §3 is implemented in three respects:
The deviation is Q3: rev 5 says "every move of the object's version (a write's end, a resize, a refused dirty supply inside a write) wakes every request of the object". A pager RequiredB3.
Evidence
Overlaps
SEND BACK for B3. After it, LAND once the T14 boot shows the above. B3 needs no further runs beyond a compile. |
The module header says a wait ends on a write or resize of the object, and design rev 5 says every move of the version wakes the object's waiters, but a pager's write, its `end_write` and a dirty supply's write end moved the version without posting `changed`: a holder parked across a pager write slept on until another post or its report deadline. `write_end` and `end_write` now post `changed` once the lock is dropped, and `changed`'s doc lists a write's end. A post only makes a waiter look again at its pure `ready`, so it can end a wait early, never with another answer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Response to review r2 (head 7ce1e83)
|
The module header says a wait ends on a write or resize of the object, and design rev 5 says every move of the version wakes the object's waiters, but a pager's write, its `end_write` and a dirty supply's write end moved the version without posting `changed`: a holder parked across a pager write slept on until another post or its report deadline. `write_end` and `end_write` now post `changed` once the lock is dropped, and `changed`'s doc lists a write's end. A post only makes a waiter look again at its pure `ready`, so it can end a wait early, never with another answer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review: #1233 at 7ce1e83 (round 3, follow-up to 43fa982)Scope. This round checks r2's blocker B3 and the one commit that fixes it: Architecture. This is r2's preferred fix and nothing more. The commit adds two posts of the existing B3: resolvedEvery version move now wakes the object's waiters. In
The syscall layer ( No waiter can be lost.
A holder also cannot park between a write's begin and its end. No self-deadlock. Both posts run with no lock held:
The comments are now true.
The evidence is sufficient. For these two posts, r2 asked for one compile plus inspection. The PR supplies both checks at 7ce1e83, each exit 0 (evidence 142a9a1,
The changed code sits under no A wake test is not needed.
Inspection establishes the claim. Overlap with main. SuggestionS3. For the orchestrator (PR description)
LAND, pending the T14 |
|
Orchestrator: T14 |
…it go 7b step 2, first part: the paged index's revocation, before any space maps a paged object. A withdraw, failure, shrink or the object's end now marks a page revoking instead of removing it. A revoking page reads as absent, takes no supply (answered present) and no write, keeps its slot and its frame, and its dirty count goes at once. One walk per object visits each mapper (`kernel::paged::Mappers`) by epoch; a removal that finds a walk running is taken by another pass of that walk, known to its caller by the pass's epoch; a mapper linked during a pass maps nothing revoking and is passed by. The walk's end gives each frame out with what its page became: absent inside the object, failed, or removed past the size or with the object. #1233's re-report follows the pages: the walk's end names again every waiter's want a page that ends absent meets, and a withdraw names them for the failed pages it removes at once, which have no frame to revoke; both take the waiters (`Waiters`). The kernel's removals go through it. No space maps a paged object yet, so each walk ends in the hold of the lock that began it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Design rev 5 §3 (after review r1 of #1233): a waiter's wait ends on a write or resize of its object as well as on its page, and a fault, which has no read to answer `WouldBlock`, then asks for its page afresh. The index gains the two pieces the fault path will use: `Object::version`, where a fault's wait begins, and `Want::anew`, a request named in a notice whatever the old one said. The model's parked faults record the version they parked at, look again whether or not their wake was theirs, and ask afresh where the version moved. `a_fault_answered_then_shrunk_and_grown_asks_afresh` plays the strand rev 5 names: a fault's request read and answered, then a shrink and a grow before it looks. Asking the same want instead leaves it parked for ever. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main's #1233 ends a holder's wait for an absent page where the object's version moved, and the read there answered WouldBlock. Here a holder's read that meets a write begins again instead, until it met READ_RESTARTS writes; its want of the page would then be the range it already named, which the pager answered before the write made the page absent again, so no notice would name it and the read would wait for ever. A read that begins again now asks anew (Want::anew), and main's model of the wait plays the restart. The pager's write is its hold, so main's tests take and give the write side where they began and ended a write. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…it go 7b step 2, first part: the paged index's revocation, before any space maps a paged object. A withdraw, failure, shrink or the object's end now marks a page revoking instead of removing it. A revoking page reads as absent, takes no supply (answered present) and no write, keeps its slot and its frame, and its dirty count goes at once. One walk per object visits each mapper (`kernel::paged::Mappers`) by epoch; a removal that finds a walk running is taken by another pass of that walk, known to its caller by the pass's epoch; a mapper linked during a pass maps nothing revoking and is passed by. The walk's end gives each frame out with what its page became: absent inside the object, failed, or removed past the size or with the object. #1233's re-report follows the pages: the walk's end names again every waiter's want a page that ends absent meets, and a withdraw names them for the failed pages it removes at once, which have no frame to revoke; both take the waiters (`Waiters`). The kernel's removals go through it. No space maps a paged object yet, so each walk ends in the hold of the lock that began it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Design rev 5 §3 (after review r1 of #1233): a waiter's wait ends on a write or resize of its object as well as on its page, and a fault, which has no read to answer `WouldBlock`, then asks for its page afresh. The index gains the two pieces the fault path will use: `Object::version`, where a fault's wait begins, and `Want::anew`, a request named in a notice whatever the old one said. The model's parked faults record the version they parked at, look again whether or not their wake was theirs, and ask afresh where the version moved. `a_fault_answered_then_shrunk_and_grown_asks_afresh` plays the strand rev 5 names: a fault's request read and answered, then a shrink and a grow before it looks. Asking the same want instead leaves it parked for ever. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…it go 7b step 2, first part: the paged index's revocation, before any space maps a paged object. A withdraw, failure, shrink or the object's end now marks a page revoking instead of removing it. A revoking page reads as absent, takes no supply (answered present) and no write, keeps its slot and its frame, and its dirty count goes at once. One walk per object visits each mapper (`kernel::paged::Mappers`) by epoch; a removal that finds a walk running is taken by another pass of that walk, known to its caller by the pass's epoch; a mapper linked during a pass maps nothing revoking and is passed by. The walk's end gives each frame out with what its page became: absent inside the object, failed, or removed past the size or with the object. #1233's re-report follows the pages: the walk's end names again every waiter's want a page that ends absent meets, and a withdraw names them for the failed pages it removes at once, which have no frame to revoke; both take the waiters (`Waiters`). The kernel's removals go through it. No space maps a paged object yet, so each walk ends in the hold of the lock that began it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Design rev 5 §3 (after review r1 of #1233): a waiter's wait ends on a write or resize of its object as well as on its page, and a fault, which has no read to answer `WouldBlock`, then asks for its page afresh. The index gains the two pieces the fault path will use: `Object::version`, where a fault's wait begins, and `Want::anew`, a request named in a notice whatever the old one said. The model's parked faults record the version they parked at, look again whether or not their wake was theirs, and ask afresh where the version moved. `a_fault_answered_then_shrunk_and_grown_asks_afresh` plays the strand rev 5 names: a fault's request read and answered, then a shrink and a grow before it looks. Asking the same want instead leaves it parked for ever. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…it go 7b step 2, first part: the paged index's revocation, before any space maps a paged object. A withdraw, failure, shrink or the object's end now marks a page revoking instead of removing it. A revoking page reads as absent, takes no supply (answered present) and no write, keeps its slot and its frame, and its dirty count goes at once. One walk per object visits each mapper (`kernel::paged::Mappers`) by epoch; a removal that finds a walk running is taken by another pass of that walk, known to its caller by the pass's epoch; a mapper linked during a pass maps nothing revoking and is passed by. The walk's end gives each frame out with what its page became: absent inside the object, failed, or removed past the size or with the object. #1233's re-report follows the pages: the walk's end names again every waiter's want a page that ends absent meets, and a withdraw names them for the failed pages it removes at once, which have no frame to revoke; both take the waiters (`Waiters`). The kernel's removals go through it. No space maps a paged object yet, so each walk ends in the hold of the lock that began it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Design rev 5 §3 (after review r1 of #1233): a waiter's wait ends on a write or resize of its object as well as on its page, and a fault, which has no read to answer `WouldBlock`, then asks for its page afresh. The index gains the two pieces the fault path will use: `Object::version`, where a fault's wait begins, and `Want::anew`, a request named in a notice whatever the old one said. The model's parked faults record the version they parked at, look again whether or not their wake was theirs, and ask afresh where the version moved. `a_fault_answered_then_shrunk_and_grown_asks_afresh` plays the strand rev 5 names: a fault's request read and answered, then a shrink and a grow before it looks. Asking the same want instead leaves it parked for ever. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Design rev 5 §3 (after review r1 of #1233): a waiter's wait ends on a write or resize of its object as well as on its page, and a fault, which has no read to answer `WouldBlock`, then asks for its page afresh. The index gains the two pieces the fault path will use: `Object::version`, where a fault's wait begins, and `Want::anew`, a request named in a notice whatever the old one said. The model's parked faults record the version they parked at, look again whether or not their wake was theirs, and ask afresh where the version moved. `a_fault_answered_then_shrunk_and_grown_asks_afresh` plays the strand rev 5 names: a fault's request read and answered, then a shrink and a grow before it looks. Asking the same want instead leaves it parked for ever. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…it go 7b step 2, first part: the paged index's revocation, before any space maps a paged object. A withdraw, failure, shrink or the object's end now marks a page revoking instead of removing it. A revoking page reads as absent, takes no supply (answered present) and no write, keeps its slot and its frame, and its dirty count goes at once. One walk per object visits each mapper (`kernel::paged::Mappers`) by epoch; a removal that finds a walk running is taken by another pass of that walk, known to its caller by the pass's epoch; a mapper linked during a pass maps nothing revoking and is passed by. The walk's end gives each frame out with what its page became: absent inside the object, failed, or removed past the size or with the object. The re-report of #1233 follows the pages: the walk's end names again every waiter's want a page that ends absent meets, and a withdraw names them for the failed pages it removes at once, which have no frame to revoke; both take the waiters (`Waiters`). The kernel's removals go through it. No space maps a paged object yet, so each walk ends in the hold of the lock that began it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Design rev 5 §3 (after review r1 of #1233): a waiter's wait ends on a write or resize of its object as well as on its page, and a fault, which has no read to answer `WouldBlock`, then asks for its page afresh. The index gains the two pieces the fault path will use: `Object::version`, where a fault's wait begins, and `Want::anew`, a request named in a notice whatever the old one said. The model's parked faults record the version they parked at, look again whether or not their wake was theirs, and ask afresh where the version moved. `a_fault_answered_then_shrunk_and_grown_asks_afresh` plays the strand rev 5 names: a fault's request read and answered, then a shrink and a grow before it looks. Asking the same want instead leaves it parked for ever. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…it go 7b step 2, first part: the paged index's revocation, before any space maps a paged object. A withdraw, failure, shrink or the object's end now marks a page revoking instead of removing it. A revoking page reads as absent, takes no supply (answered present) and no write, keeps its slot and its frame, and its dirty count goes at once. One walk per object visits each mapper (`kernel::paged::Mappers`) by epoch; a removal that finds a walk running is taken by another pass of that walk, known to its caller by the pass's epoch; a mapper linked during a pass maps nothing revoking and is passed by. The walk's end gives each frame out with what its page became: absent inside the object, failed, or removed past the size or with the object. The re-report of #1233 follows the pages: the walk's end names again every waiter's want a page that ends absent meets, and a withdraw names them for the failed pages it removes at once, which have no frame to revoke; both take the waiters (`Waiters`). The kernel's removals go through it. No space maps a paged object yet, so each walk ends in the hold of the lock that began it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Design rev 5 §3 (after review r1 of #1233): a waiter's wait ends on a write or resize of its object as well as on its page, and a fault, which has no read to answer `WouldBlock`, then asks for its page afresh. The index gains the two pieces the fault path will use: `Object::version`, where a fault's wait begins, and `Want::anew`, a request named in a notice whatever the old one said. The model's parked faults record the version they parked at, look again whether or not their wake was theirs, and ask afresh where the version moved. `a_fault_answered_then_shrunk_and_grown_asks_afresh` plays the strand rev 5 names: a fault's request read and answered, then a shrink and a grow before it looks. Asking the same want instead leaves it parked for ever. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…it go 7b step 2, first part: the paged index's revocation, before any space maps a paged object. A withdraw, failure, shrink or the object's end now marks a page revoking instead of removing it. A revoking page reads as absent, takes no supply (answered present) and no write, keeps its slot and its frame, and its dirty count goes at once. One walk per object visits each mapper (`kernel::paged::Mappers`) by epoch; a removal that finds a walk running is taken by another pass of that walk, known to its caller by the pass's epoch; a mapper linked during a pass maps nothing revoking and is passed by. The walk's end gives each frame out with what its page became: absent inside the object, failed, or removed past the size or with the object. The re-report of #1233 follows the pages: the walk's end names again every waiter's want a page that ends absent meets, and a withdraw names them for the failed pages it removes at once, which have no frame to revoke; both take the waiters (`Waiters`). The kernel's removals go through it. No space maps a paged object yet, so each walk ends in the hold of the lock that began it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Design rev 5 §3 (after review r1 of #1233): a waiter's wait ends on a write or resize of its object as well as on its page, and a fault, which has no read to answer `WouldBlock`, then asks for its page afresh. The index gains the two pieces the fault path will use: `Object::version`, where a fault's wait begins, and `Want::anew`, a request named in a notice whatever the old one said. The model's parked faults record the version they parked at, look again whether or not their wake was theirs, and ask afresh where the version moved. `a_fault_answered_then_shrunk_and_grown_asks_afresh` plays the strand rev 5 names: a fault's request read and answered, then a shrink and a grow before it looks. Asking the same want instead leaves it parked for ever. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Problem
A holder's
SYS_PAGED_READcan wait for ever on main. A reader that finds a page absent links one request, and its pager hears of the range in one notice. The reader stays linked across its wait, and asking again for the same range was a no-op. A reader left its wait only when its page was no longer absent. So any route that makes the page absent again after the notice was answered, before the reader looks, strands it: no notice goes out, the pager believes it answered, and the reader waits, reported overdue, until it is killed. Three routes do that:DirtyFull.Found reading the paged object for 7b's design review; review r1 found the second and third routes.
Change
write_end,end_write) posts the waiters' watch as a supply, failure, resize and release already did. Every write and resize moves the object's version, and a holder's read already answersWouldBlockonce it finds the version moved. The wait's ready check is nowPages::wait_over: the page is no longer absent, or the version moved. That covers the shrink-and-grow and the refused-supply routes.Pages::withdrawnow takes the object's waiters (Waiters, which the kernel implements over its request list) and marks every want its removed range meets (Want::withdrawn), in the hold of the pager's lock that removes the pages; the kernel posts the pager's watch when that is news. The kernel cannot withdraw without handing its waiters over, and a slot released and reused between two holds gets no stray notice.kernel::paged::Want(pure) is a waiting reader's range as its pager hears of it: named once while the reader waits, again where it moved, and again where a withdraw takes back a page of it.Duplicate notices happen, and are accepted. A withdraw marks a request the server has read and not yet answered (the file server's
make_roomdoes this whenever it withdraws pages inside a waiting reader's longer range), and a withdraw of a later page of a range whose first page is still awaited names the whole range again. The server answers a request for a present page as a supply, which the kernel answersPresent; the file server counts that as held. Each costs one device read. The design (7b rev 5 §3) accepts that cost, and step 2 builds on both mechanisms: its fault waiters record the version when they park and request afresh when their wait ends on a version change.Kernel only, measured against the merge base b566931: +371 / -44 over two files at 7ce1e83. Production: the pure
Want,Waiters,wait_overand the withdraw's marking about +78, the kernel's wiring +64 / -28. Tests, the wait model among them: about +215.Checks
Targeted under the owner's testing policy: two kernel files, so the kernel's paged tests, the kernel's clippy shapes and the boot that runs the paged rows. The head 7ce1e83 is 43fa982 plus review r2's B3 (two posts of the waiters' watch at a write's end, and a doc line): clippy on the x86-64 kernel shape and the AArch64 kernel check exit 0 there, and by review r2 the host tests, controls and the T14 boot below stand for it, since a post only makes a waiter look again at its pure
ready.cargo test -p kernel --lib -- paged::at 43fa982: exit 0, 22 tests.a_holders_wait_ends_where_its_page_or_its_object_changed: the shrink-and-grow and refused-supply routes end the wait; a withdraw does not, and marks the want.no_interleaving_leaves_a_read_waiting_unasked: a host model of one holder's read against its pager (which reports and answers notices later) and a client that withdraws, fails, shrinks, grows and writes. Every sequence of six steps, each followed by a fair drain, ends the read.after [Report, Answer, Supply, Supply, Shrink, Grow]: the read waits for ever;after [Report, Answer, Supply, Supply, Supply, Withdraw];Want::withdrawnnever news: those two anda_want_is_named_once_while_waited_and_again_after_a_withdrawred.-D warningsat 43fa982:-p kernel --lib --tests --features sched-check,-p kernel --target x86_64-unknown-none(default andboot-actuators,test-actuators),-p kernel --target aarch64-unknown-none-softfloat: exit 0 each.a_pager_s_end_answers_its_waiting_holders_gone(a holder's waiting read, its notice, a whole-object withdraw, the pager's end) anda_served_file_reads_through_its_paged_object(the file server's read through its paged object), run on the metaltestcasesboot alone. A first dispatch named them as QEMU rows and was refused before running anything: no result.testcasesboot with exactly those two jobs: at ab4ba05 it passed, bothexit=0, no "has a read overdue" line, but the fix changes the wait those jobs exercise, so it is restaged at 43fa982 and requested; its readback will be added here.Evidence: https://github.com/ToyOSOrg/evidence/tree/142a9a18a8011fbd414ae316732af426390026a5 (README.md maps each check to its command, revision, exit and log; r3 is this head, 7ce1e83, and r2 covers 43fa982).
🤖 Generated with Claude Code