diff --git a/shared/libraries/websocket_streaming/include/websocket_streaming/ws_streaming_server.h b/shared/libraries/websocket_streaming/include/websocket_streaming/ws_streaming_server.h index 11c0f72..339ada2 100644 --- a/shared/libraries/websocket_streaming/include/websocket_streaming/ws_streaming_server.h +++ b/shared/libraries/websocket_streaming/include/websocket_streaming/ws_streaming_server.h @@ -106,6 +106,8 @@ class WsStreamingServer : public Server CoreEventArgsPtr& args); void rescan(); + void rescan(FolderPtr folder); + void pruneRemovedSignals(); private: diff --git a/shared/libraries/websocket_streaming/src/ws_streaming_server.cpp b/shared/libraries/websocket_streaming/src/ws_streaming_server.cpp index f9c36ac..8340edc 100644 --- a/shared/libraries/websocket_streaming/src/ws_streaming_server.cpp +++ b/shared/libraries/websocket_streaming/src/ws_streaming_server.cpp @@ -194,6 +194,10 @@ void WsStreamingServer::addCapability() void WsStreamingServer::createListener(const SignalPtr& signal) { + // Without a descriptor there is no way to know which kind of handler to create. + if (!signal.getDescriptor().assigned()) + return; + SignalPtr domainSignal = signal.getDomainSignal(); if (domainSignal.assigned()) @@ -313,31 +317,46 @@ void WsStreamingServer::onComponentAdded( ComponentPtr& component, CoreEventArgsPtr& args) { - rescan(); + // openDAQ holds the changed component's lock while core event handlers run, so scanning + // from the root here deadlocks against a thread holding any other device's lock. + // The sender is the parent the component was added to, so its subtree covers the new child. + if (auto folder = component.asPtrOrNull(); folder.assigned()) + rescan(folder); } void WsStreamingServer::onComponentRemoved( ComponentPtr& component, CoreEventArgsPtr& args) { - rescan(); + pruneRemovedSignals(); } void WsStreamingServer::onComponentUpdateEnd( ComponentPtr& component, CoreEventArgsPtr& args) { - rescan(); + // Core events are muted for the whole subtree while it updates, so this is the only + // notification that anything below the component changed. An update both adds and + // removes signals. + if (auto folder = component.asPtrOrNull(); folder.assigned()) + { + pruneRemovedSignals(); + rescan(folder); + } + + else if (auto signal = component.asPtrOrNull(); signal.assigned()) + createListener(signal); } void WsStreamingServer::onAttributeChanged( ComponentPtr& component, CoreEventArgsPtr& args) { - rescan(); + if (auto signal = component.asPtrOrNull(); signal.assigned()) + createListener(signal); } -void WsStreamingServer::rescan() +void WsStreamingServer::pruneRemovedSignals() { auto it = _localSignals.begin(); while (it != _localSignals.end()) @@ -352,10 +371,19 @@ void WsStreamingServer::rescan() else ++it; } +} + +void WsStreamingServer::rescan() +{ + pruneRemovedSignals(); + rescan(_rootDevice); +} - auto items = _rootDevice.getItems(search::Recursive(search::Any())); +void WsStreamingServer::rescan(FolderPtr folder) +{ + auto items = folder.getItems(search::Recursive(search::Any())); for (const auto& item : items) - if (auto signal = item.asPtrOrNull(); signal.assigned() && signal.getDescriptor().assigned()) + if (auto signal = item.asPtrOrNull(); signal.assigned()) createListener(signal); }