mirror of
https://github.com/huggingface/lerobot.git
synced 2026-07-23 17:56:07 +00:00
fix(cameras): snapshot stop_event in read loops to avoid None deref (#3812)
* Do not set stop_event to None when stopping thread * fix(cameras): snapshot stop_event in read loops to avoid None deref The background read loops accessed self.stop_event repeatedly while _stop_read_thread() can reassign it to None after join(). Reading the attribute across the loop condition (and a mid-loop re-check) was a time-of-check/time-of-use race: stop_event could flip to None between the `is None` test and the `.is_set()` call, raising AttributeError on the worker thread. Snapshot self.stop_event into a local once, guard it, and loop on the local Event. The Event object is thread-safe and lives for the thread's lifetime; _stop_read_thread() always calls .set() before nulling the attribute, so the local observes the stop and exits cleanly. This also lets us drop the redundant pre-lock stop check. Applies to OpenCVCamera, RealSenseCamera, and ZMQ camera. --------- Co-authored-by: Anes Benmerzoug <anes.benmerzoug@gmail.com>
This commit is contained in:
@@ -442,11 +442,12 @@ class OpenCVCamera(Camera):
|
|||||||
|
|
||||||
Stops on DeviceNotConnectedError, logs other errors and continues.
|
Stops on DeviceNotConnectedError, logs other errors and continues.
|
||||||
"""
|
"""
|
||||||
if self.stop_event is None:
|
stop_event = self.stop_event
|
||||||
|
if stop_event is None:
|
||||||
raise RuntimeError(f"{self}: stop_event is not initialized before starting read loop.")
|
raise RuntimeError(f"{self}: stop_event is not initialized before starting read loop.")
|
||||||
|
|
||||||
failure_count = 0
|
failure_count = 0
|
||||||
while not self.stop_event.is_set():
|
while not stop_event.is_set():
|
||||||
try:
|
try:
|
||||||
raw_frame = self._read_from_hardware()
|
raw_frame = self._read_from_hardware()
|
||||||
processed_frame = self._postprocess_image(raw_frame)
|
processed_frame = self._postprocess_image(raw_frame)
|
||||||
|
|||||||
@@ -471,11 +471,12 @@ class RealSenseCamera(Camera):
|
|||||||
|
|
||||||
Stops on DeviceNotConnectedError, logs other errors and continues.
|
Stops on DeviceNotConnectedError, logs other errors and continues.
|
||||||
"""
|
"""
|
||||||
if self.stop_event is None:
|
stop_event = self.stop_event
|
||||||
|
if stop_event is None:
|
||||||
raise RuntimeError(f"{self}: stop_event is not initialized before starting read loop.")
|
raise RuntimeError(f"{self}: stop_event is not initialized before starting read loop.")
|
||||||
|
|
||||||
failure_count = 0
|
failure_count = 0
|
||||||
while not self.stop_event.is_set():
|
while not stop_event.is_set():
|
||||||
try:
|
try:
|
||||||
frame = self._read_from_hardware()
|
frame = self._read_from_hardware()
|
||||||
color_frame_raw = frame.get_color_frame()
|
color_frame_raw = frame.get_color_frame()
|
||||||
|
|||||||
@@ -246,11 +246,12 @@ class ZMQCamera(Camera):
|
|||||||
"""
|
"""
|
||||||
Internal loop run by the background thread for asynchronous reading.
|
Internal loop run by the background thread for asynchronous reading.
|
||||||
"""
|
"""
|
||||||
if self.stop_event is None:
|
stop_event = self.stop_event
|
||||||
|
if stop_event is None:
|
||||||
raise RuntimeError(f"{self}: stop_event is not initialized.")
|
raise RuntimeError(f"{self}: stop_event is not initialized.")
|
||||||
|
|
||||||
failure_count = 0
|
failure_count = 0
|
||||||
while not self.stop_event.is_set():
|
while not stop_event.is_set():
|
||||||
try:
|
try:
|
||||||
frame = self._read_from_hardware()
|
frame = self._read_from_hardware()
|
||||||
capture_time = time.perf_counter()
|
capture_time = time.perf_counter()
|
||||||
|
|||||||
Reference in New Issue
Block a user