diff --git a/CMakeLists.txt b/CMakeLists.txt index 850c790..b009cbb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,10 +22,15 @@ if(NOT MIND_VOSCILLATOR_PERIOD_MS GREATER 0) endif() math(EXPR MIND_VOSCILLATOR_FREQ_MS "1000 / ${MIND_VOSCILLATOR_PERIOD_MS}") +# World thread configuration +option(WORLD_USE_BODY_THREAD + "Use body thread for world component instead of separate world thread" OFF) + # Configure config.h configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/include/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/include/config.h + @ONLY ) # Include directories diff --git a/include/config.h.in b/include/config.h.in index 9680bab..a764bea 100644 --- a/include/config.h.in +++ b/include/config.h.in @@ -9,6 +9,9 @@ #define CONFIG_MIND_VOSCILLATOR_PERIOD_MS @MIND_VOSCILLATOR_PERIOD_MS@ #define CONFIG_MIND_VOSCILLATOR_FREQ_MS @MIND_VOSCILLATOR_FREQ_MS@ +/* World thread configuration */ +#cmakedefine WORLD_USE_BODY_THREAD + /* Cross-compilation configuration */ #cmakedefine CMAKE_CROSSCOMPILING diff --git a/main.cpp b/main.cpp index c88c8b8..4c9808b 100644 --- a/main.cpp +++ b/main.cpp @@ -10,17 +10,17 @@ int main(int argc, char *argv[], char *envp[]) */ std::cout << "CRT:" << __func__ << ": about to JOLT Mrntt with cmdline args" << '\n'; - smo::mrntt::mrntt->getIoService().post( + smo::mrntt::thread->getIoService().post( [argc, argv, envp]() { std::cout << "Mrntt:" << __func__ << ":JOLTED: setting cmdline args" << '\n'; smo::CrtCommandLineArgs::set(argc, argv, envp); - smo::mrntt::mrntt->getIoService().stop(); + smo::mrntt::thread->getIoService().stop(); } ); - smo::mrntt::mrntt->thread.join(); + smo::mrntt::thread->thread.join(); std::cout << "CRT:" << __func__ << ": Mrntt exited with code '" << smo::mrntt::exitCode << "'\n"; return smo::mrntt::exitCode; diff --git a/smocore/CMakeLists.txt b/smocore/CMakeLists.txt index 2293122..43fc808 100644 --- a/smocore/CMakeLists.txt +++ b/smocore/CMakeLists.txt @@ -3,6 +3,8 @@ add_library(smocore STATIC mind.cpp opts.cpp componentThread.cpp + component.cpp + body/body.cpp ) target_include_directories(smocore PUBLIC @@ -12,8 +14,9 @@ target_include_directories(smocore PUBLIC # Link against pthread for CPU affinity functions find_package(Threads REQUIRED) target_link_libraries(smocore - PRIVATE Threads::Threads senseApis) + PRIVATE Threads::Threads senseApis mindManager) add_subdirectory(marionette) add_subdirectory(deviceManager) add_subdirectory(senseApis) +add_subdirectory(mindManager) diff --git a/smocore/body/body.cpp b/smocore/body/body.cpp new file mode 100644 index 0000000..48ac9bf --- /dev/null +++ b/smocore/body/body.cpp @@ -0,0 +1,193 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +namespace smo { +namespace body { + +Body::Body(Mind &parent, const std::shared_ptr &thread) + : MindComponent(parent, thread) +{ +} + +class Body::InitializeReq +: public AsynchronousContinuation, + public ContinuationTarget +{ +public: + InitializeReq( + Mind &parent, const std::shared_ptr &caller, + bodyLifetimeMgmtOpCbFn callback) + : AsynchronousContinuation(callback), + ContinuationTarget(caller), + parent(parent) + {} + + void callOriginalCbFn(bool success) + { + if (originalCbFn) + { + caller->getIoService().post( + std::bind(originalCbFn, success)); + } + } + +private: + Mind &parent; + +public: + void initializeReq1( + [[maybe_unused]] std::shared_ptr context + ) + { + auto self = ComponentThread::getSelf(); + if (self->id != ComponentThread::BODY) + { + throw std::runtime_error(std::string(__func__) + + ": Must be executed on Body thread"); + } + + /** EXPLANATION: + * The ComponentThread instance we pass in here is the one that will be + * used by Senseapi libs to perform device-independent background + * operations. + * For example, liblivoxProto1's BroadcastListener will use this thread + * to listen for UDP broadcast dgrams from Livox devices. + * + * Right now we use Marionette, but there's a strong argument for using + * Body instead since it's meant to handle device-management operations. + */ + sense_api::SenseApiManager::getInstance() + .loadAllSenseApiLibsFromOptions(caller); + + std::cout << sense_api::SenseApiManager::getInstance().stringifyLibs() + << std::endl; + + if (OptionParser::getOptions().verbose) + { + std::cout << __func__ << ": About to initializeAllSenseApiLibs" + << '\n'; + } + sense_api::SenseApiManager::getInstance().initializeAllSenseApiLibs(); + + if (OptionParser::getOptions().verbose) + { + std::cout << __func__ << ": About to attachAllSenseDevicesFromSpecs" + << '\n'; + } + sense_api::SenseApiManager::getInstance() + .attachAllSenseDevicesFromSpecsReq( + std::bind( + &InitializeReq::initializeReq2, + context.get(), context, + std::placeholders::_1)); + } + + void initializeReq2( + [[maybe_unused]] std::shared_ptr context, + smo::AsynchronousLoop &results + ) + { + parent.bodyComponentInitialized = true; + std::cout << "Mrntt: attached " + << results.nSucceeded << " of " << results.nTotal + << " sense devices." << "\n"; + + callOriginalCbFn(results.nSucceeded == results.nTotal); + } +}; + +class Body::FinalizeReq +: public InitializeReq +{ +public: + using InitializeReq::InitializeReq; + +public: + void finalizeReq1( + [[maybe_unused]] std::shared_ptr context + ) + { + auto self = ComponentThread::getSelf(); + if (self->id != ComponentThread::BODY) + { + throw std::runtime_error(std::string(__func__) + + ": Must be executed on Body thread"); + } + + std::cout << "Mrntt: About to detach all sense devices." << "\n"; + sense_api::SenseApiManager::getInstance().detachAllSenseDevicesReq( + std::bind( + &FinalizeReq::finalizeReq2, + context.get(), context, + std::placeholders::_1)); + } + + void finalizeReq2( + [[maybe_unused]] std::shared_ptr context, + smo::AsynchronousLoop &results + ) + { + std::cout << "Mrntt: Successfully detached " + << results.nSucceeded << " of " << results.nTotal + << " sense devices." << "\n"; + + std::cout << "Mrntt: About to unload all sense api libs." << "\n"; + sense_api::SenseApiManager::getInstance().unloadAllSenseApiLibs(); + callOriginalCbFn(results.nSucceeded == results.nTotal); + } +}; + +void Body::initializeReq(bodyLifetimeMgmtOpCbFn callback) +{ + auto mrntt = ComponentThread::getSelf(); + + if (mrntt->id != ComponentThread::MRNTT) + { + throw std::runtime_error(std::string(__func__) + + ": Must be invoked by Mrntt thread"); + } + + auto request = std::make_shared( + parent, mrntt, callback); + + thread->getIoService().post( + std::bind( + &InitializeReq::initializeReq1, + request.get(), request)); +} + +void Body::finalizeReq(bodyLifetimeMgmtOpCbFn callback) +{ + auto mrntt = ComponentThread::getSelf(); + + if (mrntt->id != ComponentThread::MRNTT) + { + throw std::runtime_error(std::string(__func__) + + ": Must be invoked by Mrntt thread"); + } + + if (!parent.bodyComponentInitialized) + { + std::cout << "Mrntt: Body component not initialized. " + << "Skipping finalization." << "\n"; + callback(true); + return; + } + + auto request = std::make_shared( + parent, mrntt, callback); + + thread->getIoService().post( + std::bind( + &FinalizeReq::finalizeReq1, + request.get(), request)); +} + +} // namespace body +} // namespace smo diff --git a/smocore/component.cpp b/smocore/component.cpp new file mode 100644 index 0000000..dc94241 --- /dev/null +++ b/smocore/component.cpp @@ -0,0 +1,27 @@ +#include + +namespace smo { + +Component::Component(const std::shared_ptr &thread) +: thread(thread) +{ +} + +MindComponent::MindComponent( + Mind &parent, const std::shared_ptr &thread) +: Component(thread), +parent(parent) +{ +} + +namespace mrntt { + +MarionetteComponent::MarionetteComponent( + const std::shared_ptr &thread) +: Component(thread) +{ +} + +} // namespace mrntt + +} // namespace smo diff --git a/smocore/componentThread.cpp b/smocore/componentThread.cpp index 2ff4ecf..a6e1a66 100644 --- a/smocore/componentThread.cpp +++ b/smocore/componentThread.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -16,7 +17,7 @@ thread_local std::shared_ptr thisComponentThread; // Implementation of static method std::shared_ptr ComponentThread::getMrntt() { - return mrntt::mrntt; + return mrntt::thread; } void MarionetteThread::initializeTls(void) @@ -93,7 +94,7 @@ void MindThread::main(MindThread& self) } if (sendExceptionInd) - { mrntt::mrntt->exceptionInd(self.shared_from_this()); } + { mrntt::thread->exceptionInd(self.shared_from_this()); } } std::cout << self.name << ":" << __func__ << ": Exited event loop" << "\n"; @@ -228,7 +229,7 @@ void MindThread::joltThreadReq(threadLifetimeMgmtOpCbFn callback) + ": invoked on mrntt thread"); } - std::shared_ptr mrntt = mrntt::mrntt; + std::shared_ptr mrntt = mrntt::thread; std::shared_ptr target = getParent().getComponentThread(id); auto request = std::make_shared( @@ -330,7 +331,7 @@ public: * An exception has occurred in one of a mind's threads. We need to * shut down all of that particular mind's threads. */ - globalMind->finalizeReq( + smo::mind::globalMind->finalizeReq( std::bind( &MindShutdownIndOp::mindShutdownInd2, context.get(), context)); @@ -351,7 +352,7 @@ public: * So this should ideally be a loop * through all running Minds, calling finalizeReq on each one. */ - globalMind->finalizeReq( + smo::mind::globalMind->finalizeReq( std::bind( &MindShutdownIndOp::mindShutdownInd2, context.get(), context)); diff --git a/smocore/include/body/body.h b/smocore/include/body/body.h new file mode 100644 index 0000000..091d125 --- /dev/null +++ b/smocore/include/body/body.h @@ -0,0 +1,33 @@ +#ifndef _BODY_COMPONENT_H +#define _BODY_COMPONENT_H + +#include +#include + +namespace smo { + +class Mind; +class ComponentThread; + +namespace body { + +class Body +: public MindComponent +{ +public: + Body(Mind &parent, const std::shared_ptr &thread); + ~Body() = default; + + typedef std::function bodyLifetimeMgmtOpCbFn; + void initializeReq(bodyLifetimeMgmtOpCbFn callback); + void finalizeReq(bodyLifetimeMgmtOpCbFn callback); + +private: + class InitializeReq; + class FinalizeReq; +}; + +} // namespace body +} // namespace smo + +#endif // _BODY_COMPONENT_H \ No newline at end of file diff --git a/smocore/include/component.h b/smocore/include/component.h new file mode 100644 index 0000000..0717f0a --- /dev/null +++ b/smocore/include/component.h @@ -0,0 +1,58 @@ +#ifndef COMPONENT_H +#define COMPONENT_H + +#include +#include +#include + +namespace smo { + +class Mind; +class ComponentThread; + +class Component +{ +public: + Component(const std::shared_ptr &thread); + ~Component() = default; + +public: + std::shared_ptr thread; + +public: +}; + +class MindComponent +: public Component +{ +public: + MindComponent(Mind &parent, const std::shared_ptr &thread); + ~MindComponent() = default; + +public: + Mind &parent; +}; + +namespace mrntt { + +class MarionetteComponent +: public Component +{ +public: + MarionetteComponent(const std::shared_ptr &thread); + ~MarionetteComponent() = default; + +public: + typedef std::function mrnttLifetimeMgmtOpCbFn; + void initializeReq(mrnttLifetimeMgmtOpCbFn callback); + void finalizeReq(mrnttLifetimeMgmtOpCbFn callback); + +private: + class MrnttLifetimeMgmtOp; +}; + +} // namespace mrntt + +} // namespace smo + +#endif // COMPONENT_H diff --git a/smocore/include/componentThread.h b/smocore/include/componentThread.h index e6157f3..0c70e05 100644 --- a/smocore/include/componentThread.h +++ b/smocore/include/componentThread.h @@ -167,8 +167,8 @@ public: }; namespace mrntt { -extern std::shared_ptr mrntt; -} +extern std::shared_ptr thread; +} // namespace mrntt } // namespace smo diff --git a/smocore/include/director/director.h b/smocore/include/director/director.h index ea05287..6652c4e 100644 --- a/smocore/include/director/director.h +++ b/smocore/include/director/director.h @@ -2,16 +2,26 @@ #define DIRECTOR_H #include +#include #include #include namespace smo { + +class Mind; +class ComponentThread; + namespace director { -class Director { +class Director +: public MindComponent +{ public: - Director() = default; - ~Director() = default; + Director(Mind &parent, const std::shared_ptr &thread) + : MindComponent(parent, thread) + {} + + ~Director() = default; /** EXPLANATION: * We allow SMO to prioritize negtrins over injected goals, so that it can diff --git a/smocore/include/marionette/marionette.h b/smocore/include/marionette/marionette.h index 74c4a16..a2ed092 100644 --- a/smocore/include/marionette/marionette.h +++ b/smocore/include/marionette/marionette.h @@ -5,6 +5,7 @@ #include #include #include +#include namespace smo { @@ -13,8 +14,8 @@ class MarionetteThread; namespace mrntt { extern std::atomic exitCode; - void exitMarionetteLoop(); +extern mrntt::MarionetteComponent mrntt; } // namespace mrntt diff --git a/smocore/include/mind.h b/smocore/include/mind.h index a597b8a..14c7f4f 100644 --- a/smocore/include/mind.h +++ b/smocore/include/mind.h @@ -8,9 +8,11 @@ #include #include +#include +#include #include #include -#include +#include namespace smo { @@ -23,8 +25,6 @@ public: typedef std::function mindLifetimeMgmtOpCbFn; void initializeReq(mindLifetimeMgmtOpCbFn callback); void finalizeReq(mindLifetimeMgmtOpCbFn callback); - void initializeBodyReq(mindLifetimeMgmtOpCbFn callback); - void finalizeBodyReq(mindLifetimeMgmtOpCbFn callback); // ComponentThread access methods std::shared_ptr getComponentThread( @@ -45,15 +45,19 @@ public: // CPU distribution method void distributeAndPinThreadsAcrossCpus(); -public: - std::thread directorThread; - std::thread simulatorThread; - std::thread subconsciousThread; +private: + // Collection of ComponentThread instances (excluding marionette) + std::vector> componentThreads; - director::Director director; - simulator::Simulator canvas; +public: + director::Director director; + simulator::Simulator canvas; + MindComponent subconscious; + body::Body body; + MindComponent world; private: + friend class body::Body; /** * Indicates whether all mind threads have been JOLTed at least once. * @@ -75,13 +79,10 @@ private: */ bool threadsHaveBeenJolted = false, bodyComponentInitialized = false; - // Collection of ComponentThread instances (excluding marionette) - std::vector> componentThreads; +private: class MindLifetimeMgmtOp; class MindThreadLifetimeMgmtOp; - class InitializeBodyReq; - class FinalizeBodyReq; }; // Global Mind instance will be defined in marionette.cpp diff --git a/smocore/include/mindManager/mindManager.h b/smocore/include/mindManager/mindManager.h new file mode 100644 index 0000000..51b7386 --- /dev/null +++ b/smocore/include/mindManager/mindManager.h @@ -0,0 +1,48 @@ +#ifndef _SMO_MIND_MANAGER_H +#define _SMO_MIND_MANAGER_H + +#include +#include +#include +#include + +namespace smo { +namespace mind { + +/** EXPLANATION: + * MindManager is responsible for managing the lifecycle of all minds. + * It is responsible for creating, destroying, and managing the minds. + * + * For now it does nothing since we haven't yet added support for multiple + * minds. + */ +class MindManager +{ +public: + MindManager(void) = default; + ~MindManager(void) = default; + + static MindManager& getInstance() + { + static MindManager instance; + return instance; + } + +public: + void initialize(void) {}; + void finalize(void) {}; + + std::shared_ptr getMind(void) const; + void addMind(const std::shared_ptr& mind); + void removeMind(const std::shared_ptr& mind); + +public: + std::vector> minds; +}; + +extern std::shared_ptr globalMind; + +} // namespace mind +} // namespace smo + +#endif // _SMO_MIND_MANAGER_H diff --git a/smocore/include/simulator/simulator.h b/smocore/include/simulator/simulator.h index c20deed..8015bc6 100644 --- a/smocore/include/simulator/simulator.h +++ b/smocore/include/simulator/simulator.h @@ -2,15 +2,25 @@ #define SIMULATOR_H #include +#include #include namespace smo { + +class Mind; +class ComponentThread; + namespace simulator { -class Simulator { +class Simulator +: public MindComponent +{ public: - Simulator() = default; - ~Simulator() = default; + Simulator(Mind &parent, const std::shared_ptr &thread) + : MindComponent(parent, thread) + {} + + ~Simulator() = default; void initialize(); void loadScene(Scene::Id sceneId, Scene &scene); diff --git a/smocore/marionette/CMakeLists.txt b/smocore/marionette/CMakeLists.txt index 2fd28f2..62560f1 100644 --- a/smocore/marionette/CMakeLists.txt +++ b/smocore/marionette/CMakeLists.txt @@ -1,10 +1,12 @@ add_library(marionette STATIC - marionette.cpp + main.cpp salmanoff.cpp + lifetime.cpp ) target_link_libraries(marionette smocore + mindManager ) target_include_directories(marionette PUBLIC diff --git a/smocore/marionette/lifetime.cpp b/smocore/marionette/lifetime.cpp new file mode 100644 index 0000000..f8df6d5 --- /dev/null +++ b/smocore/marionette/lifetime.cpp @@ -0,0 +1,145 @@ +#include +#include +#include +#include +#include +#include +#include + +namespace smo { +namespace mrntt { + +class MarionetteComponent::MrnttLifetimeMgmtOp +: public AsynchronousContinuation, + public ContinuationTarget +{ +public: + MrnttLifetimeMgmtOp( + MarionetteComponent &parent, const std::shared_ptr &caller, + mrnttLifetimeMgmtOpCbFn callback) + : AsynchronousContinuation(callback), + ContinuationTarget(caller), + parent(parent) + {} + + void callOriginalCbFn(bool success) + { + if (originalCbFn) + { + caller->getIoService().post( + std::bind(originalCbFn, success)); + } + } + +private: + MarionetteComponent &parent; + +public: + void initializeReq1_posted( + [[maybe_unused]] std::shared_ptr context + ) + { + auto self = ComponentThread::getSelf(); + if (self->id != ComponentThread::MRNTT) + { + throw std::runtime_error(std::string(__func__) + + ": Must be executed on Marionette thread"); + } + + smo::mind::globalMind = std::make_shared(); + smo::mind::globalMind->initializeReq( + std::bind( + &MrnttLifetimeMgmtOp::initializeReq2, + this, context, std::placeholders::_1)); + } + + void initializeReq2( + std::shared_ptr context, + bool success + ) + { + if (!success) + { + std::cerr << __func__ << ": Failed to initialize globalMind" + << std::endl; + context->callOriginalCbFn(false); + return; + } + + context->callOriginalCbFn(success); + } + + void finalizeReq1_posted( + [[maybe_unused]] std::shared_ptr context + ) + { + auto self = ComponentThread::getSelf(); + if (self->id != ComponentThread::MRNTT) + { + throw std::runtime_error(std::string(__func__) + + ": Must be executed on Marionette thread"); + } + + smo::mind::globalMind->finalizeReq( + std::bind( + &MrnttLifetimeMgmtOp::finalizeReq2, + this, context, std::placeholders::_1)); + } + + void finalizeReq2( + std::shared_ptr context, + bool success + ) + { + if (!success) + { + std::cerr << __func__ << ": globalMind finalization failed" + << std::endl; + context->callOriginalCbFn(false); + return; + } + + context->callOriginalCbFn(success); + } +}; + +void MarionetteComponent::initializeReq(mrnttLifetimeMgmtOpCbFn callback) +{ + auto mrntt = ComponentThread::getSelf(); + + if (mrntt->id != ComponentThread::MRNTT) + { + throw std::runtime_error(std::string(__func__) + + ": Must be executed on Marionette thread"); + } + + auto request = std::make_shared( + *this, mrntt, callback); + + mrntt->getIoService().post( + std::bind( + &MrnttLifetimeMgmtOp::initializeReq1_posted, + request.get(), request)); +} + +void MarionetteComponent::finalizeReq(mrnttLifetimeMgmtOpCbFn callback) +{ + auto mrntt = ComponentThread::getSelf(); + + if (mrntt->id != ComponentThread::MRNTT) + { + throw std::runtime_error(std::string(__func__) + + ": Must be executed on Marionette thread"); + } + + auto request = std::make_shared( + *this, mrntt, callback); + + mrntt->getIoService().post( + std::bind( + &MrnttLifetimeMgmtOp::finalizeReq1_posted, + request.get(), request)); +} + +} // namespace mrntt +} // namespace smo diff --git a/smocore/marionette/marionette.cpp b/smocore/marionette/main.cpp similarity index 69% rename from smocore/marionette/marionette.cpp rename to smocore/marionette/main.cpp index 449b487..47a1d48 100644 --- a/smocore/marionette/marionette.cpp +++ b/smocore/marionette/main.cpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include #include #include @@ -20,21 +20,46 @@ void CrtCommandLineArgs::set(int argc, char *argv[], char *envp[]) crtCommandLineArgs = CrtCommandLineArgs(argc, argv, envp); } -// Global Mind instance -std::shared_ptr globalMind; - namespace mrntt { std::atomic exitCode; // Global marionette thread instance -std::shared_ptr mrntt = std::make_shared(); +std::shared_ptr thread = + std::make_shared(); +MarionetteComponent mrntt(thread); void exitMarionetteLoop() { - mrntt::mrntt->keepLooping = false; - mrntt::mrntt->getIoService().stop(); + mrntt::thread->keepLooping = false; + mrntt::thread->getIoService().stop(); std::cout << "Mrntt: Signaled main loop to exit." << "\n"; } +void marionetteFinalizeReqCb(bool success) +{ + if (!success) { + std::cerr << __func__ << ": Failed to finalize Marionette." << '\n'; + } + std::cout << __func__ << ": Marionette finalized." << '\n'; + exitMarionetteLoop(); +} + +void marionetteInitializeReqCb(bool success) +{ + if (success) + { + std::cout << __func__ << ": Marionette initialized." << '\n'; + return; + } + + std::cerr << __func__ << ": Failed to initialize Marionette. Shutting down." + << '\n'; + + mrntt::mrntt.finalizeReq( + std::bind( + &mrntt::marionetteFinalizeReqCb, + std::placeholders::_1)); +} + } // namespace mrntt void MarionetteThread::main(MarionetteThread& self) @@ -66,7 +91,10 @@ void MarionetteThread::main(MarionetteThread& self) default: break; } - self.userShutdownInd(); + mrntt::mrntt.finalizeReq( + std::bind( + &mrntt::marionetteFinalizeReqCb, + std::placeholders::_1)); } ); @@ -86,46 +114,33 @@ void MarionetteThread::main(MarionetteThread& self) throw JustPrintUsageNoError(options); } + /** EXPLANATION: + * Initialize Salmanoff's Manager classes first. + * Manager classes' initialization is synchronous in nature, so we + * don't need the minds to be running to initialize them. + * + * Then we initialize the Minds. Minds are asynchronous and they + * call upon the async methods of the Manager classes. Device + * attachment is actually Mind specific and not Smo-global + * + * It is arguable whether library loading is Mind specific or + * Smo-global. You could argue that libraries should be loaded and + * unloaded dynamically as-needed by the bodies and worlds of + * particular Minds. You could also argue that we should load all + * libraries at startup and unload them at shutdown. + * + * The latter is cleaner and more resource-respecting. The former is + * easier to implement. + */ initializeSalmanoff(); callShutdownSalmanoff = true; - self.getIoService().post([]() - { - /** EXPLANATION: - * Initialize Salmanoff's Manager classes first. - * Manager classes' initialization is synchronous in nature, so we - * don't need the minds to be running to initialize them. - * - * Then we initialize the Minds. Minds are asynchronous and they - * call upon the async methods of the Manager classes. Device - * attachment is actually Mind specific and not Smo-global - * - * It is arguable whether library loading is Mind specific or - * Smo-global. You could argue that libraries should be loaded and - * unloaded dynamically as-needed by the bodies and worlds of - * particular Minds. You could also argue that we should load all - * libraries at startup and unload them at shutdown. - * - * The latter is cleaner and more resource-respecting. The former is - * easier to implement. - */ - // Create new Mind instance just before initializeReq - globalMind = std::make_shared(); - globalMind->initializeReq( - [](bool success) - { - if (!success) - { - std::cerr << "Failed to initialize Mind object " - "(threads)" << '\n'; - } + // Create new Mind instance just before initializeReq + mrntt::mrntt.initializeReq( + std::bind( + &mrntt::marionetteInitializeReqCb, std::placeholders::_1)); - std::cout << "Mrntt: Mind object (threads) initialized." - << '\n'; - }); - }); - - std::cout << __func__ << ": Entering event loop" << "\n"; + std::cout << __func__ << ": Entering event loop" << "\n"; /* We loop here because when an exception occurs in mrntt, we need to * both direct the mind threads to exit gracefully, and then we also @@ -204,12 +219,13 @@ void MarionetteThread::main(MarionetteThread& self) if (callFinalizeReq) { - globalMind->finalizeReq([](bool success) +std::cout << __func__ << ": About call finalizeReq at end of mrnttMain." << '\n'; + mind::globalMind->finalizeReq([](bool success) { if (!success) { std::cerr << "Failed to finalize Mind object (threads)" << '\n'; } - mrntt::mrntt->getIoService().stop(); + mrntt::thread->getIoService().stop(); }); self.getIoService().reset(); self.getIoService().run(); diff --git a/smocore/marionette/salmanoff.cpp b/smocore/marionette/salmanoff.cpp index 14da021..b66c396 100644 --- a/smocore/marionette/salmanoff.cpp +++ b/smocore/marionette/salmanoff.cpp @@ -1,7 +1,7 @@ #include +#include #include #include -#include #include @@ -11,6 +11,7 @@ void initializeSalmanoff(void) { std::cout << __func__ << ": Entered." << std::endl; + mind::MindManager::getInstance().initialize(); sense_api::SenseApiManager::getInstance().initialize(); device::DeviceManager::getInstance().initialize(); device::DeviceManager::getInstance().collateAllDapSpecs(); @@ -23,6 +24,7 @@ void shutdownSalmanoff(void) std::cout << __func__ << ": Entered." << std::endl; device::DeviceManager::getInstance().finalize(); sense_api::SenseApiManager::getInstance().finalize(); + mind::MindManager::getInstance().finalize(); } } // namespace smo diff --git a/smocore/mind.cpp b/smocore/mind.cpp index 4661150..3241c5f 100644 --- a/smocore/mind.cpp +++ b/smocore/mind.cpp @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include namespace smo { @@ -13,9 +15,22 @@ Mind::Mind(void) std::make_shared(ComponentThread::DIRECTOR, *this), std::make_shared(ComponentThread::SIMULATOR, *this), std::make_shared(ComponentThread::SUBCONSCIOUS, *this), - std::make_shared(ComponentThread::BODY, *this), - std::make_shared(ComponentThread::WORLD, *this) - } + std::make_shared(ComponentThread::BODY, *this) +#ifndef WORLD_USE_BODY_THREAD + , std::make_shared(ComponentThread::WORLD, *this) +#endif + }, + director(*this, componentThreads[0]), + canvas(*this, componentThreads[1]), + subconscious(*this, componentThreads[2]), + body(*this, componentThreads[3]), + world(*this, +#ifndef WORLD_USE_BODY_THREAD + componentThreads[4] +#else + componentThreads[3] +#endif + ) { } @@ -106,7 +121,7 @@ public: { std::cout << "Mrntt: All mind threads started." << "\n"; - parent.initializeBodyReq( + parent.body.initializeReq( std::bind( &MindLifetimeMgmtOp::initializeReq3, context.get(), context, std::placeholders::_1)); @@ -192,180 +207,12 @@ void Mind::finalizeReq(mindLifetimeMgmtOpCbFn callback) auto request = std::make_shared( *this, callback); - finalizeBodyReq( + body.finalizeReq( std::bind( &MindLifetimeMgmtOp::finalizeReq1, request.get(), request, std::placeholders::_1)); } -class Mind::InitializeBodyReq -: public MindLifetimeMgmtOp, public ContinuationTarget -{ -public: - InitializeBodyReq( - Mind &parent, const std::shared_ptr &caller, - mindLifetimeMgmtOpCbFn callback) - : MindLifetimeMgmtOp(parent, callback), ContinuationTarget(caller) - {} - - void callOriginalCbFn(bool success) - { - if (originalCbFn) - { - caller->getIoService().post( - std::bind(originalCbFn, success)); - } - } - -public: - void initializeBodyReq1( - [[maybe_unused]] std::shared_ptr context - ) - { - auto self = ComponentThread::getSelf(); - if (self->id != ComponentThread::BODY) - { - throw std::runtime_error(std::string(__func__) - + ": Must be executed on Body thread"); - } - - /** EXPLANATION: - * The ComponentThread instance we pass in here is the one that will be - * used by Senseapi libs to perform device-independent background - * operations. - * For example, liblivoxProto1's BroadcastListener will use this thread - * to listen for UDP broadcast dgrams from Livox devices. - * - * Right now we use Marionette, but there's a strong argument for using - * Body instead since it's meant to handle device-management operations. - */ - sense_api::SenseApiManager::getInstance() - .loadAllSenseApiLibsFromOptions(caller); - - std::cout << sense_api::SenseApiManager::getInstance().stringifyLibs() - << std::endl; - - if (OptionParser::getOptions().verbose) - { - std::cout << __func__ << ": About to initializeAllSenseApiLibs" - << '\n'; - } - sense_api::SenseApiManager::getInstance().initializeAllSenseApiLibs(); - - if (OptionParser::getOptions().verbose) - { - std::cout << __func__ << ": About to attachAllSenseDevicesFromSpecs" - << '\n'; - } - sense_api::SenseApiManager::getInstance() - .attachAllSenseDevicesFromSpecsReq( - std::bind( - &InitializeBodyReq::initializeBodyReq2, - context.get(), context, - std::placeholders::_1)); - } - - void initializeBodyReq2( - [[maybe_unused]] std::shared_ptr context, - smo::AsynchronousLoop &results - ) - { - parent.bodyComponentInitialized = true; - std::cout << "Mrntt: attached " - << results.nSucceeded << " of " << results.nTotal - << " sense devices." << "\n"; - - callOriginalCbFn(results.nSucceeded == results.nTotal); - } -}; - -class Mind::FinalizeBodyReq -: public InitializeBodyReq -{ -public: - using InitializeBodyReq::InitializeBodyReq; - -public: - void finalizeBodyReq1( - [[maybe_unused]] std::shared_ptr context - ) - { - auto self = ComponentThread::getSelf(); - if (self->id != ComponentThread::BODY) - { - throw std::runtime_error(std::string(__func__) - + ": Must be executed on Body thread"); - } - - std::cout << "Mrntt: About to detach all sense devices." << "\n"; - sense_api::SenseApiManager::getInstance().detachAllSenseDevicesReq( - std::bind( - &FinalizeBodyReq::finalizeBodyReq2, - context.get(), context, - std::placeholders::_1)); - } - - void finalizeBodyReq2( - [[maybe_unused]] std::shared_ptr context, - smo::AsynchronousLoop &results - ) - { - std::cout << "Mrntt: Successfully detached " - << results.nSucceeded << " of " << results.nTotal - << " sense devices." << "\n"; - - std::cout << "Mrntt: About to unload all sense api libs." << "\n"; - sense_api::SenseApiManager::getInstance().unloadAllSenseApiLibs(); - callOriginalCbFn(results.nSucceeded == results.nTotal); - } -}; - -void Mind::initializeBodyReq(mindLifetimeMgmtOpCbFn callback) -{ - auto mrntt = ComponentThread::getSelf(); - - if (mrntt->id != ComponentThread::MRNTT) - { - throw std::runtime_error(std::string(__func__) - + ": Must be invoked by Mrntt thread"); - } - - auto request = std::make_shared( - *this, mrntt, callback); - - this->getComponentThread(ComponentThread::BODY)->getIoService().post( - std::bind( - &InitializeBodyReq::initializeBodyReq1, - request.get(), request)); -} - -void Mind::finalizeBodyReq(mindLifetimeMgmtOpCbFn callback) -{ - auto mrntt = ComponentThread::getSelf(); - - if (mrntt->id != ComponentThread::MRNTT) - { - throw std::runtime_error(std::string(__func__) - + ": Must be invoked by Mrntt thread"); - } - - if (!bodyComponentInitialized) - { - std::cout << "Mrntt: Body component not initialized. " - << "Skipping finalization." << "\n"; - callback(true); - return; - } - - auto request = std::make_shared( - *this, mrntt, callback); - - this->getComponentThread(ComponentThread::BODY)->getIoService().post( - std::bind( - &FinalizeBodyReq::finalizeBodyReq1, - request.get(), request)); -} - void Mind::distributeAndPinThreadsAcrossCpus() { int cpuCount = ComponentThread::getAvailableCpuCount(); diff --git a/smocore/mindManager/CMakeLists.txt b/smocore/mindManager/CMakeLists.txt new file mode 100644 index 0000000..835cd83 --- /dev/null +++ b/smocore/mindManager/CMakeLists.txt @@ -0,0 +1,12 @@ +add_library(mindManager STATIC + mindManager.cpp +) + +target_include_directories(mindManager PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR}/../include +) + +target_link_libraries(mindManager + smocore +) diff --git a/smocore/mindManager/mindManager.cpp b/smocore/mindManager/mindManager.cpp new file mode 100644 index 0000000..32db1fd --- /dev/null +++ b/smocore/mindManager/mindManager.cpp @@ -0,0 +1,10 @@ +#include + + +namespace smo { +namespace mind { + +std::shared_ptr globalMind; + +} // namespace mind +} // namespace smo diff --git a/smocore/senseApis/senseApiManager.cpp b/smocore/senseApis/senseApiManager.cpp index 44d43b7..026efad 100644 --- a/smocore/senseApis/senseApiManager.cpp +++ b/smocore/senseApis/senseApiManager.cpp @@ -456,7 +456,9 @@ void SenseApiManager::detachAllSenseDevicesReq( std::cerr << __func__ << ": Exception: " << e.what() << "\n"; if (request->loop.incrementSuccessOrFailureAndTestForCompletionDueTo(false)) { +std::cout << __func__ << ": About to call original cbFn." << "\n"; request->callOriginalCallback(); +std::cout << __func__ << ": Just called original cbFn." << "\n"; } } }