diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index e933150..8f99ea8 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,3 +1,3 @@ # Add the different subdirectories -add_subdirectory(simple) +add_subdirectory(basic-system) diff --git a/examples/basic-system/CMakeLists.txt b/examples/basic-system/CMakeLists.txt new file mode 100644 index 0000000..1bc0fa9 --- /dev/null +++ b/examples/basic-system/CMakeLists.txt @@ -0,0 +1,4 @@ +add_executable(basic-system basic-system.cpp) +ROSA_add_library_dependencies(basic-system ROSAConfig) +ROSA_add_library_dependencies(basic-system ROSACore) + diff --git a/examples/basic-system/basic-system.cpp b/examples/basic-system/basic-system.cpp new file mode 100644 index 0000000..4821c52 --- /dev/null +++ b/examples/basic-system/basic-system.cpp @@ -0,0 +1,58 @@ +/******************************************************************************* + * + * File: basic-system.cpp + * + * Contents: A simple example on the basic System and Unit classes of the RoSA + * Core library. + * + * Copyright 2017 + * + * Author: David Juhasz (david.juhasz@tuwien.ac.at) + * + ******************************************************************************/ + +#include "rosa/config/version.h" +#include "rosa/core/System.h" +#include "rosa/core/Unit.h" +#include "rosa/support/log.h" +#include "rosa/support/terminal_colors.h" +#include + +using namespace rosa; +using namespace rosa::terminal; + +// A dummy wrapper for testing System. +// NOTE: Since we test System directly here, we need to get access to its +// protected members. That we do by imitating to be a decent subclass of +// System, while calling protected member functions on an object of a type from +// which we actually don't inherit. +struct SystemTester : protected System { + static Unit &createMyUnit(System *S, + const std::string &Name = std::string()) { + return ((SystemTester *)S) + ->createUnit([](const size_t Id, const std::string &N, + System &S) { return new Unit(Id, N, S); }, + Name); + } + + static void destroyMyUnit(System *S, Unit &U) { + ((SystemTester *)S)->destroyUnit(U); + } +}; + +int main(void) { + LOG_INFO_STREAM << library_string() << " -- " << Color::Red + << "simple example" << Color::Default << std::endl; + + std::unique_ptr S = System::createSystem("Sys"); + System *SP = S.get(); + Unit &Unit1 = SystemTester::createMyUnit(SP), + &Unit2 = SystemTester::createMyUnit(SP, "Second"), + &Unit3 = SystemTester::createMyUnit(SP); + SystemTester::destroyMyUnit(SP, Unit1); + SystemTester::destroyMyUnit(SP, Unit3); + LOG_INFO_STREAM << "Dumping Unit2" << std::endl << Unit2 << std::endl; + SystemTester::destroyMyUnit(SP, Unit2); + return 0; +} + diff --git a/examples/simple/CMakeLists.txt b/examples/simple/CMakeLists.txt deleted file mode 100644 index a94625c..0000000 --- a/examples/simple/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -add_executable(simple simple.cpp) -ROSA_add_library_dependencies(simple ROSAConfig) -ROSA_add_library_dependencies(simple ROSACore) - diff --git a/examples/simple/simple.cpp b/examples/simple/simple.cpp deleted file mode 100644 index 0a718cc..0000000 --- a/examples/simple/simple.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/******************************************************************************* - * - * File: simple.cpp - * - * Contents: A very simple example to use RoSA Core library. - * - * Copyright 2017 - * - * Author: David Juhasz (david.juhasz@tuwien.ac.at) - * - ******************************************************************************/ - -#include "rosa/config/version.h" -#include "rosa/core/System.h" -#include "rosa/core/Unit.h" -#include "rosa/support/log.h" -#include "rosa/support/terminal_colors.h" -#include - -using namespace rosa; -using namespace rosa::terminal; - -// A dummy implementation of System. -class MySystem : public System { -public: - MySystem(const std::string &Name) : System(Name) {} - - // Has the base-class to create a Unit instance. - Unit &createMyUnit(const std::string &Name = std::string()) { - return createUnit([](const size_t Id, const std::string &N, - System &S) { return new Unit(Id, N, S); }, - Name); - } - - // Has the base-class to destroy the Unit instance. - void destroyMyUnit(Unit &U) { - destroyUnit(U); - } -}; - -int main(void) { - LOG_INFO_STREAM << library_string() << " -- " << Color::Red - << "simple example" << Color::Default << std::endl; - - MySystem S("Sys"); - Unit &Unit1 = S.createMyUnit(), &Unit2 = S.createMyUnit("Second"), - &Unit3 = S.createMyUnit(); - S.destroyMyUnit(Unit1); - S.destroyMyUnit(Unit3); - LOG_INFO_STREAM << "Dumping Unit2" << std::endl << Unit2 << std::endl; - S.destroyMyUnit(Unit2); - return 0; -} - diff --git a/include/rosa/core/System.h b/include/rosa/core/System.h index 71e1b64..ffed832 100644 --- a/include/rosa/core/System.h +++ b/include/rosa/core/System.h @@ -1,82 +1,115 @@ /******************************************************************************* * * File: System.h * * Contents: Declaration of System base-class. * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ #ifndef ROSA_CORE_SYSTEM_H #define ROSA_CORE_SYSTEM_H #include +#include #include -#include -#include namespace rosa { // Forward declarations. class Unit; // Base-class for actual agent-systems, the class provides facitlities to keep // track of Units of the system. +// NOTE: This class and any subclasses are supposed to provide thread-safe +// interfaces. class System { public: // Signature of creator functions for Units. using UnitCreator = Unit *(*)(const size_t, const std::string&, System&); + // Returns an object implementing the interface defined by the class. + static std::unique_ptr createSystem(const std::string &Name); + protected: // Protected ctor, only subclasses can instantiate. System(const std::string &Name) noexcept; - // Dtor. Only an empty System can be destroyed. - // PRE: empty() + // No copy and move. + System(const System&) = delete; + System(System&&) = delete; + System &operator=(const System&) = delete; + System &operator=(System&&) = delete; + +public: + // Dtor. Only a cleared System can be destroyed. + // PRE: SystemCleaned virtual ~System(void); +protected: + + // Sets SystemCleaned flag. Can be called only once and the System must not + // have any live Units. + // PRE: !SystemCleaned && empty() + // POST: SystemCleaned + void markCleaned(void) noexcept; + // Creates a Unit instance with the given UnitCreator, using the given Name - // if any, and registers the new Unit instance. + // if any, then registers the new Unit instance by calling the virtual + // member function registerUnit. + // NOTE: This function handles the member field CountUnits. + // PRE: !SystemCleaned Unit &createUnit(UnitCreator C, const std::string &Name = std::string()) noexcept; + // Registers the given Unit instance to the System. + // PRE: !isUnitRegistered(U) + // POST: isUnitRegistered(U) + virtual void registerUnit(Unit &U) noexcept = 0; + // Unregisters and destroys the given Unit. - // PRE: Units.find(&U) != Units.end() - void destroyUnit(Unit &U) noexcept; + // PRE: isUnitRegistered(U) + // POST: !isUnitRegistered(U) && 'U is destroyed' + virtual void destroyUnit(Unit &U) noexcept = 0; + + // Returns if the given Unit is registered in the System. + virtual bool isUnitRegistered(const Unit &U) const noexcept = 0; public: // The textual name of the System. const std::string Name; private: // Number of Units constructed in the system. - // NOTE: Should never decrement! + // NOTE: Should never be decremented! std::atomic CountUnits; - // Container for keeping reference of registered Units. - std::unordered_set Units; - - // Used to provide mutual exclusion when modifying Units. - std::mutex RegisterMutex; + // Indicates that the System has been cleaned and is ready for destruction. + // The field is initialized as 'false' and can be set by the member function + // markCleaned. + // Subclasses must set the flag upon destroying the System instance, which + // indicates to the destructor of the base-class that all the managed + // resources has been properly released. + std::atomic SystemCleaned; public: // Returns the number of Units constructed in the System so far, // including those being already destroyed. size_t numberOfConstructedUnits(void) const noexcept; // Returns the number of live Units, that have been constructed and not // destroyed yet. - size_t numberOfLiveUnits(void) const noexcept; + virtual size_t numberOfLiveUnits(void) const noexcept = 0; // Returns if the System has no live Units. - bool empty(void) const noexcept; + virtual bool empty(void) const noexcept = 0; }; } // End namespace rosa #endif // ROSA_CORE_SYSTEM_H diff --git a/lib/core/CMakeLists.txt b/lib/core/CMakeLists.txt index 2f4d488..6aff479 100644 --- a/lib/core/CMakeLists.txt +++ b/lib/core/CMakeLists.txt @@ -1,7 +1,8 @@ add_library(ROSACore Unit.cpp System.cpp + SystemImpl.cpp ) ROSA_add_library_dependencies(ROSACore ROSASupport) diff --git a/lib/core/System.cpp b/lib/core/System.cpp index 1474070..1d086d0 100644 --- a/lib/core/System.cpp +++ b/lib/core/System.cpp @@ -1,75 +1,68 @@ /******************************************************************************* * * File: System.cpp * * Contents: Implementation of System base-class. * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ -#include "rosa/core/System.h" -#include "rosa/core/Unit.h" +#include "SystemImpl.h" #include "rosa/config/config.h" +#include "rosa/core/Unit.h" +#include "rosa/core/System.h" #include "rosa/support/debug.hpp" #include "rosa/support/log.h" namespace rosa { +std::unique_ptr System::createSystem(const std::string &Name) { + return std::unique_ptr(new SystemImpl(Name)); +} + System::System(const std::string &Name) noexcept : Name(Name), - CountUnits(0) { + CountUnits(0), + SystemCleaned(false) { LOG_TRACE("Creating System (" + Name + ")"); } System::~System(void) { LOG_TRACE("Destroying System (" + Name + ")"); - if (!empty()) { - ROSA_CRITICAL("Trying to destroy a non-empty System"); + if (!SystemCleaned) { + ROSA_CRITICAL("Trying to destroy a uncleaned System (" + Name + ")"); + } +} + +void System::markCleaned(void) noexcept { + if (SystemCleaned) { + ROSA_CRITICAL("System (" + Name + ") has been already mark cleaned"); + } else if (!empty()) { + ROSA_CRITICAL("Trying to mark a non-empty System (" + Name + ")"); + } else { + SystemCleaned = true; + LOG_TRACE("System (" + Name + ") is marked clean"); } } Unit &System::createUnit(UnitCreator C, const std::string &Name) noexcept { + if (SystemCleaned) { + ROSA_CRITICAL("Trying to create a Unit in an already cleaned System (" + + Name + ")"); + } const uint64_t Id = ++CountUnits; const std::string N = Name.empty() ? "Unit_" + std::to_string(Id) : Name; Unit *U = C(Id, N, *this); - // Scope protected container access. - { - // Obtain exclusive access and instert the Unit. - std::lock_guard L(RegisterMutex); - auto result = Units.insert(U); - if (!result.second) { - ROSA_CRITICAL("Could not register Unit"); - } - } + registerUnit(*U); LOG_TRACE("Unit created and registered (" + U->FullName + ")"); return *U; } -void System::destroyUnit(Unit &U) noexcept { - ASSERT(Units.find(&U) != Units.end()); - LOG_TRACE("Destroying Unit (" + U.FullName + ")"); - // Scope protected container access. - { - // Obtain exclusive access and remove the Unit. - std::lock_guard L(RegisterMutex); - auto result = Units.erase(&U); - // NOTE: This case is catched by assertion when that is enabled. - if (!result) { - ROSA_CRITICAL("Trying to remove unregistered Unit"); - } - } - delete &U; -} - size_t System::numberOfConstructedUnits(void) const noexcept { return CountUnits; } -size_t System::numberOfLiveUnits(void) const noexcept { return Units.size(); } - -bool System::empty(void) const noexcept { return numberOfLiveUnits() == 0; } - } // End namespace rosa diff --git a/lib/core/SystemImpl.cpp b/lib/core/SystemImpl.cpp new file mode 100644 index 0000000..b3b29b5 --- /dev/null +++ b/lib/core/SystemImpl.cpp @@ -0,0 +1,68 @@ +/******************************************************************************* + * + * File: SystemImpl.cpp + * + * Contents: Definition of the SystemImpl class + * + * Copyright 2017 + * + * Author: David Juhasz (david.juhasz@tuwien.ac.at) + * + ******************************************************************************/ + +#include "SystemImpl.h" +#include "rosa/core/Unit.h" +#include "rosa/config/config.h" +#include "rosa/support/debug.hpp" +#include "rosa/support/log.h" + +namespace rosa { + +SystemImpl::SystemImpl(const std::string &Name) noexcept : System(Name) {} + +SystemImpl::~SystemImpl(void) { + if (!empty()) { + ROSA_CRITICAL("Trying to destroy a non-empty System (" + Name + ")"); + } else { + markCleaned(); + } +} + +void SystemImpl::registerUnit(Unit &U) noexcept { + // Obtain exclusive access and instert the Unit. + std::lock_guard L(RegisterMutex); + auto R = Units.insert(&U); + if (!R.second) { + ROSA_CRITICAL("Could not register Unit"); + } +} + +void SystemImpl::destroyUnit(Unit &U) noexcept { + ASSERT(isUnitRegistered(U)); + LOG_TRACE("Destroying Unit (" + U.FullName + ")"); + // Scope protected container access. + { + // Obtain exclusive access and remove the Unit. + std::lock_guard L(RegisterMutex); + auto R = Units.erase(&U); + // NOTE: This case is catched by assertion when that is enabled. + if (!R) { + ROSA_CRITICAL("Trying to remove unregistered Unit"); + } + } + delete &U; +} + +bool SystemImpl::isUnitRegistered(const Unit &U) const noexcept { + // NOTE: Casting away constness is safe here. + return Units.find(const_cast(&U)) != Units.cend(); +} + +size_t SystemImpl::numberOfLiveUnits(void) const noexcept { + return Units.size(); +} + +bool SystemImpl::empty(void) const noexcept { return Units.empty(); } + +} // End namespace rosa + diff --git a/lib/core/SystemImpl.h b/lib/core/SystemImpl.h new file mode 100644 index 0000000..e1f3816 --- /dev/null +++ b/lib/core/SystemImpl.h @@ -0,0 +1,61 @@ +/******************************************************************************* + * + * File: SystemImpl.h + * + * Contents: Declaration of a basic implementation of the System interface. + * + * Copyright 2017 + * + * Author: David Juhasz (david.juhasz@tuwien.ac.at) + * + ******************************************************************************/ + +#ifndef ROSA_LIB_CORE_SYSTEMIMPL_H +#define ROSA_LIB_CORE_SYSTEMIMPL_H + +#include "rosa/core/System.h" +#include +#include + +namespace rosa { + +class SystemImpl : public System { +public: + SystemImpl(const std::string &Name) noexcept; + + ~SystemImpl(void); + +private: + // Container for keeping reference of registered Units. + std::unordered_set Units; + + // Used to provide mutual exclusion when modifying Units. + std::mutex RegisterMutex; + +protected: + // Registers the given Unit instance to the System. + // PRE: !isUnitRegistered(U) + // POST: isUnitRegistered(U) + void registerUnit(Unit &U) noexcept override; + + // Unregisters and destroys the given Unit. + // PRE: isUnitRegistered(U) + // POST: !isUnitRegistered(U) && 'U is destroyed' + void destroyUnit(Unit &U) noexcept override; + + // Returns if the given Unit is registered in the System. + bool isUnitRegistered(const Unit &U) const noexcept override; + +public: + // Returns the number of live Units, that have been constructed and not + // destroyed yet. + size_t numberOfLiveUnits(void) const noexcept override; + + // Returns if the System has no live Units. + bool empty(void) const noexcept override; +}; + +} // End namespace rosa + +#endif // ROSA_LIB_CORE_SYSTEMIMPL_H +