| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * This file is part of the Falcon Player (FPP) and is Copyright (C) | ||
| 3 | * 2013-2024 by the Falcon Player Developers. | ||
| 4 | * | ||
| 5 | * The Falcon Player (FPP) is free software, and is covered under | ||
| 6 | * multiple Open Source licenses. Please see the included 'LICENSES' | ||
| 7 | * file for descriptions of what files are covered by each license. | ||
| 8 | * | ||
| 9 | * This source file is covered under the LGPL v2.1 as described in the | ||
| 10 | * included LICENSE.LGPL file. | ||
| 11 | */ | ||
| 12 | |||
| 13 | #include "fpp-pch.h" | ||
| 14 | |||
| 15 | #include "fpp-json.h" | ||
| 16 | |||
| 17 | #include <algorithm> | ||
| 18 | #include <chrono> | ||
| 19 | #include <climits> | ||
| 20 | #include <condition_variable> | ||
| 21 | #include <ctime> | ||
| 22 | #include <list> | ||
| 23 | #include <map> | ||
| 24 | #include <memory> | ||
| 25 | #include <mutex> | ||
| 26 | #include <semaphore> | ||
| 27 | #include <string> | ||
| 28 | #include <thread> | ||
| 29 | #include <unistd.h> | ||
| 30 | #include <utility> | ||
| 31 | #include <vector> | ||
| 32 | |||
| 33 | #include "OutputMonitor.h" | ||
| 34 | #include "Player.h" | ||
| 35 | #include "Warnings.h" | ||
| 36 | #include "common.h" | ||
| 37 | #include "fppversion.h" | ||
| 38 | #include "log.h" | ||
| 39 | #include "settings.h" | ||
| 40 | #include "commands/Commands.h" | ||
| 41 | |||
| 42 | #include "Events.h" | ||
| 43 | |||
| 44 | using namespace std::chrono; | ||
| 45 | using namespace std::chrono_literals; | ||
| 46 | |||
| 47 | static std::list<EventHandler*> EVENT_HANDLERS; | ||
| 48 | // Callbacks run on the event handler's thread (libmosquitto's for MQTT) while | ||
| 49 | // the main loop and API threads add and remove them -- plugins re-register on | ||
| 50 | // every config reload, and unload removes theirs before their code goes away. | ||
| 51 | // So the map is locked, a call runs from a slot the caller holds a reference | ||
| 52 | // to (never from the map), and RemoveCallback waits for calls in flight. | ||
| 53 | struct EventCallbackSlot { | ||
| 54 | std::function<void(const std::string& topic, const std::string& payload)> fn; | ||
| 55 | int inFlight = 0; // guarded by EVENT_CALLBACKS_LOCK | ||
| 56 | }; | ||
| 57 | static std::mutex EVENT_CALLBACKS_LOCK; | ||
| 58 | static std::condition_variable EVENT_CALLBACKS_IDLE; | ||
| 59 | static std::map<std::string, std::shared_ptr<EventCallbackSlot>> EVENT_CALLBACKS; | ||
| 60 | static thread_local int tl_invokingCallback = 0; | ||
| 61 | |||
| 62 | 72 | static std::vector<std::string> EventCallbackTopics() { | |
| 63 |
1/2✓ Branch 0 taken 72 times.
✗ Branch 1 not taken.
|
72 | std::lock_guard<std::mutex> lock(EVENT_CALLBACKS_LOCK); |
| 64 | 72 | std::vector<std::string> topics; | |
| 65 |
2/2✓ Branch 0 taken 432 times.
✓ Branch 1 taken 72 times.
|
504 | for (auto& c : EVENT_CALLBACKS) { |
| 66 |
1/2✓ Branch 0 taken 432 times.
✗ Branch 1 not taken.
|
432 | topics.push_back(c.first); |
| 67 | } | ||
| 68 | 72 | return topics; | |
| 69 | 72 | } | |
| 70 | // Intentionally leaked: if this were a plain file-static, __cxa_finalize would | ||
| 71 | // run ~thread() on ANY exit() call (early startup failures like a bridge-port | ||
| 72 | // bind error call exit(1) long before the orderly _exit(0) at the end of | ||
| 73 | // main()), and destroying a still-joinable std::thread calls std::terminate. | ||
| 74 | // Leaking the object means the destructor never runs; the orderly shutdown | ||
| 75 | // path still stops and joins the thread explicitly. | ||
| 76 | static std::thread& EVENT_PUBLISH_THREAD = *new std::thread(); | ||
| 77 | static std::binary_semaphore EVENT_PUBLISH_THREAD_STOP{ 0 }; | ||
| 78 | static std::vector<std::unique_ptr<EventNotifier>> eventNotifiers; | ||
| 79 | |||
| 80 | class PublishPlaylistStatus : public EventNotifier { | ||
| 81 | public: | ||
| 82 | 2 | PublishPlaylistStatus(int freq) : | |
| 83 | 2 | EventNotifier(freq) {} | |
| 84 | ✗ | ~PublishPlaylistStatus() {} | |
| 85 | |||
| 86 | 4 | void notify() { | |
| 87 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | Json::Value json = Player::INSTANCE.GetMqttStatusJSON(); |
| 88 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | std::stringstream buffer; |
| 89 |
2/4✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
|
4 | buffer << json << std::endl; |
| 90 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | LogDebug(VB_CONTROL, "Notify PublishPlaylist\n"); |
| 91 |
3/6✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
|
12 | Events::Publish("playlist_details", buffer.str()); |
| 92 | 4 | } | |
| 93 | }; | ||
| 94 | |||
| 95 | extern void GetCurrentFPPDStatus(Json::Value& result); | ||
| 96 | |||
| 97 | class PublishFPPDStatus : public EventNotifier { | ||
| 98 | public: | ||
| 99 | 1 | PublishFPPDStatus(int freq) : | |
| 100 | 1 | EventNotifier(freq) {} | |
| 101 | ✗ | ~PublishFPPDStatus() {} | |
| 102 | |||
| 103 | 5 | void notify() { | |
| 104 |
1/2✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
|
5 | Json::Value json; |
| 105 |
1/2✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
|
5 | GetCurrentFPPDStatus(json); |
| 106 | |||
| 107 |
1/2✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
|
5 | std::stringstream buffer; |
| 108 |
2/4✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
|
5 | buffer << json << std::endl; |
| 109 |
1/2✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
|
5 | LogDebug(VB_CONTROL, "Notify FPPDStatus\n"); |
| 110 |
3/6✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
|
15 | Events::Publish("fppd_status", buffer.str()); |
| 111 | 5 | } | |
| 112 | }; | ||
| 113 | |||
| 114 | class PublishPortStatus : public EventNotifier { | ||
| 115 | public: | ||
| 116 | ✗ | PublishPortStatus(int freq) : | |
| 117 | ✗ | EventNotifier(freq) {} | |
| 118 | ✗ | ~PublishPortStatus() {} | |
| 119 | |||
| 120 | ✗ | void notify() { | |
| 121 | ✗ | Json::Value json = Json::arrayValue; | |
| 122 | ✗ | OutputMonitor::INSTANCE.GetCurrentPortStatusJson(json); | |
| 123 | ✗ | std::stringstream buffer; | |
| 124 | ✗ | buffer << json << std::endl; | |
| 125 | ✗ | LogDebug(VB_CONTROL, "Notify FPPDStatus\n"); | |
| 126 | ✗ | Events::Publish("port_status", buffer.str()); | |
| 127 | ✗ | } | |
| 128 | }; | ||
| 129 | |||
| 130 | class EventWarningListener : public WarningListener { | ||
| 131 | public: | ||
| 132 | 1885 | EventWarningListener() {} | |
| 133 | 7468 | virtual ~EventWarningListener() {} | |
| 134 | |||
| 135 | 2197 | virtual void handleWarnings(const std::list<FPPWarning>& warnings) { | |
| 136 |
2/2✓ Branch 0 taken 37 times.
✓ Branch 1 taken 2160 times.
|
2197 | if (Events::HasEventHandlers()) { |
| 137 |
1/2✓ Branch 0 taken 37 times.
✗ Branch 1 not taken.
|
37 | Json::Value rc = Json::Value(Json::arrayValue); |
| 138 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 37 times.
|
44 | for (auto& w : warnings) { |
| 139 |
2/4✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
|
7 | rc.append(w.toJsonValue()); |
| 140 | } | ||
| 141 |
2/4✓ Branch 0 taken 37 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 37 times.
✗ Branch 3 not taken.
|
37 | std::string msg = SaveJsonToString(rc); |
| 142 |
1/2✓ Branch 0 taken 37 times.
✗ Branch 1 not taken.
|
37 | LogDebug(VB_CONTROL, "Sending warning message: %s\n", msg.c_str()); |
| 143 |
2/4✓ Branch 0 taken 37 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 37 times.
✗ Branch 3 not taken.
|
37 | Events::Publish("warnings", msg); |
| 144 | 37 | } | |
| 145 | 2197 | } | |
| 146 | }; | ||
| 147 | static std::unique_ptr<EventWarningListener> EVENT_WARNING_LISTENER; | ||
| 148 | |||
| 149 | 36 | EventHandler::EventHandler() {} | |
| 150 | 72 | EventHandler::~EventHandler() {} | |
| 151 | |||
| 152 | 1885 | void Events::Ready() { | |
| 153 |
1/2✓ Branch 0 taken 1885 times.
✗ Branch 1 not taken.
|
1885 | if (!EVENT_WARNING_LISTENER) { |
| 154 |
1/2✓ Branch 0 taken 1885 times.
✗ Branch 1 not taken.
|
1885 | EVENT_WARNING_LISTENER = std::make_unique<EventWarningListener>(); |
| 155 |
1/2✓ Branch 0 taken 1885 times.
✗ Branch 1 not taken.
|
1885 | WarningHolder::AddWarningListener(EVENT_WARNING_LISTENER.get()); |
| 156 | } | ||
| 157 | |||
| 158 |
2/4✓ Branch 0 taken 1885 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1885 times.
✗ Branch 3 not taken.
|
3770 | Events::Publish(MQTT_READY_TOPIC_NAME, 1); |
| 159 |
4/8✓ Branch 0 taken 1885 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1885 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1885 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1885 times.
✗ Branch 7 not taken.
|
7540 | Events::Publish("version", getFPPVersion()); |
| 160 |
4/8✓ Branch 0 taken 1885 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1885 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1885 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1885 times.
✗ Branch 7 not taken.
|
5655 | Events::Publish("branch", getFPPBranch()); |
| 161 | |||
| 162 |
1/2✓ Branch 0 taken 1885 times.
✗ Branch 1 not taken.
|
1885 | int playlistFrequency = getSettingInt("MQTTFrequency"); |
| 163 |
1/2✓ Branch 0 taken 1885 times.
✗ Branch 1 not taken.
|
1885 | int statusFrequency = getSettingInt("MQTTStatusFrequency"); |
| 164 |
1/2✓ Branch 0 taken 1885 times.
✗ Branch 1 not taken.
|
1885 | int portFrequency = getSettingInt("MQTTPortStatusFrequency"); |
| 165 | |||
| 166 |
1/2✓ Branch 0 taken 1885 times.
✗ Branch 1 not taken.
|
1885 | eventNotifiers.reserve((playlistFrequency > 0) + (statusFrequency > 0) + (portFrequency > 0)); |
| 167 | |||
| 168 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1883 times.
|
1885 | if (playlistFrequency > 0) { |
| 169 |
2/4✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
|
2 | eventNotifiers.push_back(std::make_unique<PublishPlaylistStatus>(playlistFrequency)); |
| 170 | } | ||
| 171 | |||
| 172 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1884 times.
|
1885 | if (statusFrequency > 0) { |
| 173 |
2/4✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
1 | eventNotifiers.push_back(std::make_unique<PublishFPPDStatus>(statusFrequency)); |
| 174 | } | ||
| 175 | |||
| 176 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1885 times.
|
1885 | if (portFrequency > 0) { |
| 177 | ✗ | eventNotifiers.push_back(std::make_unique<PublishPortStatus>(portFrequency)); | |
| 178 | } | ||
| 179 | |||
| 180 |
5/6✓ Branch 0 taken 1885 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 1882 times.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 1882 times.
|
1885 | if (!EVENT_PUBLISH_THREAD.joinable() && !eventNotifiers.empty()) { |
| 181 | // create background Publish Thread | ||
| 182 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | EVENT_PUBLISH_THREAD = std::thread(Events::RunPublishThread); |
| 183 | } | ||
| 184 | 1885 | } | |
| 185 | |||
| 186 | 36 | void Events::AddEventHandler(EventHandler* handler) { | |
| 187 |
3/4✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 72 times.
✓ Branch 3 taken 36 times.
|
108 | for (auto& topic : EventCallbackTopics()) { |
| 188 |
1/2✓ Branch 0 taken 72 times.
✗ Branch 1 not taken.
|
72 | handler->RegisterCallback(topic); |
| 189 | 36 | } | |
| 190 | 36 | EVENT_HANDLERS.push_back(handler); | |
| 191 | 36 | } | |
| 192 | 36 | void Events::RemoveEventHandler(EventHandler* handler) { | |
| 193 |
3/4✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 360 times.
✓ Branch 3 taken 36 times.
|
396 | for (auto& topic : EventCallbackTopics()) { |
| 194 |
1/2✓ Branch 0 taken 360 times.
✗ Branch 1 not taken.
|
360 | handler->RemoveCallback(topic); |
| 195 | 36 | } | |
| 196 | 36 | EVENT_HANDLERS.remove(handler); | |
| 197 | 36 | } | |
| 198 | |||
| 199 | 4084 | bool Events::HasEventHandlers() { | |
| 200 | 4084 | return !EVENT_HANDLERS.empty(); | |
| 201 | } | ||
| 202 | |||
| 203 | 278542 | bool Events::Publish(const std::string& topic, const std::string& data) { | |
| 204 | 278542 | bool ret = true; | |
| 205 |
2/2✓ Branch 0 taken 2145 times.
✓ Branch 1 taken 278542 times.
|
280687 | for (auto handler : EVENT_HANDLERS) { |
| 206 |
1/2✓ Branch 0 taken 2145 times.
✗ Branch 1 not taken.
|
2145 | ret &= handler->Publish(topic, data); |
| 207 | } | ||
| 208 | 278542 | return ret; | |
| 209 | } | ||
| 210 | |||
| 211 | 47729 | bool Events::Publish(const std::string& topic, const int valueconst) { | |
| 212 | 47729 | bool ret = true; | |
| 213 |
2/2✓ Branch 0 taken 1286 times.
✓ Branch 1 taken 47729 times.
|
49015 | for (auto handler : EVENT_HANDLERS) { |
| 214 |
1/2✓ Branch 0 taken 1286 times.
✗ Branch 1 not taken.
|
1286 | ret &= handler->Publish(topic, valueconst); |
| 215 | } | ||
| 216 | 47729 | return ret; | |
| 217 | } | ||
| 218 | |||
| 219 | 13323 | void Events::AddCallback(const std::string& topic, std::function<void(const std::string& topic, const std::string& payload)>& callback) { | |
| 220 |
1/2✓ Branch 0 taken 13323 times.
✗ Branch 1 not taken.
|
13323 | auto slot = std::make_shared<EventCallbackSlot>(); |
| 221 |
1/2✓ Branch 0 taken 13323 times.
✗ Branch 1 not taken.
|
13323 | slot->fn = callback; |
| 222 | { | ||
| 223 |
1/2✓ Branch 0 taken 13323 times.
✗ Branch 1 not taken.
|
13323 | std::lock_guard<std::mutex> lock(EVENT_CALLBACKS_LOCK); |
| 224 |
1/2✓ Branch 0 taken 13323 times.
✗ Branch 1 not taken.
|
13323 | EVENT_CALLBACKS[topic] = slot; |
| 225 | 13323 | } | |
| 226 |
2/2✓ Branch 0 taken 289 times.
✓ Branch 1 taken 13323 times.
|
13612 | for (auto handler : EVENT_HANDLERS) { |
| 227 |
1/2✓ Branch 0 taken 289 times.
✗ Branch 1 not taken.
|
289 | handler->RegisterCallback(topic); |
| 228 | } | ||
| 229 | 13323 | } | |
| 230 | 1 | void Events::RemoveCallback(const std::string& topic) { | |
| 231 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
2 | for (auto handler : EVENT_HANDLERS) { |
| 232 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | handler->RemoveCallback(topic); |
| 233 | } | ||
| 234 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | std::unique_lock<std::mutex> lock(EVENT_CALLBACKS_LOCK); |
| 235 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | auto it = EVENT_CALLBACKS.find(topic); |
| 236 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (it == EVENT_CALLBACKS.end()) { |
| 237 | ✗ | return; | |
| 238 | } | ||
| 239 | 1 | std::shared_ptr<EventCallbackSlot> slot = it->second; | |
| 240 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | EVENT_CALLBACKS.erase(it); |
| 241 | // Wait for a call already running on the handler's thread to finish, so | ||
| 242 | // the caller can then free what the callback uses (or unload its | ||
| 243 | // plugin). Not from inside a callback: that would wait on itself. Not | ||
| 244 | // forever either: a callback stuck waiting on the remover's thread would | ||
| 245 | // otherwise deadlock both. | ||
| 246 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
2 | if (tl_invokingCallback == 0 && |
| 247 |
3/6✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
|
4 | !EVENT_CALLBACKS_IDLE.wait_for(lock, std::chrono::seconds(5), [&slot]() { return slot->inFlight == 0; })) { |
| 248 | ✗ | LogWarn(VB_CONTROL, "Event callback for \"%s\" still running 5s after its removal\n", topic.c_str()); | |
| 249 | } | ||
| 250 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | } |
| 251 | 201 | void Events::InvokeCallback(const std::string& topic, const std::string& topic_in, const std::string& payload) { | |
| 252 | 201 | std::shared_ptr<EventCallbackSlot> slot; | |
| 253 | { | ||
| 254 | // find(), not operator[]: an unknown topic would otherwise insert an | ||
| 255 | // empty entry. | ||
| 256 |
1/2✓ Branch 0 taken 201 times.
✗ Branch 1 not taken.
|
201 | std::lock_guard<std::mutex> lock(EVENT_CALLBACKS_LOCK); |
| 257 |
1/2✓ Branch 0 taken 201 times.
✗ Branch 1 not taken.
|
201 | auto it = EVENT_CALLBACKS.find(topic); |
| 258 |
3/6✓ Branch 0 taken 201 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 201 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 201 times.
|
201 | if (it == EVENT_CALLBACKS.end() || !it->second->fn) { |
| 259 | ✗ | return; | |
| 260 | } | ||
| 261 | 201 | slot = it->second; | |
| 262 | 201 | slot->inFlight++; | |
| 263 |
1/2✓ Branch 0 taken 201 times.
✗ Branch 1 not taken.
|
201 | } |
| 264 | // Callbacks run on the event handler's own thread (libmosquitto's for | ||
| 265 | // MQTT), where nothing above us catches - an exception escaping here is | ||
| 266 | // std::terminate. The payload is whatever a broker client sent. | ||
| 267 | 201 | tl_invokingCallback++; | |
| 268 | try { | ||
| 269 |
1/2✓ Branch 0 taken 199 times.
✗ Branch 1 not taken.
|
201 | slot->fn(topic_in, payload); |
| 270 | ✗ | } catch (const std::exception& e) { | |
| 271 | ✗ | LogErr(VB_CONTROL, "Event callback for \"%s\" threw: %s\n", topic_in.c_str(), e.what()); | |
| 272 | ✗ | } catch (...) { | |
| 273 | ✗ | LogErr(VB_CONTROL, "Event callback for \"%s\" threw an unknown exception\n", topic_in.c_str()); | |
| 274 | ✗ | } | |
| 275 | 199 | tl_invokingCallback--; | |
| 276 | { | ||
| 277 |
1/2✓ Branch 0 taken 199 times.
✗ Branch 1 not taken.
|
199 | std::lock_guard<std::mutex> lock(EVENT_CALLBACKS_LOCK); |
| 278 | 199 | slot->inFlight--; | |
| 279 | 199 | } | |
| 280 | 199 | EVENT_CALLBACKS_IDLE.notify_all(); | |
| 281 |
1/2✓ Branch 0 taken 199 times.
✗ Branch 1 not taken.
|
199 | } |
| 282 | |||
| 283 | 1867 | void Events::PrepareForShutdown() { | |
| 284 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1864 times.
|
1867 | if (EVENT_PUBLISH_THREAD.joinable()) { |
| 285 | 3 | EVENT_PUBLISH_THREAD_STOP.release(); | |
| 286 | 3 | EVENT_PUBLISH_THREAD.join(); | |
| 287 | } | ||
| 288 |
1/2✓ Branch 0 taken 1867 times.
✗ Branch 1 not taken.
|
1867 | if (EVENT_WARNING_LISTENER) { |
| 289 | 1867 | WarningHolder::RemoveWarningListener(EVENT_WARNING_LISTENER.get()); | |
| 290 | 1867 | EVENT_WARNING_LISTENER.reset(); | |
| 291 | } | ||
| 292 | |||
| 293 |
2/4✓ Branch 0 taken 1867 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1867 times.
✗ Branch 3 not taken.
|
1867 | Publish(MQTT_READY_TOPIC_NAME, 0); |
| 294 |
2/2✓ Branch 0 taken 36 times.
✓ Branch 1 taken 1867 times.
|
1903 | for (auto handler : EVENT_HANDLERS) { |
| 295 |
1/2✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
|
36 | handler->PrepareForShutdown(); |
| 296 | } | ||
| 297 | 1867 | } | |
| 298 | |||
| 299 | 3 | void Events::RunPublishThread() { | |
| 300 |
2/4✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
|
3 | SetThreadName("FPP-Events"); |
| 301 |
2/4✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
|
3 | std::this_thread::sleep_for(3s); // Give everything time to start up |
| 302 | |||
| 303 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | LogInfo(VB_CONTROL, "Starting Publish Thread\n"); |
| 304 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | if (eventNotifiers.empty()) { |
| 305 | // kill thread | ||
| 306 | ✗ | LogInfo(VB_CONTROL, "Stopping Publish Thread as frequency is zero.\n"); | |
| 307 | ✗ | return; | |
| 308 | } | ||
| 309 | |||
| 310 | // Loop forever. We are running both publishes in one thread | ||
| 311 | // just to minimize the number of threads | ||
| 312 | 3 | seconds sleepDuration(0); | |
| 313 | // Thread will exit when the semaphore can be acquired | ||
| 314 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 3 times.
|
12 | while (!EVENT_PUBLISH_THREAD_STOP.try_acquire_for(sleepDuration)) { |
| 315 | 9 | auto now = system_clock::now(); | |
| 316 | |||
| 317 | 9 | sleepDuration = seconds::max(); | |
| 318 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 9 times.
|
18 | for (auto& pNotifier : eventNotifiers) { |
| 319 | // next_time is wall-clock seconds (EventNotifier is plugin ABI, so | ||
| 320 | // its clock can't change). A due time more than one period away | ||
| 321 | // can only mean the clock stepped back: publishing would otherwise | ||
| 322 | // stop for the size of the step. Publish now and re-anchor. | ||
| 323 |
4/6✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 8 times.
|
9 | if (system_clock::from_time_t(pNotifier->next_time) > now + seconds(pNotifier->frequency)) { |
| 324 | 1 | pNotifier->next_time = system_clock::to_time_t(now); | |
| 325 | } | ||
| 326 |
2/4✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
|
9 | if (system_clock::from_time_t(pNotifier->next_time) <= now) { |
| 327 |
1/2✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
|
9 | pNotifier->notify(); |
| 328 |
1/2✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
|
9 | pNotifier->next_time = system_clock::to_time_t(now + seconds(pNotifier->frequency)); |
| 329 | } | ||
| 330 |
1/2✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
|
9 | sleepDuration = std::min(sleepDuration, seconds(pNotifier->frequency)); |
| 331 | } | ||
| 332 | } | ||
| 333 | } | ||
| 334 |