fix(settings): 設定の保存の排他を錠前ファイルに移す - #82
Merged
Merged
Conversation
limit7412
marked this pull request as ready for review
August 31, 2026 09:16
Base automatically changed from
claude/remaining-issues-priority-m7daxq
to
master
August 31, 2026 09:17
Global\ の名前は作るのに権限が要る。作れない相手や、既にある物を開けない相手は セッション内だけの名前へ落ちる。落ちた側は誰とも待ち合わせていないのに、 待ち合わせているつもりで進む。設定の保存でこれが起きると、両方が同じ内容を 読んでから書き戻し、片方の更新が黙って消える。 名前空間の話を持ち込まなければ、この分岐そのものが要らない。設定ファイルの隣に 置いた錠前ファイルを共有無しで開く。ファイルハンドルの共有の指定は計算機の中で 一意に効くので、どの対話セッションから開いても同じ 1 つを取り合う。権限も要らない。 持ったまま落ちてもハンドルは OS が閉じるので、次の相手が待たされ続けることもない。 更新の適用と取得のロックは Mutex のままにした。あちらで待ち合わせに失敗しても 失うのは更新のやり直し 1 回で、利用者の入力ではない。差し替えには適用の経路を まとめて書き換える必要があり、ここで持ち込む重さではない。 Refs #81, #52
limit7412
force-pushed
the
claude/global-mutex-integrity-label
branch
from
August 31, 2026 09:17
7493791 to
3ccaf4d
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 74937918ca
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
錠前ファイルへ移った結果、直前の版 (パス別の Global\ の Mutex を取る) とこの版が 別々の対話セッションで同じ settings.json を保存すると、直列化するものが無くなる。 残していた接頭辞の無い Mutex はセッションごとに別物なので、そこでは繋がらない。 守れる範囲が違う 2 つを、どちらも取る。接頭辞の無い名前は対話セッションの中で、 Global\ の名前はセッションをまたいで効く。取る順は 「Legacy → CrossSession → 錠前ファイル」で固定した。旧版が取るのはこの並びの 先頭からの一部でしかないので、順の食い違いは起きない。 いま出ている prerelease は 9701f0a のもので、パス別の Global\ の Mutex は 入っていない。ただしこの repo は master から短い間隔で prerelease を切るため、 入った版が出回る可能性は十分ある。旧版が行き渡ったら両方消してよい。 Refs #81
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.
issue #81 への対応。#80 に積んでいたが、#80 がマージされたので base は
masterである。直すもの
GlobalMutex.CreateはGlobal\の名前で Mutex を作り、作れなければセッション内だけの名前へ落ちる。落ちた側は誰とも待ち合わせていないのに、待ち合わせているつもりで進む。 設定の保存でこれが起きると、両方が同じ内容を読んでから書き戻し、片方の更新が黙って消える。#52 で直したかったものが、この経路だけ残る。#81 の前提を見直した
issue には「昇格したプロセスが作った物は mandatory label で開けない」と書いた。これは #80 の時点で半分外れている可能性が高い。
Mutex.OpenExistingが求めるのはSynchronizeとModifyである。mutant の generic mapping ではMUTANT_QUERY_STATEが read、SYNCHRONIZEが execute に写る。どちらも write ではないので、既定のNO_WRITE_UPでは下の integrity level からでも通る。断られるのはnew Mutexの方で、こちらはWRITE_DACなどを含む全権を求める。#80 でOpenExistingを足した時点で、label だけが理由の相手とは待ち合わせられているはずである。それでも残るものの方が大きい。
Global\の名前を作るにはSeCreateGlobalPrivilegeが要る。持たない相手は物を作れず、OpenExistingも「物が無い」で返るので、結局セッション内だけの名前へ落ちる。しかも全員が落ちるので、誰もGlobal\の物を作らない。対話セッションをまたぐ排他が、黙って丸ごと無くなる。 label の話より広く、しかも静かである。label に P/Invoke で触る案は、この見立てだと的も外している。採らなかった。
直し方: 名前空間の話を持ち込まない
設定ファイルの隣に置いた錠前ファイル (
settings.json.lock) をFileShare.Noneで開く。Global\のような名前空間の分岐がそもそも無い取れなかったときは、これまでどおり諦めてプロセス内ロックだけで best-effort で書く。待ち続けると呼び出し元がその間ずっと止まるためである。
旧版と待ち合わせるための旧名の Mutex (33d7494) はそのまま残し、「旧名 → 錠前ファイル」の順で取る。錠前ファイルを取るのはこの版だけで、この版は必ず旧名から取るので、順の食い違いは起きない。
受け入れるもの: 更新の適用と取得のロック
UpdateStageの 2 つはGlobalMutexのままにした。失うものが違う。 あちらで待ち合わせに失敗しても、失うのは更新のやり直し 1 回である。
.oldも設定も残る。設定の保存で失うのは利用者が入力した値そのものである。差し替えの重さも違う。 適用のロックは
CrossProcessLock(Mutex のスレッド親和性のために所有スレッドを立てている) と CLI のApplyLockHolder(返して取り直す) が噛んでいる。錠前ファイルにはスレッド親和性が無いのでどちらも単純になるが、#74 / #75 で詰めたばかりの適用の経路をまとめて書き換えることになる。ここで持ち込む重さではない。#81 はこの分を残して開けておく。
検証
Refs #81, #52
Generated by Claude Code