/*----------------------------------------------------------------------------*/ /* Copyright (c) FIRST 2015. All Rights Reserved. */ /* Open Source Software - may be modified and shared by FRC teams. The code */ /* must be accompanied by the FIRST BSD license file in the root directory of */ /* the project. */ /*----------------------------------------------------------------------------*/ #include "Notifier.h" using namespace nt; ATOMIC_STATIC_INIT(Notifier) bool Notifier::s_destroyed = false; Notifier::Notifier() { m_active = false; m_local_notifiers = false; s_destroyed = false; } Notifier::~Notifier() { s_destroyed = true; Stop(); } void Notifier::Start() { { std::lock_guard lock(m_mutex); if (m_active) return; m_active = true; } m_thread = std::thread(&Notifier::ThreadMain, this); } void Notifier::Stop() { m_active = false; // send notification so the thread terminates m_cond.notify_one(); if (m_thread.joinable()) m_thread.join(); } void Notifier::ThreadMain() { std::unique_lock lock(m_mutex); while (m_active) { while (m_entry_notifications.empty() && m_conn_notifications.empty()) { m_cond.wait(lock); if (!m_active) return; } // Entry notifications while (!m_entry_notifications.empty()) { auto item = std::move(m_entry_notifications.front()); m_entry_notifications.pop(); if (!item.value) continue; StringRef name(item.name); if (item.only) { // Don't hold mutex during callback execution! lock.unlock(); item.only(0, name, item.value, item.flags); lock.lock(); continue; } // Use index because iterator might get invalidated. for (std::size_t i=0; i lock(m_mutex); unsigned int uid = m_entry_listeners.size(); m_entry_listeners.emplace_back(prefix, callback, flags); if ((flags & NT_NOTIFY_LOCAL) != 0) m_local_notifiers = true; return uid + 1; } void Notifier::RemoveEntryListener(unsigned int entry_listener_uid) { --entry_listener_uid; std::lock_guard lock(m_mutex); if (entry_listener_uid < m_entry_listeners.size()) m_entry_listeners[entry_listener_uid].callback = nullptr; } void Notifier::NotifyEntry(StringRef name, std::shared_ptr value, unsigned int flags, EntryListenerCallback only) { if (!m_active) return; // optimization: don't generate needless local queue entries if we have // no local listeners (as this is a common case on the server side) if ((flags & NT_NOTIFY_LOCAL) != 0 && !m_local_notifiers) return; std::unique_lock lock(m_mutex); m_entry_notifications.emplace(name, value, flags, only); lock.unlock(); m_cond.notify_one(); } unsigned int Notifier::AddConnectionListener( ConnectionListenerCallback callback) { std::lock_guard lock(m_mutex); unsigned int uid = m_entry_listeners.size(); m_conn_listeners.emplace_back(callback); return uid + 1; } void Notifier::RemoveConnectionListener(unsigned int conn_listener_uid) { --conn_listener_uid; std::lock_guard lock(m_mutex); if (conn_listener_uid < m_conn_listeners.size()) m_conn_listeners[conn_listener_uid] = nullptr; } void Notifier::NotifyConnection(bool connected, const ConnectionInfo& conn_info, ConnectionListenerCallback only) { if (!m_active) return; std::unique_lock lock(m_mutex); m_conn_notifications.emplace(connected, conn_info, only); lock.unlock(); m_cond.notify_one(); }