feat(python/launcher): データ分離した Resonite 多重起動と terminate-all を再実装#71
Merged
Conversation
engine ↔ renderer の Camera frame queue 名が固定 (resonite-io-camera-frames) で、 多重起動すると engine 同士が同名 queue を奪い合い renderer がクロス接続する問題を解消。 - IpcSocketPaths.QueueName を const から env-override 付き static プロパティへ (RESONITE_IO_CAMERA_QUEUE。GrpcHost.ResolveSocketPath と同じ「env → 既定」パターン)。 未設定時は従来固定名に fallback し単一起動は完全後方互換。 - ResoniteIOPlugin.Load() で env 未設定時のみインスタンス固有 token を生成・set。 renderer は engine の子プロセス (RenderSystem.StartRenderer の Process.Start) として env を継承するため両側が同じ token を読む。token set は renderer spawn より前の 最早期 Load() で行う必要がある (OnEngineReady では間に合わない)。 - renderer prebuilt を再生成 (IpcSocketPaths は prebuilt hash の入力)。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
以前撤回 (b8f51c6) した多重起動を、データ分離忘れと IPC 衝突を解消して再実装。 launch: - name=<label> で instances/<label>/{prefix,data,cache,logs} を自動採番し、 WINEPREFIX / -DataPath / -CachePath / -LogsPath を分離 (明示値 > ラベル自動 > 既定)。 RESONITE_IO_CAMERA_QUEUE にラベル由来 token を注入し Camera queue を分離。 - name 無しは従来の単一起動挙動を温存 (global already-running guard)。 - name 指定時は per-label lock (instances/<label>/launch.lock) で同ラベル二重起動のみ拒否。 - _wait_for_single を _wait_for_new (PID 集合差分) に戻し、他インスタンス稼働中でも 本 launch が spawn した個体だけを返す。label はサニタイズ (path traversal 防止)。 - CLI: resoio launch --name <label>。 terminate-all: - terminate_all() を list[LaunchResult] で復元 (engine と子 renderer をペアリング、 孤立 renderer も kill)。単一 terminate は pid-only のまま不変。 - 新コマンド resoio terminate-all (--format human|json)。 real OS プロセス (mock 禁止) で _wait_for_new / ラベル分離 / lock guard / terminate_all / CLI 配線を検証。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
real な 2 インスタンス (--name a / --name b) を別 WINEPREFIX/DataPath で起動し、
各 engine の resonite-{pid}.sock へ個別接続して ping + Camera screenshot を撮り、
両方がフレームを返す (Camera queue がクロストークしない) ことを検証。最後に
terminate-all で全インスタンスが host から消えることを確認する @mark_e2e シナリオ。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
docs/cli.md の launch/terminate 節と例に --name によるインスタンス分離、別 socket ターゲティング、terminate-all を追記。CHANGELOG の Added に多重起動と terminate-all を再掲。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…i-instance-launch
Member
Author
✅ e2e 多重起動を実機で実行 (green)mod を再デプロイ (engine + renderer plugin、token 変更込み) のうえ 検証できたこと:
実行ログ抜粋: |
--data-path を別々に渡せば多重起動できるため、PID 検出で launch を拒否するのは誤り だった。launch は「既に起動中」で二度と拒否しない: - name=None の global already-running guard を削除。 - per-label lock guard (launch.lock + _reject_if_instance_running / _write_instance_lock / _instance_lock_path) を削除。--name はデータツリーの自動採番 と Camera queue token のみを担い、起動をゲートしない。同ラベル再起動も許可。 - _resolve_instance_paths は instance_dir を内部算出 (lock 用の外出し不要に)。 - docs/cli.md・CHANGELOG を「多重起動は --data-path で分離、--name は便宜」に更新。 real OS プロセスで「稼働中インスタンスの隣で named/unnamed launch が拒否されず新規個体を 返す」「同ラベル再起動が通る」を検証。撤回したガードのテストは削除。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Member
Author
🔧 設計修正: 多重起動ガードを全廃レビュー指摘を反映し、PID 検出による launch ガードを global / per-label とも全廃しました。
|
`resoio launch` を --name なしで起動すると Renderer/Engine 起動直後にフリーズする 問題と、`resoio terminate-all` が単一インスタンスを 2 つ (pid=0) と誤判定する問題を 修正する。両方とも engine↔renderer が共有する Camera IPC queue token に起因する。 - フリーズ: --name なしの launch は token を注入せず、engine mod が runtime に 自己生成していたが、runtime set した env は Wine 子プロセス (renderer) に継承 されず両者が別 queue を掴んでいた。launch() が --name の有無に関わらず一意の token を exec 前に必ず env 注入するようにし、engine と renderer が同じ queue で 合意するようにする (--name が動いていたのは exec 前注入だったため)。 - terminate-all の誤判定: renderer は engine の host プロセス subtree に居ない ことがある (Wine が reparent する) ため process-tree ペアリングが取りこぼし、 1 インスタンスが 2 行に割れていた。engine/renderer が共有する token (/proc/environ) でペアリングし、process-tree を fallback に残す。 env 名を `_CAMERA_QUEUE_ENV` 定数に集約。回帰テスト 2 件追加 (unnamed launch の token 注入 / token によるペアリング)。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… camera を成立させる engine が ResoniteIOPlugin.Load() で runtime に Environment.SetEnvironmentVariable した Camera IPC queue token は、別プロセスとして起動する renderer に伝播しない。このため resoio を介さない直接起動 (Gale/Steam) では engine が Guid token・renderer が固定デフォルト名 を掴んで別 queue になり camera がフリーズしていた (resoio 経由は launcher が exec 前に 注入するので前commitで解決済み)。 self-gen を撤去し、token は常に exec 前に env へ載せる設計へ統一する: - resoio launch 経由: launcher が起動前に instance 固有 token を注入 (済)。 - Gale/Steam 直接起動: 何も set されず engine/renderer 双方が IpcSocketPaths の 固定デフォルト名に fallback して一致する (単一インスタンス)。 - mod/src/ResoniteIO/ResoniteIOPlugin.cs: Load() の self-gen if-block を撤去。 - mod/src/ResoniteIO.RendererShared/IpcSocketPaths.cs: QueueNameEnvVar の remarks を 実態 (launcher 注入 / デフォルト fallback) に更新。renderer prebuilt を再生成。 - python/tests/e2e/gale_launch.py: queue env 無しの mod launch が camera frame を返す e2e 回帰シナリオを追加 (_build_command(camera_queue=None) で Gale 起動を faithful に再現)。 実機 e2e で検証済み: gale_launch / launcher (no-name) / multi_instance すべて green。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
背景
Resonite の多重起動は以前 PR #66 で実装したが commit
b8f51c6で撤回された。撤回理由は (1) data path (WINEPREFIX / DataPath) を分離し忘れて Resonite DB が競合、(2) renderer との Camera IPC queue 名が固定 (resonite-io-camera-frames) で多重起動時に衝突・クロストークする設計欠陥。撤回時は Python launcher のみ巻き戻し、C# 側の IPC 名分離は未着手だった。本 PR はこの 2 つのブロッカーを正しく解消して多重起動を再実装する。
確定済みの方針 (ユーザー合意)
terminate(engine/renderer pid を返す pid-only) は不変。一括停止は新コマンドterminate-allで list を返す。launch --name <label>で~/.resonite-io/instances/<label>/配下に WINEPREFIX / DataPath / CachePath / LogsPath を自動採番・分離。--name無しは単一起動挙動を温存。Summary
C# (mod/camera)
IpcSocketPaths.QueueNameをconstから env-override (RESONITE_IO_CAMERA_QUEUE) 付き static プロパティ化 (GrpcHost.ResolveSocketPathと同じパターン)。受信側RendererFrameInterprocessReceiver/ 送信側FrameSenderは参照のみで自動分離。ResoniteIOPlugin.Load()で env 未設定時のみインスタンス固有 token を生成・set。renderer spawn より前の最早期Load()で行う必要がある (decompiled でRenderSystem.StartRendererがOnEngineReadyより前と確認)。just renderer-prebuild)。Python (launcher / CLI)
launch(name=...): ラベル配下に 4 dir を自動採番、prefix/data_path/cache_path/logs_pathを分離 (明示値 > ラベル自動 > 既定)、token env 注入。--name無しは global already-running guard を温存、--name時は per-label lock で同ラベル二重起動のみ拒否。_wait_for_single→_wait_for_new(PID 集合差分) に復元 (他インスタンス稼働中でも本 launch の個体だけ返す)。terminate_all() -> list[LaunchResult]を復元 (engine と子 renderer をペアリング、孤立 renderer も kill)。新コマンドresoio terminate-all(--format human|json)。resoio launch --name <label>。docs
docs/cli.md/CHANGELOG.mdに多重起動・terminate-allを反映。Test plan
just rungreen (format → gen-proto → build → test → type → check-renderer-prebuilt)IpcSocketPaths.QueueNameの env-override (real env)_wait_for_new/ ラベル分離 / per-label lock guard /terminate_all/ CLI 配線・出力@mark_e2e,tests/e2e/multi_instance.py): real 2 インスタンスを別 socket で ping/screenshot → Camera クロストーク無し →terminate-allで両方 gone ※実機 Resonite 環境で要実行🤖 Generated with Claude Code