fix: isolate the adversary review-stack lock for parallel tests and non-POSIX platforms
The advisory flock in run_checks.py uses one system-wide lock path (~/.cache/coyote/review-stack.lock). Concurrent runs of the timeout/deadline tests contend on it: the losers sleep in the bounded retry loop, the total deadline (computed before lock acquisition) expires, and commands record 'skipped: deadline' instead of the expected timeout/kill behavior. - run_checks.py: ADVERSARY_STACK_LOCK_PATH env override relocates the lock file (isolation seam, documented alongside the other env seams); every other lock semantic (default path, bounded wait, proceed-unlocked warning, fd held for process lifetime, skip when no commands) is unchanged. - run_checks.py: guard the POSIX-only fcntl import — windows-latest CI has python3 and runs these tests, so the unconditional import crashed the script there; on non-POSIX the lock is now skipped with a stderr WARN, matching the script's documented non-POSIX limitations. - tests: the shared adversary_script_command helper points every spawned script at its own throwaway lock path, isolating the run_checks tests from each other and from any real review running on the same machine. - tests: adversary_run_checks_stack_lock_path_is_env_overridable pins the override (lock file appears at the overridden path across two sequential runs; no lock file is created when no commands are declared).
This commit is contained in:
2 files changed
+88
-4
No files matched your search
@@ -31,7 +31,9 @@ in-script degradation would never fire. Each command gets
|
||||
min(PER_COMMAND_TIMEOUT_SECS, remaining budget); commands the deadline
|
||||
leaves no budget for are recorded as {"cmd", "skipped": "deadline"} entries,
|
||||
so the in-script handling stays authoritative. The env var
|
||||
ADVERSARY_RUN_CHECKS_DEADLINE_SECS overrides the deadline (test seam).
|
||||
ADVERSARY_RUN_CHECKS_DEADLINE_SECS overrides the deadline (test seam), and
|
||||
ADVERSARY_STACK_LOCK_PATH overrides the review-stack lock file location
|
||||
(isolation seam — see acquire_stack_lock).
|
||||
|
||||
Process hygiene: commands run through the shell. On POSIX each command gets
|
||||
its own session (start_new_session) and a timeout SIGKILLs the whole process
|
||||
@@ -51,14 +53,20 @@ GRAPH_STATE_FILE, so an inherited live state file would silently win.
|
||||
import json
|
||||
import os
|
||||
import signal
|
||||
import fcntl
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
|
||||
try:
|
||||
import fcntl
|
||||
except ImportError: # non-POSIX (Windows): advisory flock unavailable
|
||||
fcntl = None
|
||||
|
||||
PER_COMMAND_TIMEOUT_SECS = 900
|
||||
TOTAL_DEADLINE_SECS = float(os.environ.get("ADVERSARY_RUN_CHECKS_DEADLINE_SECS") or 3300)
|
||||
STACK_LOCK_PATH = os.path.expanduser("~/.cache/coyote/review-stack.lock")
|
||||
STACK_LOCK_PATH = os.environ.get("ADVERSARY_STACK_LOCK_PATH") or os.path.expanduser(
|
||||
"~/.cache/coyote/review-stack.lock"
|
||||
)
|
||||
STACK_LOCK_WAIT_SECS = float(os.environ.get("ADVERSARY_STACK_LOCK_WAIT_SECS") or 1200)
|
||||
|
||||
|
||||
@@ -67,7 +75,11 @@ def acquire_stack_lock():
|
||||
well-known path; the probe is instructed to flock the same file around
|
||||
stack boot/teardown). Bounded wait so a hogged lock can never turn into a
|
||||
false red verification: on timeout we proceed WITHOUT the lock and note it
|
||||
on stderr. The fd is held for the process lifetime (released on exit)."""
|
||||
on stderr. The fd is held for the process lifetime (released on exit).
|
||||
On non-POSIX platforms (no fcntl) the lock is skipped entirely."""
|
||||
if fcntl is None:
|
||||
sys.stderr.write("WARN: review-stack lock unsupported on this platform; running unlocked\n")
|
||||
return None
|
||||
try:
|
||||
os.makedirs(os.path.dirname(STACK_LOCK_PATH), exist_ok=True)
|
||||
fd = os.open(STACK_LOCK_PATH, os.O_CREAT | os.O_RDWR)
|
||||
|
||||
Reference in new issue
Block a user