Type: Investigation
Priority: Medium
Description
Core serializes concurrent text exchanges (_textExchangeLock, #186) but nothing else: concurrent calls into EnableChannel/SetDioValue/StartStreaming/network-config from multiple threads interleave SCPI writes with no device-level mutual exclusion.
The in-repo MCP server had to solve this itself because the MCP transport dispatches tool calls concurrently: it keeps a ConcurrentDictionary registry plus a SemaphoreSlim _gate that serializes every mutating operation, along with stale-handle detection and eviction (src/Daqifi.Mcp/DaqifiAgent.cs:28-29, 77-110, 467-482). Any multi-threaded consumer — a desktop UI, a web service — must reinvent the same gate to avoid corrupting device state.
Worth investigating rather than assuming a design: options range from (a) documenting "one operation at a time per device" as the consumer's contract, to (b) an internal per-device operation lock around command-sending members, to (c) a small session/connection-manager type that owns serialization + reconnect. Option (b) is likely the simplest meaningful step, but it interacts with streaming callbacks and the existing text-exchange lock, so it needs a careful look first.
Acceptance Criteria
Value
Today the concurrency contract is implicit and every concurrent consumer discovers it the hard way; either outcome of the investigation removes that trap.
Type: Investigation
Priority: Medium
Description
Core serializes concurrent text exchanges (
_textExchangeLock, #186) but nothing else: concurrent calls intoEnableChannel/SetDioValue/StartStreaming/network-config from multiple threads interleave SCPI writes with no device-level mutual exclusion.The in-repo MCP server had to solve this itself because the MCP transport dispatches tool calls concurrently: it keeps a
ConcurrentDictionaryregistry plus aSemaphoreSlim _gatethat serializes every mutating operation, along with stale-handle detection and eviction (src/Daqifi.Mcp/DaqifiAgent.cs:28-29, 77-110, 467-482). Any multi-threaded consumer — a desktop UI, a web service — must reinvent the same gate to avoid corrupting device state.Worth investigating rather than assuming a design: options range from (a) documenting "one operation at a time per device" as the consumer's contract, to (b) an internal per-device operation lock around command-sending members, to (c) a small session/connection-manager type that owns serialization + reconnect. Option (b) is likely the simplest meaningful step, but it interacts with streaming callbacks and the existing text-exchange lock, so it needs a careful look first.
Acceptance Criteria
DaqifiDevice/DaqifiStreamingDevice(thread-safety section in DEVICE_INTERFACES.md is updated to match reality)_gateshrinks or disappearsValue
Today the concurrency contract is implicit and every concurrent consumer discovers it the hard way; either outcome of the investigation removes that trap.