Skip to content
This repository was archived by the owner on Jan 23, 2026. It is now read-only.

Commit cef414c

Browse files
committed
nvdemux: remove manager callback altogether
1 parent 45fa1a1 commit cef414c

File tree

4 files changed

+15
-157
lines changed

4 files changed

+15
-157
lines changed

packages/jumpstarter-driver-pyserial/jumpstarter_driver_pyserial/nvdemux/driver.py

Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from dataclasses import dataclass, field
44
from typing import Optional
55

6-
from anyio import Event, fail_after, sleep
6+
from anyio import sleep
77
from anyio._backends._asyncio import StreamReaderWrapper, StreamWriterWrapper
88
from serial_asyncio import open_serial_connection
99

@@ -49,7 +49,6 @@ class NVDemuxSerial(Driver):
4949
poll_interval: float = field(default=1.0)
5050

5151
# Internal state (not init params)
52-
_ready: Event = field(init=False, default_factory=Event)
5352
_registered: bool = field(init=False, default=False)
5453

5554
def __post_init__(self):
@@ -65,7 +64,6 @@ def __post_init__(self):
6564
device=self.device,
6665
chip=self.chip,
6766
target=self.target,
68-
callback=self._on_target_ready,
6967
poll_interval=self.poll_interval,
7068
)
7169
self._registered = True
@@ -78,16 +76,6 @@ def __post_init__(self):
7876
def client(cls) -> str:
7977
return "jumpstarter_driver_pyserial.client.PySerialClient"
8078

81-
def _on_target_ready(self, target: str, pts_path: str):
82-
"""Callback invoked by DemuxerManager when target becomes ready.
83-
84-
Args:
85-
target: The target channel that became ready
86-
pts_path: The pts path for this target
87-
"""
88-
self.logger.info("Target '%s' ready with pts path: %s", target, pts_path)
89-
self._ready.set()
90-
9179
def close(self):
9280
"""Unregister from the DemuxerManager."""
9381
if self._registered:
@@ -105,23 +93,16 @@ async def connect(self):
10593
Waits for the demuxer to be ready (device connected and pts path discovered)
10694
before opening the serial connection.
10795
"""
108-
# Wait for ready state with timeout
109-
try:
110-
with fail_after(self.timeout):
111-
await self._ready.wait()
112-
except TimeoutError:
113-
raise TimeoutError(
114-
f"Timeout waiting for demuxer to become ready (device pattern: {self.device})"
115-
) from None
116-
117-
# Get the current pts path from manager (retry until timeout)
96+
# Poll for pts path until available or timeout
11897
manager = DemuxerManager.get_instance()
11998
pts_start = time.monotonic()
12099
pts_path = manager.get_pts_path(str(self.uuid))
121100
while not pts_path:
122101
elapsed = time.monotonic() - pts_start
123102
if elapsed >= self.timeout:
124-
raise TimeoutError("Demuxer ready but no pts path available after retrying")
103+
raise TimeoutError(
104+
f"Timeout waiting for demuxer to become ready (device pattern: {self.device})"
105+
)
125106
await sleep(self.poll_interval)
126107
pts_path = manager.get_pts_path(str(self.uuid))
127108

packages/jumpstarter-driver-pyserial/jumpstarter_driver_pyserial/nvdemux/driver_test.py

Lines changed: 0 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
"""Tests for NVDemuxSerial driver."""
22

33
import tempfile
4-
import time
54
from unittest.mock import MagicMock, patch
65

76
from .driver import NVDemuxSerial
@@ -35,36 +34,6 @@ def test_nvdemux_registration():
3534
driver.close()
3635

3736

38-
def test_nvdemux_callback_sets_ready():
39-
"""Test that callback from manager sets the ready event."""
40-
with tempfile.NamedTemporaryFile() as device_file:
41-
with patch("jumpstarter_driver_pyserial.nvdemux.driver.DemuxerManager") as mock_manager_class:
42-
mock_manager = MagicMock()
43-
mock_manager_class.get_instance.return_value = mock_manager
44-
45-
driver = NVDemuxSerial(
46-
demuxer_path="/usr/bin/demuxer",
47-
device=device_file.name,
48-
target="CCPLEX: 0",
49-
timeout=0.1,
50-
)
51-
52-
try:
53-
# Get the callback that was registered
54-
callback = mock_manager.register_driver.call_args[1]["callback"]
55-
56-
# Initially not ready
57-
assert not driver._ready.is_set()
58-
59-
# Call the callback
60-
callback("CCPLEX: 0", "/dev/pts/5")
61-
62-
# Should now be ready
63-
assert driver._ready.is_set()
64-
finally:
65-
driver.close()
66-
67-
6837
def test_nvdemux_gets_pts_from_manager():
6938
"""Test that connect() gets pts path from manager."""
7039
with tempfile.NamedTemporaryFile() as device_file:
@@ -81,10 +50,6 @@ def test_nvdemux_gets_pts_from_manager():
8150
)
8251

8352
try:
84-
# Trigger callback to set ready
85-
callback = mock_manager.register_driver.call_args[1]["callback"]
86-
callback("CCPLEX: 0", "/dev/pts/5")
87-
8853
# Should call get_pts_path when checking pts availability
8954
# (We can't test connect() easily without mocking serial, but we can test the logic)
9055
pts_path = mock_manager.get_pts_path(str(driver.uuid))
@@ -114,28 +79,6 @@ def test_nvdemux_unregisters_on_close():
11479
mock_manager.unregister_driver.assert_called_once_with(driver_id)
11580

11681

117-
def test_nvdemux_timeout_no_callback():
118-
"""Test timeout when callback is never invoked."""
119-
with tempfile.NamedTemporaryFile() as device_file:
120-
with patch("jumpstarter_driver_pyserial.nvdemux.driver.DemuxerManager") as mock_manager_class:
121-
mock_manager = MagicMock()
122-
mock_manager_class.get_instance.return_value = mock_manager
123-
124-
driver = NVDemuxSerial(
125-
demuxer_path="/usr/bin/demuxer",
126-
device=device_file.name,
127-
target="CCPLEX: 0",
128-
timeout=0.1,
129-
)
130-
131-
try:
132-
# Callback is never invoked, so ready should not be set
133-
time.sleep(0.2)
134-
assert not driver._ready.is_set()
135-
finally:
136-
driver.close()
137-
138-
13982
def test_nvdemux_default_values():
14083
"""Test default parameter values."""
14184
with tempfile.NamedTemporaryFile() as device_file:

packages/jumpstarter-driver-pyserial/jumpstarter_driver_pyserial/nvdemux/manager.py

Lines changed: 1 addition & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@ class DriverInfo:
8080

8181
driver_id: str
8282
target: str
83-
callback: Callable[[str, str], None] # (target, pts_path) -> None
8483

8584

8685
class DemuxerManager:
@@ -207,22 +206,13 @@ def _validate_config(self, demuxer_path: str, device: str, chip: str, target: st
207206
if existing_driver.target == target:
208207
raise ValueError(f"Target '{target}' already registered by another driver")
209208

210-
def _get_ready_callback(self, target: str, callback: Callable[[str, str], None]) -> tuple[str, str] | None:
211-
"""Return callback args if target is already ready."""
212-
if target in self._ready_targets:
213-
pts_path = self._pts_map.get(target)
214-
if pts_path:
215-
return target, pts_path
216-
return None
217-
218209
def register_driver(
219210
self,
220211
driver_id: str,
221212
demuxer_path: str,
222213
device: str,
223214
chip: str,
224215
target: str,
225-
callback: Callable[[str, str], None],
226216
poll_interval: float = 1.0,
227217
) -> None:
228218
"""Register a driver instance with the manager.
@@ -233,13 +223,11 @@ def register_driver(
233223
device: Device path or glob pattern
234224
chip: Chip type (T234 or T264)
235225
target: Target channel (e.g., "CCPLEX: 0")
236-
callback: Function to call when target becomes ready
237226
poll_interval: Polling interval for device reconnection
238227
239228
Raises:
240229
ValueError: If configuration doesn't match existing process
241230
"""
242-
notify_args: tuple[str, str] | None = None
243231
with self._lock:
244232
# Validate configuration matches existing process
245233
if self._drivers:
@@ -252,25 +240,15 @@ def register_driver(
252240
self._poll_interval = poll_interval
253241

254242
# Register the driver
255-
driver_info = DriverInfo(driver_id=driver_id, target=target, callback=callback)
243+
driver_info = DriverInfo(driver_id=driver_id, target=target)
256244
self._drivers[driver_id] = driver_info
257245

258246
logger.debug("Registered driver %s for target '%s'", driver_id, target)
259247

260-
# If target is already ready, notify immediately
261-
notify_args = self._get_ready_callback(target, callback)
262-
263248
# Start monitor thread only once
264249
if not self._monitor_thread or not self._monitor_thread.is_alive():
265250
self._start_monitor()
266251

267-
if notify_args:
268-
# Invoke callbacks outside the lock to avoid deadlocks/reentrancy.
269-
try:
270-
callback(*notify_args)
271-
except Exception as e:
272-
logger.error("Error in driver callback: %s", e)
273-
274252
def unregister_driver(self, driver_id: str) -> None:
275253
"""Unregister a driver instance.
276254
@@ -527,24 +505,10 @@ def _read_demuxer_output(self):
527505
if pts_path and target:
528506
logger.debug("Found pts path for target '%s': %s", target, pts_path)
529507

530-
callback_to_invoke = None
531508
with self._lock:
532509
self._pts_map[target] = pts_path
533510
self._ready_targets.add(target)
534511

535-
# Find driver callback for this specific target
536-
for driver_info in self._drivers.values():
537-
if driver_info.target == target:
538-
callback_to_invoke = driver_info.callback
539-
break # Only one driver per target
540-
541-
if callback_to_invoke:
542-
# Invoke callbacks outside the lock to avoid deadlocks/reentrancy.
543-
try:
544-
callback_to_invoke(target, pts_path)
545-
except Exception as e:
546-
logger.error("Error in driver callback: %s", e)
547-
548512
except Exception as e:
549513
logger.error("Error reading demuxer output: %s", e)
550514

0 commit comments

Comments
 (0)