re_match_program: one whole-program build per worktree at a time (a lock file); concurrent runs collided

This commit is contained in:
KeelrLeek committed 2026-10-05 17:29:36 -05:00
1 parent 5211cee9c4
commit 453434c2b2
1 file changed
+37
+37
View File
@@ -598,6 +598,10 @@ def match_program(config, targets=None, decomp=None):
text=True, encoding="utf-8", errors="replace", timeout=timeout,
cwd=str(Path(script).parent),
creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0))
# one build per worktree at a time: two runs on the same checkout write the same build folder
lock_dir = Path(config.get("debugger_temp") or tempfile.gettempdir())
lock = lock_dir / ("ltcg-" + re.sub(r"[^A-Za-z0-9]+", "_", str(decomp or "main")) + ".lock")
with _exclusive(lock, wait_seconds=1800):
built = run(["build", *extra], 1800)
if built.returncode != 0:
raise HarborError("the whole-program build failed: " + (built.stdout + built.stderr).strip()[-800:])
@@ -642,3 +646,36 @@ def behaviour_check(config, targets, decomp=None, precision=24):
rows = json.loads(out.read_text(encoding="utf-8"))
keep = ("address", "name", "verdict", "reason", "weak", "counts", "notes")
return [{k: r[k] for k in keep if k in r} for r in rows]
class _exclusive:
"""A lock file shared between processes: wait for it, hold it, remove it (stale after two hours)."""
def __init__(self, path, wait_seconds=1800):
self.path, self.wait = Path(path), wait_seconds
def __enter__(self):
import time
deadline = time.monotonic() + self.wait
while True:
try:
fd = os.open(str(self.path), os.O_CREAT | os.O_EXCL | os.O_WRONLY)
os.write(fd, str(os.getpid()).encode())
os.close(fd)
return self
except FileExistsError:
try:
if time.time() - self.path.stat().st_mtime > 7200:
self.path.unlink()
continue
except FileNotFoundError:
continue
if time.monotonic() > deadline:
raise HarborError(f"another whole-program build on this worktree is still running ({self.path.name}); try again later") from None
time.sleep(5)
def __exit__(self, *exc):
try:
self.path.unlink()
except FileNotFoundError:
pass