re_match_program: one whole-program build per worktree at a time (a lock file); concurrent runs collided
This commit is contained in:
1 parent
5211cee9c4
commit
453434c2b2
1 file changed
+46
-9
+46
-9
@@ -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
|
||||
Reference in new issue
Block a user