Skip to content

Commit e9e59a8

Browse files
Kasper Jungeclaude
authored andcommitted
refactor: hold registry lock for entire start_run to prevent race condition
The start_run method previously released the lock (via _get_run) before modifying managed.thread and calling thread.start(), creating a window where concurrent calls could race on the same run. Now the entire operation is atomic under the registry lock, matching the class's documented "Thread-safe registry" contract. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 4fe40e3 commit e9e59a8

1 file changed

Lines changed: 14 additions & 10 deletions

File tree

src/ralphify/manager.py

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -85,17 +85,21 @@ def start_run(self, run_id: str) -> None:
8585
The thread calls :func:`engine.run_loop` with the emitter built
8686
by :meth:`ManagedRun.build_emitter`, which fans out to the queue
8787
and any extra listeners.
88+
89+
The entire operation runs under the registry lock so that
90+
concurrent calls cannot race on the same run.
8891
"""
89-
managed = self._get_run(run_id)
90-
emitter = managed.build_emitter()
91-
thread = threading.Thread(
92-
target=run_loop,
93-
args=(managed.config, managed.state, emitter),
94-
daemon=True,
95-
name=f"run-{run_id}",
96-
)
97-
managed.thread = thread
98-
thread.start()
92+
with self._lock:
93+
managed = self._runs[run_id]
94+
emitter = managed.build_emitter()
95+
thread = threading.Thread(
96+
target=run_loop,
97+
args=(managed.config, managed.state, emitter),
98+
daemon=True,
99+
name=f"run-{run_id}",
100+
)
101+
managed.thread = thread
102+
thread.start()
99103

100104
def stop_run(self, run_id: str) -> None:
101105
"""Signal the run to stop after the current iteration finishes."""

0 commit comments

Comments
 (0)