[ntcore] Revamp listeners (#4511)

- In both C++ and Java, add listener functions to Instance class (same as NT3 provided)
- Add WaitForListenerQueue functions (same as NT3 provided)
- Move Java non-poller implementation to Instance (previously only handled single instance)
- Change C++ listeners to take non-const references for subscribers etc to help avoid footguns from use of temporary objects (also add doc comment)
- Fix Preferences making .type persistent
This commit is contained in:
Peter Johnson
2022-10-24 23:27:24 -07:00
committed by GitHub
parent dcfa85a5d5
commit 794669b346
35 changed files with 1222 additions and 589 deletions

View File

@@ -65,6 +65,8 @@ class ListenerThread final : public wpi::SafeThreadEvent {
wpi::DenseMap<NT_ConnectionListener,
std::function<void(const ConnectionNotification& event)>>
m_callbacks;
wpi::Event m_waitQueueWakeup;
wpi::Event m_waitQueueWaiter;
};
class CLImpl {
@@ -97,9 +99,10 @@ class CLImpl {
void ListenerThread::Main() {
while (m_active) {
WPI_Handle signaledBuf[2];
auto signaled =
wpi::WaitForObjects({m_poller, m_stopEvent.GetHandle()}, signaledBuf);
WPI_Handle signaledBuf[3];
auto signaled = wpi::WaitForObjects(
{m_poller, m_stopEvent.GetHandle(), m_waitQueueWakeup.GetHandle()},
signaledBuf);
if (signaled.empty() || !m_active) {
return;
}
@@ -118,6 +121,10 @@ void ListenerThread::Main() {
lock.lock();
}
}
if (std::find(signaled.begin(), signaled.end(),
m_waitQueueWakeup.GetHandle()) != signaled.end()) {
m_waitQueueWaiter.Set();
}
}
}
@@ -279,12 +286,25 @@ bool ConnectionList::IsConnected() const {
}
NT_ConnectionListener ConnectionList::AddListener(
std::function<void(const ConnectionNotification& event)> callback,
bool immediateNotify) {
bool immediateNotify,
std::function<void(const ConnectionNotification& event)> callback) {
std::scoped_lock lock{m_mutex};
return m_impl->AddListener(std::move(callback), immediateNotify);
}
bool ConnectionList::WaitForListenerQueue(double timeout) {
std::scoped_lock lock{m_mutex};
WPI_EventHandle h;
if (auto thr = m_impl->m_listenerThread.GetThread()) {
h = thr->m_waitQueueWaiter.GetHandle();
thr->m_waitQueueWakeup.Set();
} else {
return false;
}
bool timedOut;
return wpi::WaitForObject(h, timeout, &timedOut);
}
NT_ConnectionListenerPoller ConnectionList::CreateListenerPoller() {
std::scoped_lock lock{m_mutex};
return m_impl->CreateListenerPoller()->handle;

View File

@@ -31,8 +31,9 @@ class ConnectionList final : public IConnectionList {
bool IsConnected() const final;
NT_ConnectionListener AddListener(
std::function<void(const ConnectionNotification& event)> callback,
bool immediateNotify);
bool immediateNotify,
std::function<void(const ConnectionNotification& event)> callback);
bool WaitForListenerQueue(double timeout);
NT_ConnectionListenerPoller CreateListenerPoller();
void DestroyListenerPoller(NT_ConnectionListenerPoller pollerHandle);

View File

@@ -298,6 +298,8 @@ struct TopicListenerThread final : public wpi::SafeThreadEvent {
wpi::DenseMap<NT_TopicListener,
std::function<void(const TopicNotification& event)>>
m_callbacks;
wpi::Event m_waitQueueWakeup;
wpi::Event m_waitQueueWaiter;
};
struct ValueListenerThread final : public wpi::SafeThreadEvent {
@@ -312,6 +314,8 @@ struct ValueListenerThread final : public wpi::SafeThreadEvent {
wpi::DenseMap<NT_ValueListener,
std::function<void(const ValueNotification& event)>>
m_callbacks;
wpi::Event m_waitQueueWakeup;
wpi::Event m_waitQueueWaiter;
};
struct LSImpl {
@@ -531,9 +535,10 @@ void SubscriberData::UpdateActive() {
void TopicListenerThread::Main() {
while (m_active) {
WPI_Handle signaledBuf[2];
auto signaled =
wpi::WaitForObjects({m_poller, m_stopEvent.GetHandle()}, signaledBuf);
WPI_Handle signaledBuf[3];
auto signaled = wpi::WaitForObjects(
{m_poller, m_stopEvent.GetHandle(), m_waitQueueWakeup.GetHandle()},
signaledBuf);
if (signaled.empty() || !m_active) {
break;
}
@@ -552,14 +557,19 @@ void TopicListenerThread::Main() {
lock.lock();
}
}
if (std::find(signaled.begin(), signaled.end(),
m_waitQueueWakeup.GetHandle()) != signaled.end()) {
m_waitQueueWaiter.Set();
}
}
}
void ValueListenerThread::Main() {
while (m_active) {
WPI_Handle signaledBuf[2];
auto signaled =
wpi::WaitForObjects({m_poller, m_stopEvent.GetHandle()}, signaledBuf);
WPI_Handle signaledBuf[3];
auto signaled = wpi::WaitForObjects(
{m_poller, m_stopEvent.GetHandle(), m_waitQueueWakeup.GetHandle()},
signaledBuf);
if (signaled.empty() || !m_active) {
break;
}
@@ -578,6 +588,10 @@ void ValueListenerThread::Main() {
lock.lock();
}
}
if (std::find(signaled.begin(), signaled.end(),
m_waitQueueWakeup.GetHandle()) != signaled.end()) {
m_waitQueueWaiter.Set();
}
}
}
@@ -2365,6 +2379,19 @@ NT_TopicListener LocalStorage::AddTopicListener(
return m_impl->AddTopicListener(topicHandle, mask, std::move(callback));
}
bool LocalStorage::WaitForTopicListenerQueue(double timeout) {
std::scoped_lock lock{m_mutex};
WPI_EventHandle h;
if (auto thr = m_impl->m_topicListenerThread.GetThread()) {
h = thr->m_waitQueueWaiter.GetHandle();
thr->m_waitQueueWakeup.Set();
} else {
return false;
}
bool timedOut;
return wpi::WaitForObject(h, timeout, &timedOut);
}
NT_TopicListenerPoller LocalStorage::CreateTopicListenerPoller() {
std::scoped_lock lock{m_mutex};
return m_impl->AddTopicListenerPoller()->handle;
@@ -2423,6 +2450,19 @@ NT_ValueListener LocalStorage::AddValueListener(
return m_impl->AddValueListener(subentry, mask, std::move(callback));
}
bool LocalStorage::WaitForValueListenerQueue(double timeout) {
std::scoped_lock lock{m_mutex};
WPI_EventHandle h;
if (auto thr = m_impl->m_valueListenerThread.GetThread()) {
h = thr->m_waitQueueWaiter.GetHandle();
thr->m_waitQueueWakeup.Set();
} else {
return false;
}
bool timedOut;
return wpi::WaitForObject(h, timeout, &timedOut);
}
NT_ValueListenerPoller LocalStorage::CreateValueListenerPoller() {
std::scoped_lock lock{m_mutex};
return m_impl->AddValueListenerPoller()->handle;

View File

@@ -198,6 +198,7 @@ class LocalStorage final : public net::ILocalStorage {
NT_TopicListener AddTopicListener(
NT_Handle handle, unsigned int mask,
std::function<void(const TopicNotification&)> callback);
bool WaitForTopicListenerQueue(double timeout);
NT_TopicListenerPoller CreateTopicListenerPoller();
void DestroyTopicListenerPoller(NT_TopicListenerPoller poller);
@@ -220,6 +221,7 @@ class LocalStorage final : public net::ILocalStorage {
NT_ValueListener AddValueListener(
NT_Handle subentry, unsigned int mask,
std::function<void(const ValueNotification&)> callback);
bool WaitForValueListenerQueue(double timeout);
NT_ValueListenerPoller CreateValueListenerPoller();
void DestroyValueListenerPoller(NT_ValueListenerPoller poller);

View File

@@ -15,6 +15,7 @@
#include "networktables/FloatTopic.h"
#include "networktables/IntegerArrayTopic.h"
#include "networktables/IntegerTopic.h"
#include "networktables/MultiSubscriber.h"
#include "networktables/RawTopic.h"
#include "networktables/StringArrayTopic.h"
#include "networktables/StringTopic.h"
@@ -100,8 +101,16 @@ void NetworkTableInstance::SetServer(std::span<const std::string_view> servers,
SetServer(serversArr);
}
NT_ConnectionListener NetworkTableInstance::AddConnectionListener(
std::function<void(const ConnectionNotification& event)> callback,
bool immediate_notify) const {
return ::nt::AddConnectionListener(m_handle, callback, immediate_notify);
NT_TopicListener NetworkTableInstance::AddTopicListener(
MultiSubscriber& subscriber, int eventMask,
std::function<void(const TopicNotification&)> listener) {
return ::nt::AddTopicListener(subscriber.GetHandle(), eventMask,
std::move(listener));
}
NT_ValueListener NetworkTableInstance::AddValueListener(
MultiSubscriber& subscriber, unsigned int eventMask,
std::function<void(const ValueNotification&)> listener) {
return ::nt::AddValueListener(subscriber.GetHandle(), eventMask,
std::move(listener));
}

View File

@@ -423,6 +423,10 @@ NT_TopicListener NT_AddTopicListenerSingle(NT_Topic topic, unsigned int mask,
});
}
NT_Bool NT_WaitForTopicListenerQueue(NT_Handle handle, double timeout) {
return nt::WaitForTopicListenerQueue(handle, timeout);
}
NT_TopicListenerPoller NT_CreateTopicListenerPoller(NT_Inst inst) {
return nt::CreateTopicListenerPoller(inst);
}
@@ -477,6 +481,10 @@ NT_ValueListener NT_AddValueListener(NT_Handle subentry, unsigned int mask,
});
}
NT_Bool NT_WaitForValueListenerQueue(NT_Handle handle, double timeout) {
return nt::WaitForValueListenerQueue(handle, timeout);
}
NT_ValueListenerPoller NT_CreateValueListenerPoller(NT_Inst inst) {
return nt::CreateValueListenerPoller(inst);
}
@@ -502,17 +510,19 @@ void NT_RemoveValueListener(NT_ValueListener value_listener) {
}
NT_ConnectionListener NT_AddConnectionListener(
NT_Inst inst, void* data, NT_ConnectionListenerCallback callback,
NT_Bool immediate_notify) {
return nt::AddConnectionListener(
inst,
[=](const ConnectionNotification& event) {
NT_ConnectionNotification event_c;
ConvertToC(event, &event_c);
callback(data, &event_c);
DisposeConnectionNotification(&event_c);
},
immediate_notify != 0);
NT_Inst inst, NT_Bool immediate_notify, void* data,
NT_ConnectionListenerCallback callback) {
return nt::AddConnectionListener(inst, immediate_notify != 0,
[=](const ConnectionNotification& event) {
NT_ConnectionNotification event_c;
ConvertToC(event, &event_c);
callback(data, &event_c);
DisposeConnectionNotification(&event_c);
});
}
NT_Bool NT_WaitForConnectionListenerQueue(NT_Handle handle, double timeout) {
return nt::WaitForConnectionListenerQueue(handle, timeout);
}
NT_ConnectionListenerPoller NT_CreateConnectionListenerPoller(NT_Inst inst) {

View File

@@ -407,6 +407,14 @@ NT_TopicListener AddTopicListener(
}
}
bool WaitForTopicListenerQueue(NT_Handle handle, double timeout) {
if (auto ii = InstanceImpl::GetHandle(handle)) {
return ii->localStorage.WaitForTopicListenerQueue(timeout);
} else {
return {};
}
}
NT_TopicListenerPoller CreateTopicListenerPoller(NT_Inst inst) {
if (auto ii = InstanceImpl::GetTyped(inst, Handle::kInstance)) {
return ii->localStorage.CreateTopicListenerPoller();
@@ -466,6 +474,14 @@ NT_ValueListener AddValueListener(
}
}
bool WaitForValueListenerQueue(NT_Handle handle, double timeout) {
if (auto ii = InstanceImpl::GetHandle(handle)) {
return ii->localStorage.WaitForValueListenerQueue(timeout);
} else {
return {};
}
}
NT_ValueListenerPoller CreateValueListenerPoller(NT_Inst inst) {
if (auto ii = InstanceImpl::GetTyped(inst, Handle::kInstance)) {
return ii->localStorage.CreateValueListenerPoller();
@@ -505,12 +521,19 @@ void RemoveValueListener(NT_ValueListener listener) {
}
NT_ConnectionListener AddConnectionListener(
NT_Inst inst,
std::function<void(const ConnectionNotification& event)> callback,
bool immediate_notify) {
NT_Inst inst, bool immediate_notify,
std::function<void(const ConnectionNotification& event)> callback) {
if (auto ii = InstanceImpl::GetTyped(inst, Handle::kInstance)) {
return ii->connectionList.AddListener(std::move(callback),
immediate_notify);
return ii->connectionList.AddListener(immediate_notify,
std::move(callback));
} else {
return {};
}
}
bool WaitForConnectionListenerQueue(NT_Handle handle, double timeout) {
if (auto ii = InstanceImpl::GetHandle(handle)) {
return ii->connectionList.WaitForListenerQueue(timeout);
} else {
return {};
}

View File

@@ -28,7 +28,7 @@ class ConnectionListener final {
*
* @param inst Instance
* @param immediateNotify if notification should be immediately created for
* existing connections
* existing connections
* @param listener Listener function
*/
ConnectionListener(
@@ -50,6 +50,18 @@ class ConnectionListener final {
*/
NT_ConnectionListener GetHandle() const { return m_handle; }
/**
* Wait for the connection listener queue to be empty. This is primarily
* useful for deterministic testing. This blocks until either the connection
* listener queue is empty (e.g. there are no more events that need to be
* passed along to callbacks or poll queues) or the timeout expires.
*
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior, or
* a negative value to block indefinitely
* @return False if timed out, otherwise true.
*/
bool WaitForQueue(double timeout);
private:
NT_ConnectionListener m_handle{0};
};

View File

@@ -17,7 +17,7 @@ inline ConnectionListener::ConnectionListener(
NetworkTableInstance inst, bool immediateNotify,
std::function<void(const ConnectionNotification&)> listener)
: m_handle{
AddConnectionListener(inst.GetHandle(), listener, immediateNotify)} {}
AddConnectionListener(inst.GetHandle(), immediateNotify, listener)} {}
inline ConnectionListener::ConnectionListener(ConnectionListener&& rhs)
: m_handle(rhs.m_handle) {
@@ -36,6 +36,14 @@ inline ConnectionListener::~ConnectionListener() {
}
}
inline bool ConnectionListener::WaitForQueue(double timeout) {
if (m_handle != 0) {
return nt::WaitForConnectionListenerQueue(m_handle, timeout);
} else {
return false;
}
}
inline ConnectionListenerPoller::ConnectionListenerPoller(
NetworkTableInstance inst)
: m_handle(nt::CreateConnectionListenerPoller(inst.GetHandle())) {}

View File

@@ -27,9 +27,11 @@ class FloatArrayTopic;
class FloatTopic;
class IntegerArrayTopic;
class IntegerTopic;
class MultiSubscriber;
class RawTopic;
class StringArrayTopic;
class StringTopic;
class Subscriber;
class Topic;
/**
@@ -359,15 +361,17 @@ class NetworkTableInstance final {
*/
/**
* Add a connection listener.
* Add a connection listener. The callback function is called asynchronously
* on a separate thread, so it's important to use synchronization or atomics
* when accessing any shared state from the callback function.
*
* @param callback listener to add
* @param immediate_notify notify listener of all existing connections
* @param callback listener to add
* @return Listener handle
*/
NT_ConnectionListener AddConnectionListener(
std::function<void(const ConnectionNotification& event)> callback,
bool immediate_notify) const;
bool immediate_notify,
std::function<void(const ConnectionNotification& event)> callback) const;
/**
* Remove a connection listener.
@@ -376,6 +380,190 @@ class NetworkTableInstance final {
*/
static void RemoveConnectionListener(NT_ConnectionListener conn_listener);
/**
* Wait for the connection listener queue to be empty. This is primarily
* useful for deterministic testing. This blocks until either the connection
* listener queue is empty (e.g. there are no more events that need to be
* passed along to callbacks or poll queues) or the timeout expires.
*
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior, or
* a negative value to block indefinitely
* @return False if timed out, otherwise true.
*/
bool WaitForConnectionListenerQueue(double timeout);
/** @} */
/**
* @{
* @name Topic Listener Functions
*/
/**
* Add a topic listener for changes on a particular topic. The callback
* function is called asynchronously on a separate thread, so it's important
* to use synchronization or atomics when accessing any shared state from the
* callback function.
*
* @param topic Topic
* @param eventMask Bitmask of TopicListenerFlags values
* @param listener Listener function
* @return Listener handle
*/
NT_TopicListener AddTopicListener(
Topic topic, unsigned int eventMask,
std::function<void(const TopicNotification&)> listener);
/**
* Add a topic listener for topic changes on a subscriber. The callback
* function is called asynchronously on a separate thread, so it's important
* to use synchronization or atomics when accessing any shared state from the
* callback function. This does NOT keep the subscriber active.
*
* @param subscriber Subscriber
* @param eventMask Bitmask of TopicListenerFlags values
* @param listener Listener function
* @return Listener handle
*/
NT_TopicListener AddTopicListener(
Subscriber& subscriber, unsigned int eventMask,
std::function<void(const TopicNotification&)> listener);
/**
* Add a topic listener for topic changes on a subscriber. The callback
* function is called asynchronously on a separate thread, so it's important
* to use synchronization or atomics when accessing any shared state from the
* callback function. This does NOT keep the subscriber active.
*
* @param subscriber Subscriber
* @param eventMask Bitmask of TopicListenerFlags values
* @param listener Listener function
* @return Listener handle
*/
NT_TopicListener AddTopicListener(
MultiSubscriber& subscriber, int eventMask,
std::function<void(const TopicNotification&)> listener);
/**
* Add a topic listener for topic changes on an entry. The callback function
* is called asynchronously on a separate thread, so it's important to use
* synchronization or atomics when accessing any shared state from the
* callback function.
*
* @param entry Entry
* @param eventMask Bitmask of TopicListenerFlags values
* @param listener Listener function
* @return Listener handle
*/
NT_TopicListener AddTopicListener(
NetworkTableEntry& entry, int eventMask,
std::function<void(const TopicNotification&)> listener);
/**
* Add a topic listener for changes to topics with names that start with any
* of the given prefixes. The callback function is called asynchronously on a
* separate thread, so it's important to use synchronization or atomics when
* accessing any shared state from the callback function.
*
* @param prefixes Topic name string prefixes
* @param eventMask Bitmask of TopicListenerFlags values
* @param listener Listener function
* @return Listener handle
*/
NT_TopicListener AddTopicListener(
std::span<const std::string_view> prefixes, int eventMask,
std::function<void(const TopicNotification&)> listener);
/**
* Remove a topic listener.
*
* @param listener Listener handle to remove
*/
static void RemoveTopicListener(NT_TopicListener listener);
/**
* Wait for the topic listener queue to be empty. This is primarily useful for
* deterministic testing. This blocks until either the topic listener queue is
* empty (e.g. there are no more events that need to be passed along to
* callbacks or poll queues) or the timeout expires.
*
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior, or
* a negative value to block indefinitely
* @return False if timed out, otherwise true.
*/
bool WaitForTopicListenerQueue(double timeout);
/** @} */
/**
* @{
* @name Value Listener Functions
*/
/**
* Add a value listener for value changes on a subscriber. The callback
* function is called asynchronously on a separate thread, so it's important
* to use synchronization or atomics when accessing any shared state from the
* callback function. This does NOT keep the subscriber active.
*
* @param subscriber Subscriber
* @param eventMask Bitmask of ValueListenerFlags values
* @param listener Listener function
* @return Listener handle
*/
NT_ValueListener AddValueListener(
Subscriber& subscriber, unsigned int eventMask,
std::function<void(const ValueNotification&)> listener);
/**
* Add a value listener for value changes on a subscriber. The callback
* function is called asynchronously on a separate thread, so it's important
* to use synchronization or atomics when accessing any shared state from the
* callback function. This does NOT keep the subscriber active.
*
* @param subscriber Subscriber
* @param eventMask Bitmask of ValueListenerFlags values
* @param listener Listener function
* @return Listener handle
*/
NT_ValueListener AddValueListener(
MultiSubscriber& subscriber, unsigned int eventMask,
std::function<void(const ValueNotification&)> listener);
/**
* Add a value listener for value changes on an entry. The callback function
* is called asynchronously on a separate thread, so it's important to use
* synchronization or atomics when accessing any shared state from the
* callback function.
*
* @param entry Entry
* @param eventMask Bitmask of ValueListenerFlags values
* @param listener Listener function
* @return Listener handle
*/
NT_ValueListener AddValueListener(
NetworkTableEntry& entry, int eventMask,
std::function<void(const ValueNotification&)> listener);
/**
* Remove a value listener.
*
* @param listener Listener handle to remove
*/
static void RemoveValueListener(NT_ValueListener listener);
/**
* Wait for the value listener queue to be empty. This is primarily useful for
* deterministic testing. This blocks until either the value listener queue is
* empty (e.g. there are no more events that need to be passed along to
* callbacks or poll queues) or the timeout expires.
*
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior, or
* a negative value to block indefinitely
* @return False if timed out, otherwise true.
*/
bool WaitForValueListenerQueue(double timeout);
/** @} */
/**

View File

@@ -82,11 +82,83 @@ inline NetworkTableEntry NetworkTableInstance::GetEntry(std::string_view name) {
return NetworkTableEntry{::nt::GetEntry(m_handle, name)};
}
inline NT_ConnectionListener NetworkTableInstance::AddConnectionListener(
bool immediate_notify,
std::function<void(const ConnectionNotification& event)> callback) const {
return ::nt::AddConnectionListener(m_handle, immediate_notify,
std::move(callback));
}
inline bool NetworkTableInstance::WaitForConnectionListenerQueue(
double timeout) {
return ::nt::WaitForConnectionListenerQueue(m_handle, timeout);
}
inline void NetworkTableInstance::RemoveConnectionListener(
NT_ConnectionListener conn_listener) {
::nt::RemoveConnectionListener(conn_listener);
}
inline NT_TopicListener NetworkTableInstance::AddTopicListener(
Topic topic, unsigned int eventMask,
std::function<void(const TopicNotification&)> listener) {
return ::nt::AddTopicListener(topic.GetHandle(), eventMask,
std::move(listener));
}
inline NT_TopicListener NetworkTableInstance::AddTopicListener(
Subscriber& subscriber, unsigned int eventMask,
std::function<void(const TopicNotification&)> listener) {
return ::nt::AddTopicListener(subscriber.GetHandle(), eventMask,
std::move(listener));
}
inline NT_TopicListener NetworkTableInstance::AddTopicListener(
NetworkTableEntry& entry, int eventMask,
std::function<void(const TopicNotification&)> listener) {
return ::nt::AddTopicListener(entry.GetHandle(), eventMask,
std::move(listener));
}
inline NT_TopicListener NetworkTableInstance::AddTopicListener(
std::span<const std::string_view> prefixes, int eventMask,
std::function<void(const TopicNotification&)> listener) {
return ::nt::AddTopicListener(m_handle, prefixes, eventMask,
std::move(listener));
}
inline void NetworkTableInstance::RemoveTopicListener(
NT_TopicListener listener) {
return ::nt::RemoveTopicListener(listener);
}
inline bool NetworkTableInstance::WaitForTopicListenerQueue(double timeout) {
return ::nt::WaitForTopicListenerQueue(m_handle, timeout);
}
inline NT_ValueListener NetworkTableInstance::AddValueListener(
Subscriber& subscriber, unsigned int eventMask,
std::function<void(const ValueNotification&)> listener) {
return ::nt::AddValueListener(subscriber.GetHandle(), eventMask,
std::move(listener));
}
inline NT_ValueListener NetworkTableInstance::AddValueListener(
NetworkTableEntry& entry, int eventMask,
std::function<void(const ValueNotification&)> listener) {
return ::nt::AddValueListener(entry.GetHandle(), eventMask,
std::move(listener));
}
inline void NetworkTableInstance::RemoveValueListener(
NT_ValueListener listener) {
::nt::RemoveValueListener(listener);
}
inline bool NetworkTableInstance::WaitForValueListenerQueue(double timeout) {
return ::nt::WaitForValueListenerQueue(m_handle, timeout);
}
inline unsigned int NetworkTableInstance::GetNetworkMode() const {
return ::nt::GetNetworkMode(m_handle);
}

View File

@@ -98,23 +98,25 @@ class TopicListener final {
std::function<void(const TopicNotification&)> listener);
/**
* Create a listener for topic changes on a subscriber.
* Create a listener for topic changes on a subscriber. This does NOT keep the
* subscriber active.
*
* @param subscriber Subscriber
* @param mask Bitmask of TopicListenerFlags values
* @param listener Listener function
*/
TopicListener(const Subscriber& subscriber, unsigned int mask,
TopicListener(Subscriber& subscriber, unsigned int mask,
std::function<void(const TopicNotification&)> listener);
/**
* Create a listener for topic changes on a subscriber.
* Create a listener for topic changes on a subscriber. This does NOT keep the
* subscriber active.
*
* @param subscriber Subscriber
* @param mask Bitmask of TopicListenerFlags values
* @param listener Listener function
*/
TopicListener(const MultiSubscriber& subscriber, unsigned int mask,
TopicListener(MultiSubscriber& subscriber, unsigned int mask,
std::function<void(const TopicNotification&)> listener);
/**
@@ -124,7 +126,7 @@ class TopicListener final {
* @param mask Bitmask of TopicListenerFlags values
* @param listener Listener function
*/
TopicListener(const NetworkTableEntry& entry, unsigned int mask,
TopicListener(NetworkTableEntry& entry, unsigned int mask,
std::function<void(const TopicNotification&)> listener);
TopicListener(const TopicListener&) = delete;
@@ -142,6 +144,18 @@ class TopicListener final {
*/
NT_TopicListener GetHandle() const { return m_handle; }
/**
* Wait for the topic listener queue to be empty. This is primarily useful for
* deterministic testing. This blocks until either the topic listener queue is
* empty (e.g. there are no more events that need to be passed along to
* callbacks or poll queues) or the timeout expires.
*
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior, or
* a negative value to block indefinitely
* @return False if timed out, otherwise true.
*/
bool WaitForQueue(double timeout);
private:
NT_TopicListener m_handle{0};
};
@@ -198,22 +212,24 @@ class TopicListenerPoller final {
NT_TopicListener Add(Topic topic, unsigned int mask);
/**
* Start listening to topic changes on a subscriber.
* Start listening to topic changes on a subscriber. This does NOT keep the
* subscriber active.
*
* @param subscriber Subscriber
* @param mask Bitmask of TopicListenerFlags values
* @return Listener handle
*/
NT_TopicListener Add(const Subscriber& subscriber, unsigned int mask);
NT_TopicListener Add(Subscriber& subscriber, unsigned int mask);
/**
* Start listening to topic changes on a subscriber.
* Start listening to topic changes on a subscriber. This does NOT keep the
* subscriber active.
*
* @param subscriber Subscriber
* @param mask Bitmask of TopicListenerFlags values
* @return Listener handle
*/
NT_TopicListener Add(const MultiSubscriber& subscriber, unsigned int mask);
NT_TopicListener Add(MultiSubscriber& subscriber, unsigned int mask);
/**
* Start listening to topic changes on an entry.
@@ -222,7 +238,7 @@ class TopicListenerPoller final {
* @param mask Bitmask of TopicListenerFlags values
* @return Listener handle
*/
NT_TopicListener Add(const NetworkTableEntry& entry, unsigned int mask);
NT_TopicListener Add(NetworkTableEntry& entry, unsigned int mask);
/**
* Remove a listener.

View File

@@ -29,17 +29,17 @@ inline TopicListener::TopicListener(
: m_handle{AddTopicListener(topic.GetHandle(), mask, listener)} {}
inline TopicListener::TopicListener(
const Subscriber& subscriber, unsigned int mask,
Subscriber& subscriber, unsigned int mask,
std::function<void(const TopicNotification&)> listener)
: m_handle{AddTopicListener(subscriber.GetHandle(), mask, listener)} {}
inline TopicListener::TopicListener(
const MultiSubscriber& subscriber, unsigned int mask,
MultiSubscriber& subscriber, unsigned int mask,
std::function<void(const TopicNotification&)> listener)
: m_handle{AddTopicListener(subscriber.GetHandle(), mask, listener)} {}
inline TopicListener::TopicListener(
const NetworkTableEntry& entry, unsigned int mask,
NetworkTableEntry& entry, unsigned int mask,
std::function<void(const TopicNotification&)> listener)
: m_handle{AddTopicListener(entry.GetHandle(), mask, listener)} {}
@@ -59,6 +59,14 @@ inline TopicListener::~TopicListener() {
}
}
inline bool TopicListener::WaitForQueue(double timeout) {
if (m_handle != 0) {
return nt::WaitForTopicListenerQueue(m_handle, timeout);
} else {
return false;
}
}
inline TopicListenerPoller::TopicListenerPoller(NetworkTableInstance inst)
: m_handle(nt::CreateTopicListenerPoller(inst.GetHandle())) {}
@@ -89,17 +97,17 @@ inline NT_TopicListener TopicListenerPoller::Add(Topic topic,
return nt::AddPolledTopicListener(m_handle, topic.GetHandle(), mask);
}
inline NT_TopicListener TopicListenerPoller::Add(const Subscriber& subscriber,
inline NT_TopicListener TopicListenerPoller::Add(Subscriber& subscriber,
unsigned int mask) {
return nt::AddPolledTopicListener(m_handle, subscriber.GetHandle(), mask);
}
inline NT_TopicListener TopicListenerPoller::Add(
const MultiSubscriber& subscriber, unsigned int mask) {
inline NT_TopicListener TopicListenerPoller::Add(MultiSubscriber& subscriber,
unsigned int mask) {
return nt::AddPolledTopicListener(m_handle, subscriber.GetHandle(), mask);
}
inline NT_TopicListener TopicListenerPoller::Add(const NetworkTableEntry& entry,
inline NT_TopicListener TopicListenerPoller::Add(NetworkTableEntry& entry,
unsigned int mask) {
return nt::AddPolledTopicListener(m_handle, entry.GetHandle(), mask);
}

View File

@@ -57,23 +57,25 @@ class ValueListener final {
ValueListener() = default;
/**
* Create a listener for value changes on a subscriber.
* Create a listener for value changes on a subscriber. This does NOT keep the
* subscriber active.
*
* @param subscriber Subscriber
* @param mask Bitmask of ValueListenerFlags values
* @param listener Listener function
*/
ValueListener(const Subscriber& subscriber, unsigned int mask,
ValueListener(Subscriber& subscriber, unsigned int mask,
std::function<void(const ValueNotification&)> listener);
/**
* Create a listener for value changes on a subscriber.
* Create a listener for value changes on a subscriber. This does NOT keep the
* subscriber active.
*
* @param subscriber Subscriber
* @param mask Bitmask of ValueListenerFlags values
* @param listener Listener function
*/
ValueListener(const MultiSubscriber& subscriber, unsigned int mask,
ValueListener(MultiSubscriber& subscriber, unsigned int mask,
std::function<void(const ValueNotification&)> listener);
/**
@@ -83,17 +85,7 @@ class ValueListener final {
* @param mask Bitmask of ValueListenerFlags values
* @param listener Listener function
*/
ValueListener(const NetworkTableEntry& entry, unsigned int mask,
std::function<void(const ValueNotification&)> listener);
/**
* Create a listener for value changes on a subscriber/entry handle.
*
* @param subentry Subscriber/entry handle
* @param mask Bitmask of ValueListenerFlags values
* @param listener Listener function
*/
ValueListener(NT_Handle subentry, unsigned int mask,
ValueListener(NetworkTableEntry& entry, unsigned int mask,
std::function<void(const ValueNotification&)> listener);
ValueListener(const ValueListener&) = delete;
@@ -111,6 +103,18 @@ class ValueListener final {
*/
NT_ValueListener GetHandle() const { return m_handle; }
/**
* Wait for the value listener queue to be empty. This is primarily useful for
* deterministic testing. This blocks until either the value listener queue is
* empty (e.g. there are no more events that need to be passed along to
* callbacks or poll queues) or the timeout expires.
*
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior, or
* a negative value to block indefinitely
* @return False if timed out, otherwise true.
*/
bool WaitForQueue(double timeout);
private:
NT_ValueListener m_handle{0};
};
@@ -147,22 +151,24 @@ class ValueListenerPoller final {
NT_ValueListenerPoller GetHandle() const { return m_handle; }
/**
* Start listening to value changes on a subscriber.
* Start listening to value changes on a subscriber. This does NOT keep the
* subscriber active.
*
* @param subscriber Subscriber
* @param mask Bitmask of ValueListenerFlags values
* @return Listener handle
*/
NT_ValueListener Add(const Subscriber& subscriber, unsigned int mask);
NT_ValueListener Add(Subscriber& subscriber, unsigned int mask);
/**
* Start listening to value changes on a subscriber.
* Start listening to value changes on a subscriber. This does NOT keep the
* subscriber active.
*
* @param subscriber Subscriber
* @param mask Bitmask of ValueListenerFlags values
* @return Listener handle
*/
NT_ValueListener Add(const MultiSubscriber& subscriber, unsigned int mask);
NT_ValueListener Add(MultiSubscriber& subscriber, unsigned int mask);
/**
* Start listening to value changes on an entry.
@@ -171,16 +177,7 @@ class ValueListenerPoller final {
* @param mask Bitmask of ValueListenerFlags values
* @return Listener handle
*/
NT_ValueListener Add(const NetworkTableEntry& entry, unsigned int mask);
/**
* Start listening to value changes on a subscriber/entry handle.
*
* @param subentry Subscriber/entry handle
* @param mask Bitmask of ValueListenerFlags values
* @return Listener handle
*/
NT_ValueListener Add(NT_Handle subentry, unsigned int mask);
NT_ValueListener Add(NetworkTableEntry& entry, unsigned int mask);
/**
* Remove a listener.

View File

@@ -17,25 +17,20 @@
namespace nt {
inline ValueListener::ValueListener(
const Subscriber& subscriber, unsigned int mask,
Subscriber& subscriber, unsigned int mask,
std::function<void(const ValueNotification&)> listener)
: m_handle{AddValueListener(subscriber.GetHandle(), mask, listener)} {}
inline ValueListener::ValueListener(
const MultiSubscriber& subscriber, unsigned int mask,
MultiSubscriber& subscriber, unsigned int mask,
std::function<void(const ValueNotification&)> listener)
: m_handle{AddValueListener(subscriber.GetHandle(), mask, listener)} {}
inline ValueListener::ValueListener(
const NetworkTableEntry& entry, unsigned int mask,
NetworkTableEntry& entry, unsigned int mask,
std::function<void(const ValueNotification&)> listener)
: m_handle{AddValueListener(entry.GetHandle(), mask, listener)} {}
inline ValueListener::ValueListener(
NT_Handle subentry, unsigned int mask,
std::function<void(const ValueNotification&)> listener)
: m_handle{AddValueListener(subentry, mask, listener)} {}
inline ValueListener::ValueListener(ValueListener&& rhs)
: m_handle(rhs.m_handle) {
rhs.m_handle = 0;
@@ -52,6 +47,14 @@ inline ValueListener::~ValueListener() {
}
}
inline bool ValueListener::WaitForQueue(double timeout) {
if (m_handle != 0) {
return nt::WaitForValueListenerQueue(m_handle, timeout);
} else {
return false;
}
}
inline ValueListenerPoller::ValueListenerPoller(NetworkTableInstance inst)
: m_handle(nt::CreateValueListenerPoller(inst.GetHandle())) {}
@@ -72,24 +75,19 @@ inline ValueListenerPoller::~ValueListenerPoller() {
}
}
inline NT_ValueListener ValueListenerPoller::Add(const Subscriber& subscriber,
inline NT_ValueListener ValueListenerPoller::Add(Subscriber& subscriber,
unsigned int mask) {
return Add(subscriber.GetHandle(), mask);
return nt::AddPolledValueListener(m_handle, subscriber.GetHandle(), mask);
}
inline NT_ValueListener ValueListenerPoller::Add(
const MultiSubscriber& subscriber, unsigned int mask) {
return Add(subscriber.GetHandle(), mask);
inline NT_ValueListener ValueListenerPoller::Add(MultiSubscriber& subscriber,
unsigned int mask) {
return nt::AddPolledValueListener(m_handle, subscriber.GetHandle(), mask);
}
inline NT_ValueListener ValueListenerPoller::Add(const NetworkTableEntry& entry,
inline NT_ValueListener ValueListenerPoller::Add(NetworkTableEntry& entry,
unsigned int mask) {
return Add(entry.GetHandle(), mask);
}
inline NT_ValueListener ValueListenerPoller::Add(NT_Handle subentry,
unsigned int mask) {
return nt::AddPolledValueListener(m_handle, subentry, mask);
return nt::AddPolledValueListener(m_handle, entry.GetHandle(), mask);
}
inline void ValueListenerPoller::Remove(NT_ValueListener listener) {

View File

@@ -862,6 +862,19 @@ NT_TopicListener NT_AddTopicListenerSingle(NT_Topic topic, unsigned int mask,
void* data,
NT_TopicListenerCallback callback);
/**
* Wait for the topic listener queue to be empty. This is primarily useful
* for deterministic testing. This blocks until either the topic listener
* queue is empty (e.g. there are no more events that need to be passed along to
* callbacks or poll queues) or the timeout expires.
*
* @param handle handle
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior, or a
* negative value to block indefinitely
* @return False if timed out, otherwise true.
*/
NT_Bool NT_WaitForTopicListenerQueue(NT_Handle handle, double timeout);
/**
* Creates a topic listener poller.
*
@@ -971,6 +984,19 @@ NT_ValueListener NT_AddValueListener(NT_Handle subentry, unsigned int mask,
void* data,
NT_ValueListenerCallback callback);
/**
* Wait for the value listener queue to be empty. This is primarily useful
* for deterministic testing. This blocks until either the value listener
* queue is empty (e.g. there are no more events that need to be passed along to
* callbacks or poll queues) or the timeout expires.
*
* @param handle handle
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior, or a
* negative value to block indefinitely
* @return False if timed out, otherwise true.
*/
NT_Bool NT_WaitForValueListenerQueue(NT_Handle handle, double timeout);
/**
* Create a value listener poller.
*
@@ -1050,8 +1076,21 @@ typedef void (*NT_ConnectionListenerCallback)(
* @return Listener handle
*/
NT_ConnectionListener NT_AddConnectionListener(
NT_Inst inst, void* data, NT_ConnectionListenerCallback callback,
NT_Bool immediate_notify);
NT_Inst inst, NT_Bool immediate_notify, void* data,
NT_ConnectionListenerCallback callback);
/**
* Wait for the connection listener queue to be empty. This is primarily useful
* for deterministic testing. This blocks until either the connection listener
* queue is empty (e.g. there are no more events that need to be passed along to
* callbacks or poll queues) or the timeout expires.
*
* @param handle handle
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior, or a
* negative value to block indefinitely
* @return False if timed out, otherwise true.
*/
NT_Bool NT_WaitForConnectionListenerQueue(NT_Handle handle, double timeout);
/**
* Create a connection listener poller.

View File

@@ -799,6 +799,19 @@ NT_TopicListener AddTopicListener(
NT_Handle handle, unsigned int mask,
std::function<void(const TopicNotification&)> callback);
/**
* Wait for the topic listener queue to be empty. This is primarily useful
* for deterministic testing. This blocks until either the topic listener
* queue is empty (e.g. there are no more events that need to be passed along to
* callbacks or poll queues) or the timeout expires.
*
* @param handle handle
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior, or a
* negative value to block indefinitely
* @return False if timed out, otherwise true.
*/
bool WaitForTopicListenerQueue(NT_Handle handle, double timeout);
/**
* Creates a topic listener poller.
*
@@ -880,6 +893,19 @@ NT_ValueListener AddValueListener(
NT_Handle subentry, unsigned int mask,
std::function<void(const ValueNotification&)> callback);
/**
* Wait for the value listener queue to be empty. This is primarily useful
* for deterministic testing. This blocks until either the value listener
* queue is empty (e.g. there are no more events that need to be passed along to
* callbacks or poll queues) or the timeout expires.
*
* @param handle handle
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior, or a
* negative value to block indefinitely
* @return False if timed out, otherwise true.
*/
bool WaitForValueListenerQueue(NT_Handle handle, double timeout);
/**
* Create a value listener poller.
*
@@ -945,9 +971,21 @@ void RemoveValueListener(NT_ValueListener listener);
* @return Listener handle
*/
NT_ConnectionListener AddConnectionListener(
NT_Inst inst,
std::function<void(const ConnectionNotification& event)> callback,
bool immediate_notify);
NT_Inst inst, bool immediate_notify,
std::function<void(const ConnectionNotification& event)> callback);
/**
* Wait for the connection listener queue to be empty. This is primarily useful
* for deterministic testing. This blocks until either the connection listener
* queue is empty (e.g. there are no more events that need to be passed along to
* callbacks or poll queues) or the timeout expires.
*
* @param handle handle
* @param timeout timeout, in seconds. Set to 0 for non-blocking behavior, or a
* negative value to block indefinitely
* @return False if timed out, otherwise true.
*/
bool WaitForConnectionListenerQueue(NT_Handle handle, double timeout);
/**
* Create a connection listener poller.