Skip to content

Commit 71bb1aa

Browse files
committed
Cleanup abandoned recorder and set pending reset
Ensure proper cleanup and restartability when writer threads time out: VideoRecorder.stop now clears abandoned state and associated resources if the writer thread has already exited, allowing the recorder to be fully stopped and restarted. DLCLiveProcessor: moved settings/processor assignment under the lifecycle lock, and mark _pending_reset when shutdown cannot stop the worker thread so the instance can be reaped later. Minor comment tweak for STOP_WORKER_TIMEOUT semantics. Tests updated to assert abandoned is cleared and that recorder can be restarted after the writer exits.
1 parent 6be4047 commit 71bb1aa

File tree

3 files changed

+20
-4
lines changed

3 files changed

+20
-4
lines changed

dlclivegui/services/dlc_processor.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from dlclivegui.temp import Engine # type: ignore # TODO use main package enum when released
2222

2323
logger = logging.getLogger(__name__)
24-
STOP_WORKER_TIMEOUT = 10.0 # seconds to wait for worker thread to stop before marking as faulted
24+
STOP_WORKER_TIMEOUT = 10.0 # seconds to wait for worker thread to stop before STOPPING state and reaping
2525

2626
try: # pragma: no cover - optional dependency
2727
from dlclive import (
@@ -184,8 +184,8 @@ def configure(self, settings: DLCProcessorSettings, processor: Any | None = None
184184
with self._lifecycle_lock:
185185
if self._state != WorkerState.STOPPED:
186186
raise RuntimeError("Cannot configure DLCLiveProcessor while it is running. Please stop it first.")
187-
self._settings = settings
188-
self._processor = processor
187+
self._settings = settings
188+
self._processor = processor
189189

190190
def reset(self) -> None:
191191
"""Stop the worker thread and drop the current DLCLive instance."""
@@ -215,6 +215,8 @@ def reset(self) -> None:
215215
def shutdown(self) -> None:
216216
stopped = self._stop_worker()
217217
if not stopped:
218+
with self._lifecycle_lock:
219+
self._pending_reset = True
218220
logger.warning(
219221
"Shutdown requested but worker thread is still alive; DLCLive instance may not be fully released."
220222
)

dlclivegui/services/video_recorder.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,16 @@ def write(self, frame: np.ndarray, timestamp: float | None = None) -> bool:
212212
def stop(self) -> None:
213213
with self._lifecycle_lock:
214214
if self._writer is None and not self.is_running:
215-
return
215+
# If the recorder was previously marked as abandoned because the
216+
# writer thread did not stop in time, but the thread has since
217+
# exited, perform cleanup so the recorder can become fully stopped
218+
# and restartable.
219+
t = self._writer_thread
220+
if self._abandoned and (t is None or not t.is_alive()):
221+
self._writer_thread = None
222+
self._queue = None
223+
self._stop_event.clear()
224+
self._abandoned = False
216225

217226
self._stop_event.set()
218227

tests/services/test_video_recorder.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,3 +367,8 @@ def test_stop_timeout_marks_abandoned_and_prevents_restart(
367367

368368
# Call stop again to clear resources / reset flags (now it can join cleanly)
369369
rec.stop()
370+
# After the writer thread has exited, a second stop() should fully reset state
371+
# so that the recorder is no longer marked as abandoned and can be restarted.
372+
assert rec._abandoned is False
373+
rec.start()
374+
rec.stop()

0 commit comments

Comments
 (0)