fix(issue-17): review R1 - a finalize wait is measured on the monotonic clock, like bash's sleep
codex, PR #51 round 1. wrapper._sleep computed the span and the remainder with time.time(). The broker re-observation (up to 3 s) and the disk-heartbeat grace (1 s) use it, so a wall clock stepped back during the wait stretched it by the step: on a mocked clock stepped back 10 s, an already completed job was seen after 11 s / 13 s instead of 1 s / 3 s. The old bash ran `sleep 1` / `sleep "$left"`, which do not follow the wall clock. _sleep now measures on time.monotonic(). The deadline (START_TIME + TIMEOUT), the evidence window and event timestamps stay on the wall clock (_now), as in the bash. test_u_a_wait_is_not_stretched_by_a_wall_clock_step_back (1 s, 3 s) swaps the module's time for a clock stepped back 10 s during the first sleep; on the previous code it measures 11.0 / 13.0 and fails. The comparison test and the finalize-related behaviour tests (issue33, issue18, issue14) on macOS / Python 3.9: 321 passed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VCgu9Fa9iT7TbJUSzdq4Ff
This commit is contained in:
1 parent
518bedac5d
commit
4c86107846
2 files changed
+43
-3
No files matched your search
@@ -143,11 +143,14 @@ def _check_owner(a: argparse.Namespace) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def _sleep(a: argparse.Namespace, seconds: float) -> None:
|
def _sleep(a: argparse.Namespace, seconds: float) -> None:
|
||||||
"""time.sleep that looks at the wrapper every 0.5 s."""
|
"""time.sleep that looks at the wrapper every 0.5 s. The span is measured
|
||||||
end = time.time() + seconds
|
on the monotonic clock, as bash's `sleep N` is: a wall clock stepped back
|
||||||
|
during the wait must not stretch it (review R1, codex). Deadlines stay on
|
||||||
|
the wall clock (_now)."""
|
||||||
|
end = time.monotonic() + seconds
|
||||||
while True:
|
while True:
|
||||||
_check_owner(a)
|
_check_owner(a)
|
||||||
left = end - time.time()
|
left = end - time.monotonic()
|
||||||
if left <= 0:
|
if left <= 0:
|
||||||
return
|
return
|
||||||
time.sleep(min(left, 0.5))
|
time.sleep(min(left, 0.5))
|
||||||
|
|||||||
@@ -591,3 +591,40 @@ def test_c_an_unreaped_wrapper_leaves_the_real_record_alone(tmp_path):
|
|||||||
time.sleep(6)
|
time.sleep(6)
|
||||||
states.append((rc, took < 4.5, json.loads((reg / f"{jid}.json").read_text())["status"]))
|
states.append((rc, took < 4.5, json.loads((reg / f"{jid}.json").read_text())["status"]))
|
||||||
assert states[0] == states[1] == (-15, True, "pending"), states
|
assert states[0] == states[1] == (-15, True, "pending"), states
|
||||||
|
|
||||||
|
|
||||||
|
# --- review R1 (codex): a wall clock stepped back does not stretch a wait ----
|
||||||
|
|
||||||
|
class _SteppedClock:
|
||||||
|
"""A `time` stand-in: wall and monotonic both advance on sleep(), and the
|
||||||
|
wall clock is stepped back once, during the first sleep."""
|
||||||
|
|
||||||
|
def __init__(self, step_back: float):
|
||||||
|
self.wall, self.mono, self.step_back, self.slept = 1000.0, 50.0, step_back, 0.0
|
||||||
|
|
||||||
|
def time(self):
|
||||||
|
return self.wall
|
||||||
|
|
||||||
|
def monotonic(self):
|
||||||
|
return self.mono
|
||||||
|
|
||||||
|
def sleep(self, dt):
|
||||||
|
self.wall += dt
|
||||||
|
self.mono += dt
|
||||||
|
self.slept += dt
|
||||||
|
if self.step_back:
|
||||||
|
self.wall -= self.step_back
|
||||||
|
self.step_back = 0
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("seconds", [1, 3])
|
||||||
|
def test_u_a_wait_is_not_stretched_by_a_wall_clock_step_back(monkeypatch, seconds):
|
||||||
|
"""The broker re-observation (up to 3 s) and the disk-heartbeat grace (1 s)
|
||||||
|
re-read the record after `seconds`, as bash's `sleep N` did, even when the
|
||||||
|
wall clock steps back 10 s meanwhile (on the old wall-clock arithmetic the
|
||||||
|
wait became seconds + 10)."""
|
||||||
|
import argparse
|
||||||
|
clock = _SteppedClock(step_back=10)
|
||||||
|
monkeypatch.setattr(decide, "time", clock)
|
||||||
|
decide._sleep(argparse.Namespace(watch_pid=None), seconds)
|
||||||
|
assert seconds <= clock.slept <= seconds + 0.5, clock.slept
|
||||||
Reference in new issue
Block a user