fix(#295): lyra track が now-playing 不在時にハングする問題を解消#301
Conversation
lyra track が now-playing セッション不在時に出力せず無限に待つ問題を修正。
根本原因: DarwinGateway.runStreaming が DispatchQueue.global() の GCD
スレッドから continuation.yield() していた。lyra track は
AsyncRunnableCommand が async→sync を DispatchSemaphore.wait() でブリッジし
呼び出しスレッドを占有するため、GCD スレッドからの yield が cooperative
pool の消費側 (await iterator.next()) を再開できず初回行が届かずハングした。
daemon は NSApplication.run() が main ランループを回すので露見しなかった。
テストモック StreamingGateway が Task {}(cooperative)で yield して再現し
なかったのも同根。
修正: 行配送を Task { for await line in reader.bytes.lines } に置き換え、
cooperative pool 内でストリームを供給。GCD→cooperative の越境を消し、動作
していた daemon/モックと同じ実行文脈に揃える。FileHandle.AsyncBytes.lines
は EOF で末尾の未終端行も emit するため行分割契約は不変。
- runStreaming 配送の順序・first-line 即時性・finish をユニットテストで検証
(.timeLimit で万一の再ハングが CI を止めないよう保険)
- 実環境シナリオ(音楽不在での lyra track → 空 JSON 即返し)は VM ハーネスで確認
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
Warning Review limit reached
Next review available in: 58 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
✨ Finishing Touches🧪 Generate unit tests (beta)
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 |
There was a problem hiding this comment.
Pull request overview
lyra track が now-playing セッション不在時に出力ゼロのまま待ち続ける (#295) 問題を、DarwinGateway.runStreaming の実行コンテキストを GCD スレッドから Swift Concurrency の cooperative pool (Task) に寄せることで解消する PR です。CLI の one-shot 実行でもストリームの初回行が確実に配信されるようにし、デッドロックを回避します。
Changes:
DarwinGateway.runStreamingの行配送をFileHandle.AsyncBytes.lines+Taskに置換し、cooperative pool からcontinuation.yieldするよう変更runStreamingの期待するストリーム挙動(初回行の即時配送・順序・プロセス終了時 finish)を検証するテストを追加- バージョンを patch bump(2.19.0 → 2.19.1)
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| Tests/DarwinGatewayTests/DarwinGatewayRunStreamingTests.swift | runStreaming の即時配送・順序・終了時 finish をカバーする回帰テストを追加 |
| Sources/DarwinGateway/DarwinGateway.swift | runStreaming の読み取り/配送を Task 上に移し、CLI one-shot でのハング要因を除去 |
| Sources/VersionHandler/Resources/version.txt | fix に伴う patch bump |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| continuation.onTermination = { _ in | ||
| task.terminate() | ||
| readerTask.cancel() | ||
| } |
There was a problem hiding this comment.
onTermination に try? reader.close() を追加しました。task.terminate() の直後・readerTask.cancel() の前に置くことで、pending な await lines.next() が EOF として自然に返り、Task が自力で終了できるになっています。f3bb360
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Copilot 指摘 (#3523226542) への対応。 Pipe の読み取り FileHandle を deinit 任せにせず、continuation.onTermination で `try? reader.close()` を呼ぶことで FD を確定的に解放する。 close は readerTask.cancel() の前に置くことで、pending な `await lines.next()` が EOF として自然に返り、Task が自力で終了できるようにしている。
概要
lyra trackが now-playing セッション不在時に何も出力せず無限に待ち続ける問題 (#295) を修正する。one-shot コマンドなので、空の JSON を即返して終了するのが期待動作。根本原因
DarwinGateway.runStreamingがDispatchQueue.global()の GCD スレッドからcontinuation.yield()していた。lyra trackはAsyncRunnableCommandが async→sync をDispatchSemaphore.wait()でブリッジして呼び出しスレッドを占有するため、GCD スレッドからの yield が cooperative pool の消費側 (await iterator.next()) を再開できず、初回行 ({"has_info":false}) が届かずデッドロックしていた。NSApplication.run()が main ランループを回し続けるため、GCD → cooperative の通知が処理されていた。StreamingGatewayはTask {}(cooperative pool) で yield するため、この越境が起きず再現しなかった。変更内容
runStreamingの行配送をTask { for await line in reader.bytes.lines }に置き換え、cooperative pool 内でストリームを供給。GCD→cooperative の越境を消し、動作していた daemon / モックと同じ実行文脈に揃えた。FileHandle.AsyncBytes.linesは EOF で末尾の未終端行も emit するため、行分割契約は不変 (既存テストrunStreaming yields lines and flushes final unterminated lineがそのまま green)。AsyncStream<String>では読み取りエラーは意味的に EOF なので、空catch {}ではなくtry?で終端として扱う。onTerminationで子プロセス終了 +readerTask.cancel()。テスト計画
DarwinGatewayRunStreamingTestsを追加 — first-line 即時配送 (lyra track が now-playing セッション不在時に出力せず待ち続ける #295 の核心) / 複数行の順序 / プロセス終了での finish を検証。.timeLimit(.minutes(1))で万一の再ハングが CI を止めないよう保険。swift test1084 件 green (daemon 経路含む回帰なし)。lyra track実行 → 再生中の曲を即返し (happy-path 確認、回帰なし):lyra track → 空 JSON 即返しは VM ハーネス (lyra-vm-harness.sh exec $VM -- lyra track) で最終確認する。備考