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
+46 -9
+46 -9
View File
@@ -598,15 +598,19 @@ 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))
built = run(["build", *extra], 1800)
if built.returncode != 0:
raise HarborError("the whole-program build failed: " + (built.stdout + built.stderr).strip()[-800:])
with tempfile.TemporaryDirectory(dir=config.get("debugger_temp")) as tmp:
out = Path(tmp) / "check.json"
checked = run(["check", *extra, "--json", str(out)] + (["--only", ",".join(wanted)] if wanted else []), 900)
if not out.exists():
raise HarborError("the whole-program check failed: " + (checked.stdout + checked.stderr).strip()[-800:])
rows = json.loads(out.read_text(encoding="utf-8"))
# 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:])
with tempfile.TemporaryDirectory(dir=config.get("debugger_temp")) as tmp:
out = Path(tmp) / "check.json"
checked = run(["check", *extra, "--json", str(out)] + (["--only", ",".join(wanted)] if wanted else []), 900)
if not out.exists():
raise HarborError("the whole-program check failed: " + (checked.stdout + checked.stderr).strip()[-800:])
rows = json.loads(out.read_text(encoding="utf-8"))
counts = {}
for r in rows:
counts[r.get("status")] = counts.get(r.get("status"), 0) + 1
@@ -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