/*----------------------------------------------------------------------------*/ /* Copyright (c) 2015-2017 FIRST. 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" #include #include #include "Handle.h" #include "SinkImpl.h" #include "SourceImpl.h" using namespace cs; ATOMIC_STATIC_INIT(Notifier) bool Notifier::s_destroyed = false; namespace { // Vector which provides an integrated freelist for removal and reuse of // individual elements. template class UidVector { public: typedef typename std::vector::size_type size_type; size_type size() const { return m_vector.size(); } T& operator[](size_type i) { return m_vector[i]; } const T& operator[](size_type i) const { return m_vector[i]; } // Add a new T to the vector. If there are elements on the freelist, // reuses the last one; otherwise adds to the end of the vector. // Returns the resulting element index (+1). template unsigned int emplace_back(Args&&... args) { unsigned int uid; if (m_free.empty()) { uid = m_vector.size(); m_vector.emplace_back(std::forward(args)...); } else { uid = m_free.back(); m_free.pop_back(); m_vector[uid] = T(std::forward(args)...); } return uid + 1; } // Removes the identified element by replacing it with a default-constructed // one. The element is added to the freelist for later reuse. void erase(unsigned int uid) { --uid; if (uid >= m_vector.size() || !m_vector[uid]) return; m_free.push_back(uid); m_vector[uid] = T(); } private: std::vector m_vector; std::vector m_free; }; } // anonymous namespace class Notifier::Thread : public wpi::SafeThread { public: Thread(std::function on_start, std::function on_exit) : m_on_start(on_start), m_on_exit(on_exit) {} void Main(); struct Listener { Listener() = default; Listener(std::function callback_, int eventMask_) : callback(callback_), eventMask(eventMask_) {} explicit operator bool() const { return static_cast(callback); } std::string prefix; std::function callback; int eventMask; }; UidVector m_listeners; std::queue m_notifications; std::function m_on_start; std::function m_on_exit; }; Notifier::Notifier() { s_destroyed = false; } Notifier::~Notifier() { s_destroyed = true; } void Notifier::Start() { auto thr = m_owner.GetThread(); if (!thr) m_owner.Start(new Thread(m_on_start, m_on_exit)); } void Notifier::Stop() { m_owner.Stop(); } void Notifier::Thread::Main() { if (m_on_start) m_on_start(); std::unique_lock lock(m_mutex); while (m_active) { while (m_notifications.empty()) { m_cond.wait(lock); if (!m_active) goto done; } while (!m_notifications.empty()) { if (!m_active) goto done; auto item = std::move(m_notifications.front()); m_notifications.pop(); // Use index because iterator might get invalidated. for (size_t i = 0; i < m_listeners.size(); ++i) { if (!m_listeners[i]) continue; // removed // Event type must be within requested set for this listener. if ((item.kind & m_listeners[i].eventMask) == 0) continue; // make a copy of the callback so we can safely release the mutex auto callback = m_listeners[i].callback; // Don't hold mutex during callback execution! lock.unlock(); callback(item); lock.lock(); } } } done: if (m_on_exit) m_on_exit(); } int Notifier::AddListener(std::function callback, int eventMask) { Start(); auto thr = m_owner.GetThread(); return thr->m_listeners.emplace_back(callback, eventMask); } void Notifier::RemoveListener(int uid) { auto thr = m_owner.GetThread(); if (!thr) return; thr->m_listeners.erase(uid); } void Notifier::NotifySource(llvm::StringRef name, CS_Source source, CS_EventKind kind) { auto thr = m_owner.GetThread(); if (!thr) return; thr->m_notifications.emplace(name, source, static_cast(kind)); thr->m_cond.notify_one(); } void Notifier::NotifySource(const SourceImpl& source, CS_EventKind kind) { auto handleData = Sources::GetInstance().Find(source); NotifySource(source.GetName(), handleData.first, kind); } void Notifier::NotifySourceVideoMode(const SourceImpl& source, const VideoMode& mode) { auto thr = m_owner.GetThread(); if (!thr) return; auto handleData = Sources::GetInstance().Find(source); thr->m_notifications.emplace(source.GetName(), handleData.first, mode); thr->m_cond.notify_one(); } void Notifier::NotifySourceProperty(const SourceImpl& source, CS_EventKind kind, llvm::StringRef propertyName, int property, CS_PropertyKind propertyKind, int value, llvm::StringRef valueStr) { auto thr = m_owner.GetThread(); if (!thr) return; auto handleData = Sources::GetInstance().Find(source); thr->m_notifications.emplace( propertyName, handleData.first, static_cast(kind), Handle{handleData.first, property, Handle::kProperty}, propertyKind, value, valueStr); thr->m_cond.notify_one(); } void Notifier::NotifySink(llvm::StringRef name, CS_Sink sink, CS_EventKind kind) { auto thr = m_owner.GetThread(); if (!thr) return; thr->m_notifications.emplace(name, sink, static_cast(kind)); thr->m_cond.notify_one(); } void Notifier::NotifySink(const SinkImpl& sink, CS_EventKind kind) { auto handleData = Sinks::GetInstance().Find(sink); NotifySink(sink.GetName(), handleData.first, kind); } void Notifier::NotifySinkSourceChanged(llvm::StringRef name, CS_Sink sink, CS_Source source) { auto thr = m_owner.GetThread(); if (!thr) return; RawEvent event{name, sink, RawEvent::kSinkSourceChanged}; event.sourceHandle = source; thr->m_notifications.emplace(std::move(event)); thr->m_cond.notify_one(); } void Notifier::NotifyNetworkInterfacesChanged() { auto thr = m_owner.GetThread(); if (!thr) return; thr->m_notifications.emplace(RawEvent::kNetworkInterfacesChanged); thr->m_cond.notify_one(); }