mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-20 00:51:42 +00:00
During shared library loading, a different libLLVM can be pulled in, causing llvm symbols from dependent libraries to resolve to that library instead of this one. This has been seen in the wild with the Mesa OpenGL implementation in JavaFX applications (see wpilibsuite/shuffleboard#361). This is clearly a very breaking change. For some level of backwards compatibility, a namespace alias from llvm to wpi is performed in the "llvm" headers. Unfortunately, forward declarations of llvm classes will still break, but compilers seem to generate clear error messages in those cases ("namespace alias 'llvm' not allowed here, assuming 'wpi'"). This change also moves all the wpiutil headers to a single "wpi" subdirectory from the previously split "llvm", "support", "tcpsockets", and "udpsockets". Shim headers will be added for backwards compatibility in a later commit.
235 lines
6.9 KiB
C++
235 lines
6.9 KiB
C++
/*----------------------------------------------------------------------------*/
|
|
/* Copyright (c) 2015-2018 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 <queue>
|
|
#include <vector>
|
|
|
|
#include "Handle.h"
|
|
#include "SinkImpl.h"
|
|
#include "SourceImpl.h"
|
|
|
|
using namespace cs;
|
|
|
|
bool Notifier::s_destroyed = false;
|
|
|
|
namespace {
|
|
// Vector which provides an integrated freelist for removal and reuse of
|
|
// individual elements.
|
|
template <typename T>
|
|
class UidVector {
|
|
public:
|
|
typedef typename std::vector<T>::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 <class... Args>
|
|
unsigned int emplace_back(Args&&... args) {
|
|
unsigned int uid;
|
|
if (m_free.empty()) {
|
|
uid = m_vector.size();
|
|
m_vector.emplace_back(std::forward<Args>(args)...);
|
|
} else {
|
|
uid = m_free.back();
|
|
m_free.pop_back();
|
|
m_vector[uid] = T(std::forward<Args>(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<T> m_vector;
|
|
std::vector<unsigned int> m_free;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
class Notifier::Thread : public wpi::SafeThread {
|
|
public:
|
|
Thread(std::function<void()> on_start, std::function<void()> on_exit)
|
|
: m_on_start(on_start), m_on_exit(on_exit) {}
|
|
|
|
void Main();
|
|
|
|
struct Listener {
|
|
Listener() = default;
|
|
Listener(std::function<void(const RawEvent& event)> callback_,
|
|
int eventMask_)
|
|
: callback(callback_), eventMask(eventMask_) {}
|
|
|
|
explicit operator bool() const { return static_cast<bool>(callback); }
|
|
|
|
std::string prefix;
|
|
std::function<void(const RawEvent& event)> callback;
|
|
int eventMask;
|
|
};
|
|
UidVector<Listener> m_listeners;
|
|
|
|
std::queue<RawEvent> m_notifications;
|
|
|
|
std::function<void()> m_on_start;
|
|
std::function<void()> 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<wpi::mutex> 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<void(const RawEvent& event)> 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(wpi::StringRef name, CS_Source source,
|
|
CS_EventKind kind) {
|
|
auto thr = m_owner.GetThread();
|
|
if (!thr) return;
|
|
thr->m_notifications.emplace(name, source, static_cast<RawEvent::Kind>(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,
|
|
wpi::StringRef propertyName, int property,
|
|
CS_PropertyKind propertyKind, int value,
|
|
wpi::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<RawEvent::Kind>(kind),
|
|
Handle{handleData.first, property, Handle::kProperty}, propertyKind,
|
|
value, valueStr);
|
|
thr->m_cond.notify_one();
|
|
}
|
|
|
|
void Notifier::NotifySink(wpi::StringRef name, CS_Sink sink,
|
|
CS_EventKind kind) {
|
|
auto thr = m_owner.GetThread();
|
|
if (!thr) return;
|
|
|
|
thr->m_notifications.emplace(name, sink, static_cast<RawEvent::Kind>(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(wpi::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();
|
|
}
|
|
|
|
void Notifier::NotifyTelemetryUpdated() {
|
|
auto thr = m_owner.GetThread();
|
|
if (!thr) return;
|
|
|
|
thr->m_notifications.emplace(RawEvent::kTelemetryUpdated);
|
|
thr->m_cond.notify_one();
|
|
}
|