66 std::unique_lock<std::mutex> dispatchLock(dispatchMutex_);
67 dispatchCv_.wait(dispatchLock, [&] {
return !dispatchInProgress_; });
68 dispatchLock.unlock();
70 std::lock_guard<std::mutex> listenerLock(listenerMutex_);
71 for (
const auto &entry : listeners_) entry->active =
false;
73 listenerCount_.store(0, std::memory_order_release);
106 if (handle == 0)
return false;
108 std::shared_ptr<ListenerEntry> entry;
109 std::unique_lock<std::mutex> lock(listenerMutex_);
110 auto it = std::find_if(listeners_.begin(), listeners_.end(),
111 [handle](
const std::shared_ptr<ListenerEntry> &candidate)
113 return candidate->handle == handle;
115 if (it == listeners_.end())
return false;
118 entry->active =
false;
119 listeners_.erase(it);
120 listenerCount_.store(listeners_.size(), std::memory_order_release);
122 if (currentDispatcher ==
this && currentListenerHandle == handle)
return true;
124 entry->cv.wait(lock, [&] {
return entry->activeCalls == 0; });
146 if (!enabled(event.level))
return;
162 dispatcher->endDispatch();
168 std::vector<std::shared_ptr<ListenerEntry>> entries;
170 std::lock_guard<std::mutex> lock(listenerMutex_);
171 entries = listeners_;
174 for (
const auto &entry : entries)
178 std::lock_guard<std::mutex> lock(listenerMutex_);
179 if (!entry->active)
continue;
180 listener = entry->listener;
181 ++entry->activeCalls;
184 currentDispatcher =
this;
185 currentListenerHandle = entry->handle;
193 currentListenerHandle = 0;
194 currentDispatcher =
nullptr;
197 std::lock_guard<std::mutex> lock(listenerMutex_);
198 --entry->activeCalls;
199 if (entry->activeCalls == 0) entry->cv.notify_all();
487 return std::visit([&](
const auto & payload) -> std::string
489 using PayloadType = std::decay_t<
decltype(payload)>;
490 if constexpr (std::is_same<PayloadType, ConnectionPayload>::value)
494 return "communication failure: " + payload.reason;
496 std::string out = std::string(
"connection: ") +
toString(payload.state);
497 if (!payload.reason.empty()) out +=
": " + payload.reason;
500 else if constexpr (std::is_same<PayloadType, FramePayload>::value)
504 else if constexpr (std::is_same<PayloadType, StatusDecodedPayload>::value)
506 std::string out =
"status decoded";
507 if (payload.sequence != 0) out +=
" seq=" + std::to_string(payload.sequence);
508 if (!payload.frameType.empty()) out +=
" type=" + payload.frameType;
509 if (!payload.fields.empty()) out +=
": " + formatNamedValues(payload.fields);
512 else if constexpr (std::is_same<PayloadType, FieldChangesPayload>::value)
514 std::string out =
"field changes";
515 if (payload.sequence != 0) out +=
" seq=" + std::to_string(payload.sequence);
516 if (!payload.changes.empty()) out +=
": " + formatFieldChanges(payload.changes);
519 else if constexpr (std::is_same<PayloadType, VerificationPayload>::value)
521 std::string out = std::string(
"verification ") +
toString(payload.outcome) +
" " +
toString(payload.operation);
522 if (payload.relatedSequence != 0) out +=
" seq=" + std::to_string(payload.relatedSequence);
523 if (!payload.detail.empty()) out +=
": " + payload.detail;
526 else if constexpr (std::is_same<PayloadType, WarningPayload>::value)
528 if (!payload.message.empty())
return payload.message;
529 return std::string(
"warning: ") +
toString(payload.code);
531 else if constexpr (std::is_same<PayloadType, ParserAnomalyPayload>::value)
533 std::string out = std::string(
"parser anomaly ") +
toString(payload.code);
534 if (!payload.detail.empty()) out +=
": " + payload.detail;
535 if (!payload.frame.empty()) out +=
" (raw: " +
escapeFrame(payload.frame) +
")";
538 else if constexpr (std::is_same<PayloadType, ReconnectAttemptPayload>::value)
542 return "reconnect: exhausted retries, exiting";
546 return "reconnect: attempt " + std::to_string(payload.attempt) +
"/" + std::to_string(payload.maxAttempts);
548 std::string out =
"reconnect: ";
549 if (payload.attempt != 0 && payload.maxAttempts != 0)
550 out +=
"attempt " + std::to_string(payload.attempt) +
"/" + std::to_string(payload.maxAttempts) +
" ";
552 if (payload.delayBeforeAttempt.count() > 0)
553 out +=
" after " + std::to_string(payload.delayBeforeAttempt.count()) +
"ms";
556 else if constexpr (std::is_same<PayloadType, TimingPayload>::value)
558 return std::string(
"timing: ") +
toString(payload.operation) +
" " +
559 (payload.success ?
"ok" :
"failed") +
" " +
560 std::to_string(payload.duration.count()) +
"us";