diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index c5f89df..eceb893 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,5 +1,6 @@ # Add the different subdirectories add_subdirectory(basic-system) add_subdirectory(type-facilities) add_subdirectory(messaging) +add_subdirectory(messaging-system) diff --git a/examples/messaging-system/CMakeLists.txt b/examples/messaging-system/CMakeLists.txt new file mode 100644 index 0000000..f598a42 --- /dev/null +++ b/examples/messaging-system/CMakeLists.txt @@ -0,0 +1,4 @@ +add_executable(messaging-system messaging-system.cpp) +ROSA_add_library_dependencies(messaging-system ROSAConfig) +ROSA_add_library_dependencies(messaging-system ROSACore) + diff --git a/examples/messaging-system/messaging-system.cpp b/examples/messaging-system/messaging-system.cpp new file mode 100644 index 0000000..34ccbdf --- /dev/null +++ b/examples/messaging-system/messaging-system.cpp @@ -0,0 +1,115 @@ +/******************************************************************************* + * + * File: messaging-system.cpp + * + * Contents: A simple example on the MessagingSystem and Agent classes of the + * RoSA Core library. + * + * Copyright 2017 + * + * Author: David Juhasz (david.juhasz@tuwien.ac.at) + * + ******************************************************************************/ + +#include "rosa/config/version.h" + +#include "rosa/core/Agent.hpp" +#include "rosa/core/MessagingSystem.hpp" + +#include "rosa/support/log.h" +#include "rosa/support/terminal_colors.h" + +#include + +using namespace rosa; +using namespace rosa::terminal; + +// A dummy wrapper for testing MessagingSystem. +// NOTE: Since we test MessagingSystem directly here, we need to get access to +// its protected members. That we do by imitating to be a decent subclass of +// MessagingSystem, while calling protected member functions on an object of a +// type from which we actually don't inherit. +struct SystemTester : protected MessagingSystem { + template + static AgentHandle createMyAgent(MessagingSystem *S, const std::string &Name, + Fun &&F, Funs &&... Fs) { + return ((SystemTester *)S) + ->createAgent(Name, std::move(F), std::move(Fs)...); + } + + static void destroyMyAgent(MessagingSystem *S, const AgentHandle &H) { + ((SystemTester *)S)->destroyUnit(H.agent()); + } + + template + static void sendToMyAgent(MessagingSystem *S, const AgentHandle &H, + const Type &T, const Types &... Ts) { + ((SystemTester *)S)->send(H, T, Ts...); + } + + template + static void sendToMyAgent(MessagingSystem *S, const AgentHandle &H, Type &&T, + Types &&... Ts) { + ((SystemTester *)S) + ->send(H, std::move(T), std::move(Ts)...); + } +}; + +int main(void) { + LOG_INFO_STREAM << library_string() << " -- " << Color::Red + << "messaging-system example" << Color::Default << std::endl; + + std::unique_ptr S = MessagingSystem::createSystem("Sys"); + MessagingSystem *SP = S.get(); + + AgentHandle Agent1 = SystemTester::createMyAgent( + SP, "Agent1", Invoker::F([](const std::string &M) noexcept { + LOG_INFO("Agent1: " + M); + })); + + using PrintAtom = AtomConstant; + using ForwardAtom = AtomConstant; + AgentHandle Agent2 = SystemTester::createMyAgent( + SP, "Agent2", + Invoker::F([](PrintAtom, uint8_t N) noexcept { + LOG_INFO("Agent2: " + std::to_string(N)); + }), + Invoker::F([&Agent1](ForwardAtom, + uint8_t N) noexcept { + if (Agent1) { + Agent1.send(std::to_string(N)); + } else { + LOG_INFO("Agent2 cannot forward: Agent1 is not valid"); + } + })); + + LOG_INFO_STREAM << std::endl + << "Agent1 is valid: " << bool(Agent1) << std::endl + << "Agent2 is valid: " << bool(Agent2) << std::endl; + + LOG_INFO("Sending a print-message to Agent2..."); + SystemTester::sendToMyAgent(SP, Agent2, PrintAtom::Value, + 42); + + LOG_INFO("Sending a forward-message to Agent2..."); + SystemTester::sendToMyAgent(SP, Agent2, + ForwardAtom::Value, 42); + + LOG_INFO("Sending an unexpected message to Agent2..."); + SystemTester::sendToMyAgent(SP, Agent2, unit); + + SystemTester::destroyMyAgent(SP, Agent1); + + LOG_INFO_STREAM << std::endl + << "Agent1 is valid: " << bool(Agent1) << std::endl + << "Agent2 is valid: " << bool(Agent2) << std::endl; + + LOG_INFO("Sending a forward-message to Agent2..."); + SystemTester::sendToMyAgent(SP, Agent2, + ForwardAtom::Value, 42); + + SystemTester::destroyMyAgent(SP, Agent2); + + return 0; +} + diff --git a/include/rosa/core/AbstractAgent.hpp b/include/rosa/core/AbstractAgent.hpp new file mode 100644 index 0000000..06e9f70 --- /dev/null +++ b/include/rosa/core/AbstractAgent.hpp @@ -0,0 +1,90 @@ +/******************************************************************************* + * + * File: AbstractAgent.hpp + * + * Contents: Declaration of an abstract interface for Agents. + * + * Copyright 2017 + * + * Author: David Juhasz (david.juhasz@tuwien.ac.at) + * + ******************************************************************************/ + +#ifndef ROSA_CORE_ABSTRACTAGENT_HPP +#define ROSA_CORE_ABSTRACTAGENT_HPP + +#include "rosa/core/Message.hpp" +#include "rosa/core/forward_declarations.h" + +#include "rosa/support/debug.hpp" + +#include + +namespace rosa { + +// Abstract class declaring an interface for Agents. +// NOTE: Ref is reference for AbstractAgent, whose actual value must be a class +// derived from AbstractAgent. +template class AbstractAgent { + // NOTE: It can be statically checked if Ref is derived from + // AbstractAgent, but the static assertion cannot be defined directly in + // the class body. That is because a class C derived from AbstractAgent is + // not complete when the static assertion in the definition of + // AbstractAgent would be evaluated. Thus, the static assertion is placed + // in the constructor. +protected: + // Ctor. + // STATIC PRE: std::is_base_of, Ref>::value + AbstractAgent(void) noexcept; + +public: + // Dtor. + virtual ~AbstractAgent(void) = default; + + // Tells if the Agent is in valid state. + virtual operator bool(void) const noexcept = 0; + + // Tells if the given reference refers to this Agent. + virtual bool operator==(const Ref &) const noexcept = 0; + + // Returns a reference to this Agent. + virtual Ref self(void) noexcept = 0; + + // Sends the given Message to this Agent. + // PRE: bool(*this) + virtual void sendMessage(message_t &&) noexcept = 0; + + // Convenience template, which creates the Message from the given constant + // lvalue references and sends to this Agent. + // PRE: bool(*this) + template + void send(const Type &T, const Types &... Ts) noexcept; + + // Convenience template, which creates the Message from the given rvalue + // references and sends to this Agent. + // PRE: bool(*this) + template + void send(Type &&T, Types &&... Ts) noexcept; +}; + +template AbstractAgent::AbstractAgent(void) noexcept { + STATIC_ASSERT((std::is_base_of, Ref>::value), + "not derived Agent"); // Sanity check. +} + +template +template +void AbstractAgent::send(const Type &T, const Types &... Ts) noexcept { + sendMessage(Message::create(T, Ts...)); +} + +template +template +void AbstractAgent::send(Type &&T, Types &&... Ts) noexcept { + sendMessage(Message::create(std::move(T), std::move(Ts)...)); +} + +} // End namespace rosa + +#endif // ROSA_CORE_ABSTRACTAGENT_HPP + diff --git a/include/rosa/core/Agent.hpp b/include/rosa/core/Agent.hpp new file mode 100644 index 0000000..2ca9088 --- /dev/null +++ b/include/rosa/core/Agent.hpp @@ -0,0 +1,77 @@ +/******************************************************************************* + * + * File: Agent.hpp + * + * Contents: Declaration of the Agent class. + * + * Copyright 2017 + * + * Author: David Juhasz (david.juhasz@tuwien.ac.at) + * + ******************************************************************************/ + +#ifndef ROSA_CORE_AGENT_HPP +#define ROSA_CORE_AGENT_HPP + +#include "rosa/core/AgentHandle.hpp" +#include "rosa/core/MessageHandler.hpp" +#include "rosa/core/MessagingSystem.hpp" +#include "rosa/core/Unit.h" + +#include "rosa/support/log.h" + +namespace rosa { + +// Implements an Agent that is a special Unit owned by a MessagingSystem, +// capable of handling Messages as MessageHandler, and provides the +// AbstractAgent interface with AgentHandle as reference type. +class Agent : public Unit, + public MessageHandler, + public AbstractAgent { + friend class AgentHandle; // AgentHandle is our friend. + +protected: + // A handle for this Agent. + const AgentHandle Self; + +public: + // Ctor for instantiating all the base-classes. + template + Agent(const AtomValue Kind, const id_t Id, const std::string &Name, + MessagingSystem &S, Fun &&F, Funs &&... Fs); + + // Dtor. + ~Agent(void); + + // Tells if the Agent is in valid state. + operator bool(void) const noexcept override; + + // Tells if the given reference refers to this Agent. + bool operator==(const AgentHandle &H) const noexcept override; + + // Returns a reference to this Agent. + AgentHandle self(void) noexcept override; + + // Sends the given Message to this Agent. + void sendMessage(message_t &&M) noexcept override; + + // Dumping the Agent into a string for tracing purposes. + std::string dump(void) const noexcept override; + +protected: + // Returns the owning MessagingSystem. + MessagingSystem &system(void) const noexcept override; +}; + +template +Agent::Agent(const AtomValue Kind, const id_t Id, const std::string &Name, + MessagingSystem &S, Fun &&F, Funs &&... Fs) + : Unit(Kind, Id, Name, S), MessageHandler(std::move(F), std::move(Fs)...), + Self(*this) { + LOG_TRACE("Agent is created."); +} + +} // End namespace rosa + +#endif // ROSA_CORE_AGENT_HPP + diff --git a/include/rosa/core/AgentHandle.hpp b/include/rosa/core/AgentHandle.hpp new file mode 100644 index 0000000..a5b5313 --- /dev/null +++ b/include/rosa/core/AgentHandle.hpp @@ -0,0 +1,61 @@ +/******************************************************************************* + * + * File: AgentHandle.hpp + * + * Contents: Declaration of AgentHandle. + * + * Copyright 2017 + * + * Author: David Juhasz (david.juhasz@tuwien.ac.at) + * + ******************************************************************************/ + +#ifndef ROSA_CORE_AGENTHANDLE_HPP +#define ROSA_CORE_AGENTHANDLE_HPP + +#include "rosa/core/AbstractAgent.hpp" + +namespace rosa { + +// Wraps an actual Agent to decouple its public interface. +// NOTE: Such decoupling might be necessary when operating with remote +// Systems, sometime in the future. +class AgentHandle : public AbstractAgent { + // The wrapped Agent instance. + Agent &A; + + // The owning MessagingSystem of A. + MessagingSystem &S; + +public: + // Ctor. + AgentHandle(Agent &A); + + // Dtor, nothing to take care of. + ~AgentHandle(void) = default; + + // Gives a reference to the wrapped Agent. + Agent &agent(void) const noexcept; + + // Tells if the AgentHandle is valid, referring to an existing Agent. + // NOTE: AgentHandlers belong to MessagingSystems. Working with an + // AgentHandler whose originating MessagingSystem has already been destroyed + // results in undefined behavior. + operator bool(void) const noexcept override; + + // Tells if the given reference refers to this Agent. + bool operator==(const AgentHandle &H) const noexcept override; + + // Returns a reference to this Agent. + AgentHandle self(void) noexcept override; + + // Sends the given Message to this Agent. + // PRE: bool(*this) + void sendMessage(message_t &&M) noexcept override; + +}; + +} // End namespace rosa + +#endif // ROSA_CORE_AGENTHANDLE_HPP + diff --git a/include/rosa/core/MessagingSystem.hpp b/include/rosa/core/MessagingSystem.hpp new file mode 100644 index 0000000..f040563 --- /dev/null +++ b/include/rosa/core/MessagingSystem.hpp @@ -0,0 +1,93 @@ +/******************************************************************************* + * + * File: MessagingSystem.hpp + * + * Contents: Declaration of the class MessagingSystem. + * + * Copyright 2017 + * + * Author: David Juhasz (david.juhasz@tuwien.ac.at) + * + ******************************************************************************/ + +#ifndef ROSA_CORE_MESSAGINGSYSTEM_HPP +#define ROSA_CORE_MESSAGINGSYSTEM_HPP + +#include "rosa/core/AgentHandle.hpp" +#include "rosa/core/System.hpp" + +#include "rosa/support/atom.hpp" + +namespace rosa { + +// Extends the System interface with features to create Agents and register +// Messages for them. +class MessagingSystem : public System { + friend class AgentHandle; // AgentHandle is our friend. + +public: + // Returns an object implementing the interface defined by the class. + static std::unique_ptr + createSystem(const std::string &Name) noexcept; + +private: + // Kind used for Unit categorization of Agents. + static constexpr AtomValue AgentKind = atom("agent"); + +protected: + // Ctor. + MessagingSystem(void) noexcept = default; + +protected: + // Creates an Agent owned by the MessagingSystem and returns a handle for it. + // STATIC PRE: std::is_base_of::value + template + AgentHandle createAgent(const std::string &Name, Fun &&F, Funs &&... Fs); + +public: + // Sends the given Message to the Agent referred by the given AgentHandle. + // NOTE: If the given Message cannot be handled by the referred Agent, the + // Message is simply ignored. + // PRE: isUnitRegistered(H.agent()) + virtual void send(const AgentHandle &H, message_t &&M) noexcept = 0; + + // Convenience template, which creates the Message from the given constant + // lvalue references and sends to the Agent. + // PRE: isUnitRegistered(H.agent()) + template + void send(const AgentHandle &H, const Type &T, const Types &... Ts) noexcept; + + // Convenience template, which creates the Message from the given rvalue + // references and sends to the Agent. + // PRE: isUnitRegistered(H.agent()) + template + void send(const AgentHandle &H, Type &&T, Types &&... Ts) noexcept; +}; + +template +AgentHandle MessagingSystem::createAgent(const std::string &Name, Fun &&F, + Funs &&... Fs) { + STATIC_ASSERT((std::is_base_of::value), "not an Agent"); + Agent &A = createUnit([&](const id_t Id, + MessagingSystem &S) noexcept { + return new T(AgentKind, Id, Name, S, std::move(F), std::move(Fs)...); + }); + return {A}; +} + +template +void MessagingSystem::send(const AgentHandle &H, const Type &T, + const Types &... Ts) noexcept { + send(H, Message::create(T, Ts...)); +} + +template +void MessagingSystem::send(const AgentHandle &H, Type &&T, + Types &&... Ts) noexcept { + send(H, Message::create(std::move(T), std::move(Ts)...)); +} + +} // End namespace rosa + +#endif // ROSA_CORE_MESSAGINGSYSTEM_HPP + diff --git a/lib/core/Agent.cpp b/lib/core/Agent.cpp new file mode 100644 index 0000000..b3a673c --- /dev/null +++ b/lib/core/Agent.cpp @@ -0,0 +1,48 @@ +/******************************************************************************* + * + * File: Agent.cpp + * + * Contents: Implementation of non-template part of the Agent class. + * + * Copyright 2017 + * + * Author: David Juhasz (david.juhasz@tuwien.ac.at) + * + ******************************************************************************/ + +#include "rosa/core/Agent.hpp" + +namespace rosa { + +Agent::~Agent(void) { + LOG_TRACE("Destroying Agent..."); +} + +Agent::operator bool(void) const noexcept { + // An Agent itself is always valid. + return true; +} + +bool Agent::operator==(const AgentHandle &H) const noexcept { + // Return if the Agent wrapped by H is this very object. + return this == &H.agent(); +} + +AgentHandle Agent::self(void) noexcept { return Self; } + +void Agent::sendMessage(message_t &&M) noexcept { + system().send(Self, std::move(M)); +} + +std::string Agent::dump(void) const noexcept { + LOG_TRACE("Dumping Agent (" + FullName + ")"); + return "[Agent] " + FullName; +} + +MessagingSystem &Agent::system(void) const noexcept { + // NOTE: The System the Unit is created with is a MessagingSystem. + return static_cast(Unit::system()); +} + +} // End namespace rosa + diff --git a/lib/core/AgentHandle.cpp b/lib/core/AgentHandle.cpp new file mode 100644 index 0000000..b0bf4bc --- /dev/null +++ b/lib/core/AgentHandle.cpp @@ -0,0 +1,46 @@ +/******************************************************************************* + * + * File: AgentHandle.cpp + * + * Contents: Implementation of AgentHandle. + * + * Copyright 2017 + * + * Author: David Juhasz (david.juhasz@tuwien.ac.at) + * + ******************************************************************************/ + +#include "rosa/core/AgentHandle.hpp" + +#include "rosa/core/Agent.hpp" + +namespace rosa { + +AgentHandle::AgentHandle(Agent &A) : A(A), S(A.system()) {} + +Agent &AgentHandle::agent(void) const noexcept { + return A; +} + +AgentHandle::operator bool(void) const noexcept { + // NOTE: The referred MessageSystem is supposed to be still alive. + return S.isUnitRegistered(A); +} + +bool AgentHandle::operator==(const AgentHandle &H) const noexcept { + // Return if the referred Agent is the same object in both AgentHandlers. + return &A == &H.A; +} + +AgentHandle AgentHandle::self(void) noexcept { + // Return a copy of this AgentHandle. + return *this; +} + +void AgentHandle::sendMessage(message_t &&M) noexcept { + ASSERT(bool(*this)); + S.send(*this, std::move(M)); +} + +} // End namespace rosa + diff --git a/lib/core/CMakeLists.txt b/lib/core/CMakeLists.txt index ca1162d..d9e04ba 100644 --- a/lib/core/CMakeLists.txt +++ b/lib/core/CMakeLists.txt @@ -1,12 +1,16 @@ add_library(ROSACore Unit.cpp System.cpp SystemBase.cpp SystemImpl.cpp Message.cpp Invoker.cpp MessageHandler.cpp + Agent.cpp + AgentHandle.cpp + MessagingSystem.cpp + MessagingSystemImpl.cpp ) ROSA_add_library_dependencies(ROSACore ROSASupport) diff --git a/lib/core/MessagingSystem.cpp b/lib/core/MessagingSystem.cpp new file mode 100644 index 0000000..1e8d669 --- /dev/null +++ b/lib/core/MessagingSystem.cpp @@ -0,0 +1,25 @@ +/******************************************************************************* + * + * File: MessagingSystem.cp + * + * Contents: Implementation of the class MessagingSystem. + * + * Copyright 2017 + * + * Author: David Juhasz (david.juhasz@tuwien.ac.at) + * + ******************************************************************************/ + +#include "rosa/core/MessagingSystem.hpp" + +#include "MessagingSystemImpl.hpp" + +namespace rosa { + +std::unique_ptr +MessagingSystem::createSystem(const std::string &Name) noexcept { + return std::unique_ptr(new MessagingSystemImpl(Name)); +} + +} // End namespace rosa + diff --git a/lib/core/MessagingSystemImpl.cpp b/lib/core/MessagingSystemImpl.cpp new file mode 100644 index 0000000..475889a --- /dev/null +++ b/lib/core/MessagingSystemImpl.cpp @@ -0,0 +1,31 @@ +/******************************************************************************* + * + * File: MessagingSystemImpl.cpp + * + * Contents: Implementation of the MessagingSystemImpl class. + * + * Copyright 2017 + * + * Author: David Juhasz (david.juhasz@tuwien.ac.at) + * + ******************************************************************************/ + +#include "MessagingSystemImpl.hpp" + +#include "rosa/core/Agent.hpp" + +namespace rosa { + +MessagingSystemImpl::MessagingSystemImpl(const std::string &Name) noexcept + : MessagingSystem(), + SystemImpl(Name) { + LOG_TRACE("System '" + Name + "' is a MessagingSystem"); +} + +void MessagingSystemImpl::send(const AgentHandle &H, message_t &&M) noexcept { + ASSERT(isUnitRegistered(H.agent())); + H.agent()(*M); +} + +} // End namespace rosa + diff --git a/lib/core/MessagingSystemImpl.hpp b/lib/core/MessagingSystemImpl.hpp new file mode 100644 index 0000000..526d21a --- /dev/null +++ b/lib/core/MessagingSystemImpl.hpp @@ -0,0 +1,80 @@ +/******************************************************************************* + * + * File: MessagingSystemImpl.hpp + * + * Contents: Declaration of a basic implementation of the MessagingSystem + * interface. + * + * Copyright 2017 + * + * Author: David Juhasz (david.juhasz@tuwien.ac.at) + * + ******************************************************************************/ + +#ifndef ROSA_LIB_CORE_MESSAGINGSYSTEMIMPL_HPP // NOLINT +#define ROSA_LIB_CORE_MESSAGINGSYSTEMIMPL_HPP + +#include "SystemImpl.hpp" + +#include "rosa/core/MessagingSystem.hpp" + +namespace rosa { + +// Implements MessagingSystem by extending SystemImpl by adding a +// simple implementation of Message sending: directly invoking the Agent with +// the given Message. +// NOTE: Keep in mind that sending a Message with this implementation +// translates into a direct function call. +class MessagingSystemImpl : public MessagingSystem, public SystemImpl { + // Referring to SystemImpl as Base inside the class. + using Base = SystemImpl; + +public: + // Ctor. + MessagingSystemImpl(const std::string &Name) noexcept; + +protected: + // NOTE: Simply forwarding calls to implementations from Base for the System + // interface. + // FIXME: How could we use the inherited implementations in a simpler way? + + inline id_t nextId(void) noexcept override { return Base::nextId(); } + + inline bool isSystemCleaned(void) const noexcept override { + return Base::isSystemCleaned(); + } + + inline void markCleaned(void) noexcept override { Base::markCleaned(); } + + inline void registerUnit(Unit &U) noexcept override { Base::registerUnit(U); } + + inline void destroyUnit(Unit &U) noexcept override { Base::destroyUnit(U); } + + inline bool isUnitRegistered(const Unit &U) const noexcept override { + return Base::isUnitRegistered(U); + } + +public: + inline const std::string &name(void) const noexcept override { + return Base::name(); + } + + inline size_t numberOfConstructedUnits(void) const noexcept override { + return Base::numberOfConstructedUnits(); + } + + inline size_t numberOfLiveUnits(void) const noexcept override { + return Base::numberOfLiveUnits(); + } + + inline bool empty(void) const noexcept override { return Base::empty(); } + + // Directly invokes the Agent with the Message. + // PRE: isUnitRegistered(H.agent()) + void send(const AgentHandle &H, message_t &&M) noexcept override; +}; + +} // End namespace rosa + +#endif // ROSA_LIB_CORE_MESSAGINGSYSTEMIMPL_HPP +