code integration
This commit is contained in:
390
sdi_toolBox_2.x.x/toolBox/sdi_toolBox/desktop/eventBus/node.h
Normal file
390
sdi_toolBox_2.x.x/toolBox/sdi_toolBox/desktop/eventBus/node.h
Normal file
@@ -0,0 +1,390 @@
|
||||
/*
|
||||
Copyright (c) 2026 - SD-Innovation S.A.S. - FRANCE
|
||||
*/
|
||||
|
||||
/*
|
||||
ver: 2.x.x - build: 2026-04-28
|
||||
*/
|
||||
|
||||
/*
|
||||
The zlib License
|
||||
|
||||
Copyright (c) 2026 SD-Innovation S.A.S.
|
||||
|
||||
This software is provided ‘as-is’, without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source
|
||||
distribution.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "bus.h"
|
||||
#include "inode.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
|
||||
namespace sdi_toolBox::desktop::eventBus
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
/**
|
||||
* @class Node
|
||||
* @brief Concrete subscriber node in the event bus system.
|
||||
*
|
||||
* Node is the concrete implementation of @ref INode. It represents a participant
|
||||
* in the event bus that can subscribe to specific message types or to broadcast
|
||||
* mode, emit and post messages through the bus, and consume received messages
|
||||
* from its internal FIFO queue.
|
||||
*
|
||||
* Each Node holds a reference to the @ref Bus it belongs to. Subscriptions and
|
||||
* message transmissions are delegated to the bus. Incoming messages are stored
|
||||
* in an internal thread-safe queue and can be retrieved via @ref popMessage().
|
||||
*
|
||||
* The Node also supports synchronous waiting: a thread can block on
|
||||
* @ref syncWaitForMessage() until at least one message is available in the queue.
|
||||
*
|
||||
* On destruction, the Node automatically unsubscribes from all event types and
|
||||
* broadcast mode, preventing dangling pointers in the bus routing table.
|
||||
*
|
||||
* @note Node is non-copyable and non-movable.
|
||||
* @note A @ref Bus reference must be provided at construction time.
|
||||
* @note The Node does not take ownership of the @ref Bus.
|
||||
*
|
||||
* @par Example usage:
|
||||
* @code
|
||||
* sdi_toolBox::desktop::eventBus::Bus bus;
|
||||
* sdi_toolBox::desktop::eventBus::Node node(bus);
|
||||
*
|
||||
* node.subscribe(MY_EVENT_TYPE);
|
||||
* node.emit<MyMessage>(42);
|
||||
*
|
||||
* node.syncWaitForMessage();
|
||||
* auto msg = std::dynamic_pointer_cast<MyMessage>(node.popMessage());
|
||||
* @endcode
|
||||
*
|
||||
* @see Bus
|
||||
* @see INode
|
||||
* @see Message
|
||||
*/
|
||||
class Node : public INode
|
||||
{
|
||||
public:
|
||||
///@name Construction & Destruction
|
||||
///@{
|
||||
|
||||
Node() = delete; ///< Default constructor - deleted. A @ref Bus reference must be provided.
|
||||
|
||||
/**
|
||||
* @brief Destructor.
|
||||
*
|
||||
* Automatically unsubscribes the node from all specific event types and
|
||||
* broadcast mode via @ref unsubscribeFromAll(), preventing dangling pointers
|
||||
* in the bus routing table. Also notifies any thread blocked in
|
||||
* @ref syncWaitForMessage() to unblock it gracefully.
|
||||
*/
|
||||
virtual ~Node();
|
||||
|
||||
/**
|
||||
* @brief Copy constructor - deleted.
|
||||
*
|
||||
* Node is non-copyable.
|
||||
*/
|
||||
Node(const Node &obj) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move constructor - deleted.
|
||||
*
|
||||
* Node is non-movable.
|
||||
*/
|
||||
Node(Node &&obj) noexcept = delete;
|
||||
|
||||
/**
|
||||
* @brief Copy assignment operator - deleted.
|
||||
*
|
||||
* Node is non-copyable.
|
||||
*/
|
||||
Node &operator=(const Node &obj) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move assignment operator - deleted.
|
||||
*
|
||||
* Node is non-movable.
|
||||
*/
|
||||
Node &operator=(Node &&obj) noexcept = delete;
|
||||
|
||||
/**
|
||||
* @brief Construct a Node attached to the given @ref Bus.
|
||||
*
|
||||
* @param bus Reference to the @ref Bus this node belongs to.
|
||||
* The bus must outlive the node.
|
||||
*/
|
||||
explicit Node(Bus &bus);
|
||||
|
||||
///@}
|
||||
///@name Synchronization
|
||||
///@{
|
||||
|
||||
/**
|
||||
* @brief Block the calling thread until a message is received.
|
||||
*
|
||||
* Suspends the calling thread using an atomic wait until at least one message
|
||||
* has been appended to the node's internal queue by the @ref Bus. This method
|
||||
* is intended for synchronous event-driven patterns where a thread should idle
|
||||
* until work is available.
|
||||
*
|
||||
* @note Returns immediately if a message is already pending in the queue
|
||||
* at the time of the call.
|
||||
* @note If the Node is destroyed while a thread is blocked here, the destructor
|
||||
* triggers a notification to unblock the waiting thread gracefully.
|
||||
* @warning The caller is responsible for checking the queue after this call
|
||||
* returns, as the notification may also be triggered by the destructor
|
||||
* with an empty queue.
|
||||
*/
|
||||
void syncWaitForMessage();
|
||||
|
||||
///@}
|
||||
///@name Subscription Management
|
||||
///@{
|
||||
|
||||
/**
|
||||
* @brief Subscribe this node to a specific message type.
|
||||
*
|
||||
* Delegates to @ref Bus::subscribe(). The node will receive all messages
|
||||
* of the given type posted to the bus. Duplicate subscriptions are ignored.
|
||||
*
|
||||
* @param eventType The message type identifier to subscribe to.
|
||||
* @see Bus::subscribe()
|
||||
*/
|
||||
void subscribe(MessageTypeID eventType);
|
||||
|
||||
/**
|
||||
* @brief Unsubscribe this node from a specific message type.
|
||||
*
|
||||
* Delegates to @ref Bus::unsubscribe(). If the node was not subscribed
|
||||
* to the given type, this call has no effect.
|
||||
*
|
||||
* @param eventType The message type identifier to unsubscribe from.
|
||||
* @see Bus::unsubscribe()
|
||||
*/
|
||||
void unsubscribe(MessageTypeID eventType);
|
||||
|
||||
/**
|
||||
* @brief Unsubscribe this node from all message types and broadcast mode.
|
||||
*
|
||||
* Delegates to @ref Bus::unsubscribeFromAll(). After this call, the node
|
||||
* will no longer receive any messages until it re-subscribes.
|
||||
*
|
||||
* @see Bus::unsubscribeFromAll()
|
||||
*/
|
||||
void unsubscribeFromAll();
|
||||
|
||||
///@}
|
||||
///@name Message Transmission
|
||||
///@{
|
||||
|
||||
/**
|
||||
* @brief Construct and emit a message of type @p T through the bus.
|
||||
*
|
||||
* Forwards the call to @ref Bus::emit(). Creates a new message of type @p T
|
||||
* using the provided arguments and posts it to the bus.
|
||||
*
|
||||
* @tparam T The message type to emit. Must be derived from @ref Message.
|
||||
* @tparam Args Constructor argument types for @p T.
|
||||
* @param args Arguments forwarded to the constructor of @p T.
|
||||
* @return @c true if at least one subscriber received the message,
|
||||
* @c false otherwise.
|
||||
*
|
||||
* @see Bus::emit()
|
||||
*/
|
||||
template<class T, class... Args>
|
||||
bool emit(Args &&...args);
|
||||
|
||||
/**
|
||||
* @brief Post an already constructed message through the bus.
|
||||
*
|
||||
* Forwards the call to @ref Bus::post().
|
||||
*
|
||||
* @param message Shared pointer to the message to post. Must not be @c nullptr.
|
||||
* @return @c true if at least one subscriber received the message,
|
||||
* @c false otherwise.
|
||||
*
|
||||
* @see Bus::post()
|
||||
*/
|
||||
bool post(const std::shared_ptr<Message> &message) const;
|
||||
|
||||
/**
|
||||
* @brief Notify the node that a message has been received.
|
||||
*
|
||||
* Sets the atomic waiting flag to @c true and triggers a wake-up for any
|
||||
* thread blocked in @ref syncWaitForMessage(). Has no effect if the flag
|
||||
* is already set.
|
||||
*/
|
||||
void messageNotify();
|
||||
|
||||
///@}
|
||||
///@name Message queue management
|
||||
///@{
|
||||
|
||||
/**
|
||||
* @brief Get the number of messages currently in the node's queue.
|
||||
*
|
||||
* @return The number of pending messages waiting to be consumed.
|
||||
* @note This operation is thread-safe.
|
||||
*/
|
||||
size_t getMessageCount() const;
|
||||
|
||||
/**
|
||||
* @brief Remove and return the front message from the node's queue (FIFO).
|
||||
*
|
||||
* Retrieves the oldest message in the queue and removes it. If the queue
|
||||
* is empty, returns @c nullptr.
|
||||
*
|
||||
* @return A shared pointer to the front @ref Message, or @c nullptr if the
|
||||
* queue is empty.
|
||||
* @note This operation is thread-safe.
|
||||
*/
|
||||
std::shared_ptr<Message> popMessage(); // Pop a message from the node's message queue (remove and return the front message)
|
||||
|
||||
///@}
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Append a message to the node's internal queue.
|
||||
*
|
||||
* Called exclusively by @ref Bus when dispatching a message to this node.
|
||||
* Pushes the message onto the queue and notifies any thread waiting in
|
||||
* @ref syncWaitForMessage().
|
||||
*
|
||||
* @param message Shared pointer to the message being delivered.
|
||||
*/
|
||||
void append(const std::shared_ptr<Message> &message) override;
|
||||
|
||||
Bus &m_bus; ///< Reference to the event bus
|
||||
std::atomic_bool m_nodeWaitingFlag{ false }; ///< Flag to indicate if the node is waiting for a message (used for synchronous waiting)
|
||||
|
||||
/// @brief Internal message queue, protected by a mutex for thread-safe access.
|
||||
struct
|
||||
{
|
||||
mutable std::mutex mtx; ///< Mutex for thread-safe access to the message queue
|
||||
std::queue<std::shared_ptr<Message>> messageQueue; ///< Queue of messages received by the node
|
||||
} m_busMessages;
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------
|
||||
/* Constructor */
|
||||
inline Node::Node(Bus &bus)
|
||||
: m_bus(bus)
|
||||
{
|
||||
// Nothing to do here
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Default destructor */
|
||||
inline Node::~Node()
|
||||
{
|
||||
// Unsubscribe from all event types when the node is destroyed
|
||||
unsubscribeFromAll();
|
||||
|
||||
// Notify the bus that the node is being destroyed (in case it is waiting for a message)
|
||||
messageNotify();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Synchronously wait for a message to be posted to the bus and received by the node (block the calling thread until a message is received) */
|
||||
inline void Node::syncWaitForMessage()
|
||||
{
|
||||
// Wait until at least one message is received in the node's
|
||||
// message queue
|
||||
m_nodeWaitingFlag = false;
|
||||
m_nodeWaitingFlag.wait(false);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Subscribe to receive messages of a specific event type */
|
||||
inline void Node::subscribe(const MessageTypeID eventType)
|
||||
{
|
||||
m_bus.subscribe(this, eventType);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Unsubscribe from receiving messages of a specific event type */
|
||||
inline void Node::unsubscribe(const MessageTypeID eventType)
|
||||
{
|
||||
m_bus.unsubscribe(this, eventType);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Unsubscribe from receiving messages of all event types */
|
||||
inline void Node::unsubscribeFromAll()
|
||||
{
|
||||
m_bus.unsubscribeFromAll(this);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Emit a message of type T with the given arguments (create a message and post it to the bus) */
|
||||
template<class T, class... Args>
|
||||
bool Node::emit(Args &&...args)
|
||||
{
|
||||
return m_bus.emit<T>(std::forward<Args>(args)...);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Post a message to the bus */
|
||||
inline bool Node::post(const std::shared_ptr<Message> &message) const
|
||||
{
|
||||
return m_bus.post(message);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Get the number of messages in the node's message queue */
|
||||
inline size_t Node::getMessageCount() const
|
||||
{
|
||||
std::scoped_lock lock(m_busMessages.mtx);
|
||||
|
||||
return m_busMessages.messageQueue.size();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Pop a message from the node's message queue (remove and return the front message) */
|
||||
inline std::shared_ptr<Message> Node::popMessage()
|
||||
{
|
||||
std::scoped_lock lock(m_busMessages.mtx);
|
||||
|
||||
if (m_busMessages.messageQueue.empty())
|
||||
return nullptr; // No messages in the queue
|
||||
|
||||
auto message = m_busMessages.messageQueue.front(); // Get the front message
|
||||
m_busMessages.messageQueue.pop(); // Remove the front message from the queue
|
||||
|
||||
return message; // Return the popped message
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Insert a message into the node's message queue (called by the bus when a message is posted to the bus) */
|
||||
inline void Node::append(const std::shared_ptr<Message> &message)
|
||||
{
|
||||
std::scoped_lock lock(m_busMessages.mtx);
|
||||
|
||||
m_busMessages.messageQueue.push(message);
|
||||
|
||||
// If the node is waiting for a message, notify it that a
|
||||
// message has been received
|
||||
messageNotify();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Notify the node that a message has been received (used for synchronous waiting) */
|
||||
inline void Node::messageNotify()
|
||||
{
|
||||
if (!m_nodeWaitingFlag.load())
|
||||
{
|
||||
m_nodeWaitingFlag = true;
|
||||
m_nodeWaitingFlag.notify_one();
|
||||
}
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
} // namespace sdi_toolBox::desktop::eventBus
|
||||
Reference in New Issue
Block a user