Repository navigation
perf: run native clipboard writes off the client event loop - #58
Merged
Merged
Conversation
pbcopy, wl-copy and xclip are separate processes and the client waited for each on its event loop, 14-21 ms per copy on macOS. Native writes now run in order on one worker thread; a failed write returns to the loop as OSC 52, since only the loop writes stdout, and later writes queue behind it so an older copy cannot land last. SSH, WSL and VS Code sessions keep writing OSC 52 inline. Shutdown briefly waits for queued writes. refs #55
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 51 minutes. View limit detailsLimit details: You’ve used the included review currently available. Review configuration: ⚙️ Run configuration
📒 Files selected for processing (10)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Only the newest copy matters to the clipboard, so a burst of copies no longer runs one native tool per write in the background, and a detach in the middle of a burst keeps the newest copy. Shutdown warns when fallback copies are still waiting for the loop. refs #55
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.
コピーのたびに client のメインループがネイティブのクリップボードツール(macOS では
pbcopy)の終了を同期で待っていたのを、専用のワーカースレッドへ移す。何が問題だったか
crate::selection::write_osc52_bytesは client のメインイベントループ上でplatform::write_clipboardを呼んでいた。macOS ではpbcopyを spawn し、stdin に書き、child.wait()で終了まで待つ。実測で 1 回 14〜21 ms。ClientShellAction::ClipboardWriteの dispatch と、ServerMessage::Clipboardを受けたforward_clipboard)。待っている間は入力処理もフレーム描画も止まる。Claude Code でのコピーでは Claude Code 自身のpbcopyと合わせて 2 回走るので、toast 直後のもたつきの一因になっていた。wl-copyの起動待ちでは最大 100 ms ループ上で待っていた。変更
src/client/clipboard_writer.rs(platformcfgなし)にClipboardWriterを追加した。clipboard-writerスレッド 1 本と FIFO(std::sync::mpsc)で順に実行する。ループはキューに積むだけで待たない。ClientLoopEvent::ClipboardFallback(bytes)でループへ返し、ループが OSC 52 を書く。stdout に書くのはループだけだから。pending_fallbacks > 0)は、後続のコピーもネイティブを試さずフォールバック側に並べる。古いコピーが後から OSC 52 で上書きする逆転を防ぐため。待ちが解消すれば次のコピーからネイティブに戻る。OpenClipboardは他アプリが握っているだけで一時的に失敗し、OSC 52 は conhost や Terminal.app では無視されるため。should_prefer_osc52())ではワーカーを作らず、従来どおりループ上で OSC 52 を即書きする。判定は client 起動時に 1 回だけ行う(環境変数も WSL 判定もプロセスの途中で変わらない)。ClientStateにclipboardを持たせ、dispatch_client_shell_actionsに&ClipboardWriterを渡す。forward_clipboard(&ClipboardWriter, data)は「キューに積めたら true」を返す。toast を出すタイミングは今のフォークと同じ。event_tx/event_rx)の生成をClientStateの初期化より前へ移した。ワーカーが自前の sender を持つため。src/selection.rs:write_osc52_bytesをwrite_osc52_sequence(stdout に OSC 52 を書くだけ)に置き換え、should_prefer_osc52をpub(crate)にした。名前を残して中身だけ変えると、将来 upstream 側で増えた呼び出しが黙ってネイティブ経路を失うので、名前ごと置き換えた。検証
cargo fmt --check、cargo clippy --all-targets --locked -- -D warnings(macOS): OKcargo clippy --bin herdr --locked --target x86_64-pc-windows-msvc -- -D warnings: OKcargo nextest run --no-fail-fast: 3353 tests run, 3351 passed, 2 failed。live_handoff::live_handoff_preserves_pane_process_ioとlive_handoff::live_handoff_keeps_unmanaged_agent_name_bound_to_saved_session。FORK_WORKFLOW.md§0 の環境依存の既知失敗で、herdr serverだけを起動するテストなので client の変更は関与しない。scripts.test_ui_hot_path_architecture: OKflushを明示的に呼ぶ):native_writes_run_in_order_on_the_worker_thread— Flush で区切った書き込みが、その順にclipboard-writerスレッド上で実行されるwrites_queued_behind_a_slow_native_write_collapse_to_the_newest— 1 件目のネイティブ書き込みが止まっている間に 2〜5 件目と Flush を積むと、ネイティブは 1 件目と 5 件目だけで呼ばれ、Flush は 5 件目を書いてから完了するfailed_native_write_holds_later_writes_until_its_fallback_is_written— 失敗時のフォールバック、pending 中の後続はネイティブを試さないこと、解消後にネイティブへ戻ることpreferred_osc52_writes_inline_without_native— OSC 52 優先時は即書きし、ネイティブを呼ばないflush_waits_for_a_slow_native_write/flush_times_out_on_a_hung_native_write— Flush の待ち合わせとタイムアウト、止まった書き込みが戻ればワーカーが動き続けることforwarded_clipboard_is_decoded_and_queued/invalid_clipboard_payload_writes_nothingclient/tests/mod.rsのforward_clipboard_uses_local_clipboard_pathは upstream eef1914 と同じ形で削除した(次の同期で衝突させないため)。VSCODE_IPC_HOOK_CLIで OSC 52 経路を強制するので、ループ上の即書き経路と、コピー・toast の回帰を確認した。copy --rounds 3 --text-mode static: 9/9 matchedcopy-click --rounds 6: 6/6 focused、p50 12.5 ms / max 15.3 msapp --rounds 6 --drag: 6/6 focus switches、p50 13.0 ms / max 15.6 msreal --rounds 4 --panes 3 ...: 0 failures、focus shown p50 14 ms / max 19 mscargo fmt --check、cargo clippy --all-targets -D warnings(macOS)、Windows ターゲットのcargo clippy --bin herdr -D warnings: OK--stress-count 30: 30/30 iterations passed-E 'test(/client::/)'): 561 passed残課題・注意点
write_osc52_bytesを bool を返す形にし、クリップボードが同じ内容なら書かない(clipboard_text_matches、実装は Windows のみ)。本 PR でwrite_osc52_bytesを消したので、selection.rsとclipboard_forwarding.rsが衝突する。falseを返して toast を出さない。キューに積んだ時点で toast を出す本 PR の形とは合わないので、その扱いは同期時に決める必要がある。dispatch_client_shell_actionsのevent_tx引数がscheduled_activationに変わっているので、引数リストも小さく衝突する。pbcopyが先に届きうる。人の操作間隔では起きない。pbcopy/xclipが exit しない)、以後のコピーはそのセッションの間ずっと、その後ろで黙って待つことになる。従来はクライアント全体が固まっていたので、それよりは軽い(終了時の待ちも 500 ms で打ち切る)。タイムアウトや kill は入れていない。入れるには upstream 管理の platform コード(run_clipboard_command)を変える必要があるため。OpenClipboardが一時的に失敗したとき、そのコピーに加えて次のコピーも失われうる。1 件目のフォールバックをループが書く前に次のコピーがワーカーに取り出されると、pending 中としてネイティブを試さずフォールバックに回るため。refs #55
🤖 Generated with Claude Code