Repository navigation
The app's USB-unplug watcher duplicates Core 1.8.0's serial liveness detector; both race to the same teardown #441
Description
Summary
The app runs two USB-unplug detectors for every serial device, and both end in the same teardown. The second is the app's own IDeviceWatcher stack: Services/DeviceWatcher/* (5 files, 391 lines) plus ConnectionManager.CheckIfSerialDeviceWasRemoved. It re-implements a detector Core 1.8.0 already runs on the same port.
- Core:
SerialStreamTransportpolls port presence every 1 s (DefaultLivenessCheckInterval, inPublicAPI.Shipped.txtatv1.8.0). It needs two consecutive misses, then raisesConnectionStatus.Lost. That runsAbstractStreamingDevice.OnCoreStatusChanged→ConnectionLost→ConnectionManager.OnDeviceConnectionLost→TearDownDroppedDevice. Core measured 1.6 s on a real cable pull (daqifi-core#382/refactor(abstraction-police): the connection dialog asks Core for serial port names instead of importing System.IO.Ports (+2/−2 prod) #403). The app'sSerialStreamingDevicebuildsnew SerialStreamTransport(Port.PortName, enableDtr: true), which uses the default interval, so the detector is on. - App:
SerialPortPollingDeviceWatcherpollsSerialPort.GetPortNames()every 1 s on macOS/Linux.WmiDeviceWatcherdoes the same job on Windows. Either one raisesDeviceRemoved→CheckIfSerialDeviceWasRemoved→ the sameTearDownDroppedDevice.
The code already knows. ConnectionManager.cs, inside CheckIfSerialDeviceWasRemoved, says Core's own port-presence poll "reports the same unplug as ConnectionLost ... so this and OnDeviceConnectionLost race for one removal." Both paths skip a device under firmware update (IsDeviceBeingUpdated), and whichever arrives second finds the device already gone.
Why it was kept, and why that reason does not hold
PR #172 kept the watcher: "This port's macOS/Linux USB hotplug detection lives there and is strictly better than upstream's pre-deletion state." That compares it with upstream desktop, not with Core. The watcher was the only macOS detector when #150 added it. It stopped being the only one when Core added transport liveness (#382). PR #404's title repeats the stale premise ("the only USB-unplug detector on macOS/Linux").
What would actually change for a user
- Latency: Core reports in about 1.6-4 s. The app's poller takes about 1-2 s, and WMI on Windows is event-driven. This is the one real difference; name it in the PR.
- Notification wording: "X was unplugged." becomes "X disconnected (reason)."
- A false notification disappears:
DaqifiViewModel.ReportHotplugDetectionUnavailabletells the user that "unplugging a connected device will not be noticed" whenever the watcher fails to start. That is untrue while Core's detector runs.
Retirement plan
Delete:
Services/DeviceWatcher/(all 5 files)ConnectionManager.CheckIfSerialDeviceWasRemoved, the watcher field and its construction blockHotplugDetectionUnavailableMessage/HotplugUnavailableUserMessageDaqifiViewModel.ReportHotplugDetectionUnavailableDaqifi.Avalonia.Tests/Services/SerialPortPollingDeviceWatcherTests.cs- the
System.ManagementPackageReferenceinDaqifi.Avalonia.csproj(WmiDeviceWatcheris its only code user; the other hit is a comment)
Update the internal test constructor used by ConnectionManagerTeardownTests and ConnectionManagerConnectResultTests (it takes a NoOpDeviceWatcher).
Guarding tests: ConnectionManagerTeardownTests already drives OnDeviceConnectionLost → TearDownDroppedDevice, including the firmware-update exemption, and that is the path every unplug will take. Add one test that a Lost status on a serial device removes it with no watcher present.
Verification this needs, and why it is not unattended-safe: nobody has watched a physical unplug reach ConnectionStatus.Lost through this app (#172's bench run covered the false-positive direction only). Before merging, do one attended cable pull on macOS, and ideally one on Windows, with the watcher deleted. Read DAQifiAppLog.log for the teardown line.
🤖 Generated with Claude Code
Activity