Repository navigation
Keep the app answering when a command holds the core, and save ink on text - #22
Merged
Merged
Conversation
A page's geometry is written rounded to 0.01 (frames, an anchored ink block's dx/dy, the view, paper and background, column widths), but the readers kept the value an edit gave them. The interface measures where ink sits on its words in screen pixels, so its offset has more decimals than the file keeps, and every save's read-back check (spec 17.7, S5) then failed with "blocks differs after reading back": the page never reached disk, a retry ran every 30 s, each one left another orphan .onk segment in ink/, and the title bar said Saved (beta 4, T2-1). Every reader of a geometry value now rounds it the way the writer does (file reads, edits, and resolved frames alike), so the page in memory is the page its bytes read back as. A save that fails after its ink segment was written removes the segment again. The title bar follows the core's save events: "Couldn't save" while a page's last save failed, "Saved" only once the core saves it. The components vitest project takes its port from OPENNOTE_VITEST_PORT, so parallel runs on one machine don't collide. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Setting the same style twice is a repeat, not a change, now that notebook.json's styles are read with the writer's rounding (T2-1 follow-up). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Beta 4's T2-3: once one command never returned, every later core command parked a runtime worker and an IPC request behind it; the webview carries only a few IPC requests at once, so soon nothing reached Rust at all, Close and the log included, and the status said "Saving" for the rest of the session. - Every core command runs on a blocking thread, and waits at most 15 s for the core before failing as `coreBusy`, naming the holder, so the IPC channel stays open. - Journal opens wait at most 20 s for the journal thread; a page whose journal doesn't answer runs degraded instead of holding the core. - The exit polls the core lock up to the flush time and goes on without the final save when the core is held. - A watchdog names the command that has held the core for 10 s, logs it, and sends core:stalled / core:responsive; the title bar shows "Not responding" (announced) instead of "Saving" forever, also on a `coreBusy` error. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A command that waits for the core slept 10 ms between try_lock polls, so a contended command paid up to 10 ms extra and a waiter could be passed by newer arrivals for as long as it waited. The commands now queue at a door (a flag and a condition variable): a waiter sleeps until the holder leaves and starts the moment it does, and still gives up as coreBusy after COMMAND_WAIT. The journal open timeout drops from 20 s to 10 s, below COMMAND_WAIT (15 s), so a stuck journal open gives up before the commands queued behind it do, and the two constants say how they relate. The interface's coreBusy path has tests: a busy page_apply keeps the text and its riders unsent, shows the "not kept" toast and sends them with the next flush (textSync.test), and marks the core stalled before the watchdog's event (tauri.test). One useless Cow conversion clippy flagged is gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CI's checks on the pull request raised what the lane's own files owed: the bridge had grown past the 800-line limit, rustfmt wanted eight of the lane's files reflowed, and seven comments were too long or missed a serial comma. The constants, the door, the guard, the holder and the watchdog now live in core_bridge/held.rs, which reads as one thing: how a command takes the core, how long it waits, and who says when one holds it too long. The bridge keeps the commands. The lane's files are formatted as CI's rustfmt wants them, and the comments say the same in shorter sentences. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Owner
Author
|
Merged through beta in the Beta 5 integration (beta head 9e2ba25); GitHub marked this PR merged when beta was pushed. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes beta 4's T2-1 (ink on text never saved) and contains T2-3 (the core stopped responding and Close did nothing).
T2-1: ink on text never saves
Every geometry reader (file reads, edits, frames, notebook styles) now rounds the way the writer does (0.01; crops 6 decimals; line height 2), so the save's read-back check passes. A failed save forgets its orphan
.onksegment, and the title bar shows "Couldn't save" fromcore:save-failedinstead of "Saved".T2-3: the core stops responding, Close does nothing
What the T2 profile and log show: the core answered until 14:48:54, then nothing at all was logged, not even the exit handshake's warning that a Close must produce within 3 s. So the webview's IPC was dead, not only the core: every core command took the core's one lock unbounded on an async worker, so once one command never returned, each later one parked a worker and an IPC request until the channel was full. (Tauri 2.12 sends Windows invokes as
fetch()on theipccustom protocol, so Chromium's per-host connection cap plausibly applies; not measured.)The command that held the core at 14:49 is not identified. Every lock order in the core was checked by hand and is consistent; the only unbounded cross-thread waits under the core lock were the journal opens, which the inline rename and the new page's first edit both go through. This PR is containment plus diagnosis, so the next occurrence names its holder:
OPEN_TIMEOUT(10 s) and the page runs without a journal (spec 20.12); the worker detaches a journal nobody waits for.CoreBridge::run), so a wait never parks the async runtime.COMMAND_WAIT(15 s, longer thanOPEN_TIMEOUTso a stuck open gives up before the commands behind it) at a Condvar door and then fails with codecoreBusy, naming the holder. The failed request completes, so the IPC channel stays open for Close, the log and everything else.core:stalled/core:responsive); the title bar shows "Not responding", also as soon as any command fails ascoreBusy.coreBusypage_apply is a non-resync error: the text stays unsent with its riders, the "not kept" toast shows, and the next flush sends it.Known limits
coreBusymessage exist to catch it next time.bridge.run(external.rsnotebook_roots, backup.rs) now get acoreBusyerror after 15 s instead of blocking forever; both handle it.Verification
🤖 Generated with Claude Code