diff --git a/smocore/componentThread.cpp b/smocore/componentThread.cpp index 2ad8cee..2ff4ecf 100644 --- a/smocore/componentThread.cpp +++ b/smocore/componentThread.cpp @@ -13,17 +13,18 @@ namespace smo { thread_local std::shared_ptr thisComponentThread; -namespace mrntt { -extern std::shared_ptr mrntt; -} - // Implementation of static method -std::shared_ptr ComponentThread::getMrntt() +std::shared_ptr ComponentThread::getMrntt() { return mrntt::mrntt; } -void ComponentThread::initializeTls(void) +void MarionetteThread::initializeTls(void) +{ + thisComponentThread = shared_from_this(); +} + +void MindThread::initializeTls(void) { thisComponentThread = shared_from_this(); } @@ -39,7 +40,7 @@ const std::shared_ptr ComponentThread::getSelf(void) return thisComponentThread; } -void ComponentThread::main(ComponentThread& self) +void MindThread::main(MindThread& self) { if (OptionParser::getOptions().verbose) @@ -98,13 +99,13 @@ void ComponentThread::main(ComponentThread& self) std::cout << self.name << ":" << __func__ << ": Exited event loop" << "\n"; } -class ComponentThread::ThreadLifetimeMgmtOp +class MindThread::ThreadLifetimeMgmtOp : public TargetedAsynchronousContinuation { public: ThreadLifetimeMgmtOp( const std::shared_ptr &caller, - const std::shared_ptr &target, + const std::shared_ptr &target, threadLifetimeMgmtOpCbFn callback) : TargetedAsynchronousContinuation( caller, callback), @@ -119,7 +120,7 @@ public: } public: - const std::shared_ptr target; + const std::shared_ptr target; public: void joltThreadReq1( @@ -204,7 +205,7 @@ void ComponentThread::cleanup(void) this->keepLooping = false; } -void ComponentThread::joltThreadReq(threadLifetimeMgmtOpCbFn callback) +void MindThread::joltThreadReq(threadLifetimeMgmtOpCbFn callback) { /** EXPLANATION: * We can't use shared_from_this() here because JOLTing occurs prior to @@ -227,9 +228,8 @@ void ComponentThread::joltThreadReq(threadLifetimeMgmtOpCbFn callback) + ": invoked on mrntt thread"); } - std::shared_ptr - mrntt = mrntt::mrntt, - target = parent.getComponentThread(id); + std::shared_ptr mrntt = mrntt::mrntt; + std::shared_ptr target = getParent().getComponentThread(id); auto request = std::make_shared( mrntt, target, callback); @@ -241,7 +241,7 @@ void ComponentThread::joltThreadReq(threadLifetimeMgmtOpCbFn callback) } // Thread management method implementations -void ComponentThread::startThreadReq(threadLifetimeMgmtOpCbFn callback) +void MindThread::startThreadReq(threadLifetimeMgmtOpCbFn callback) { std::shared_ptr caller = getSelf(); auto request = std::make_shared( @@ -253,7 +253,7 @@ void ComponentThread::startThreadReq(threadLifetimeMgmtOpCbFn callback) request.get(), request)); } -void ComponentThread::exitThreadReq(threadLifetimeMgmtOpCbFn callback) +void MindThread::exitThreadReq(threadLifetimeMgmtOpCbFn callback) { std::shared_ptr caller = getSelf(); auto request = std::make_shared( @@ -264,13 +264,13 @@ void ComponentThread::exitThreadReq(threadLifetimeMgmtOpCbFn callback) &ThreadLifetimeMgmtOp::exitThreadReq1_mainQueue, request.get(), request)); - this->pause_io_service.post( + pause_io_service.post( std::bind( &ThreadLifetimeMgmtOp::exitThreadReq1_pauseQueue, request.get(), request)); } -void ComponentThread::pauseThreadReq(threadLifetimeMgmtOpCbFn callback) +void MindThread::pauseThreadReq(threadLifetimeMgmtOpCbFn callback) { if (id == ComponentThread::MRNTT) { @@ -288,7 +288,7 @@ void ComponentThread::pauseThreadReq(threadLifetimeMgmtOpCbFn callback) request.get(), request)); } -void ComponentThread::resumeThreadReq(threadLifetimeMgmtOpCbFn callback) +void MindThread::resumeThreadReq(threadLifetimeMgmtOpCbFn callback) { if (id == ComponentThread::MRNTT) { @@ -301,13 +301,13 @@ void ComponentThread::resumeThreadReq(threadLifetimeMgmtOpCbFn callback) auto request = std::make_shared( caller, shared_from_this(), callback); - this->pause_io_service.post( + pause_io_service.post( std::bind( &ThreadLifetimeMgmtOp::resumeThreadReq1, request.get(), request)); } -class ComponentThread::MindShutdownIndOp +class MindThread::MindShutdownIndOp : public TargetedAsynchronousContinuation { public: @@ -391,13 +391,13 @@ void ComponentThread::exceptionInd( + ": invoked on non-mrntt thread " + faultyThread->name); } - auto request = std::make_shared( + auto request = std::make_shared( faultyThread, nullptr); // Post the exception to the mrntt thread. this->getIoService().post( std::bind( - &MindShutdownIndOp::mindShutdownInd1_exception, + &MindThread::MindShutdownIndOp::mindShutdownInd1_exception, request.get(), request)); } @@ -409,13 +409,13 @@ void ComponentThread::userShutdownInd() + ": invoked on non-mrntt thread " + this->name); } - auto request = std::make_shared( + auto request = std::make_shared( ComponentThread::getMrntt(), nullptr); // Post the user shutdown to the mrntt thread. this->getIoService().post( std::bind( - &MindShutdownIndOp::mindShutdownInd1_userShutdown, + &MindThread::MindShutdownIndOp::mindShutdownInd1_userShutdown, request.get(), request)); } @@ -442,7 +442,7 @@ int ComponentThread::getAvailableCpuCount() return cpuCount; } -void ComponentThread::pinToCpu(int cpuId) +void MindThread::pinToCpu(int cpuId) { if (cpuId < 0) { diff --git a/smocore/include/componentThread.h b/smocore/include/componentThread.h index bb9126f..e6157f3 100644 --- a/smocore/include/componentThread.h +++ b/smocore/include/componentThread.h @@ -16,9 +16,10 @@ namespace smo { class Mind; // Forward declaration +class MarionetteThread; +class MindThread; class ComponentThread -: public std::enable_shared_from_this { public: enum ThreadId @@ -32,57 +33,26 @@ public: N_ITEMS }; - ComponentThread(ThreadId _id, Mind& parent) - : id(_id), name(getThreadName(_id)), parent(parent), - work(io_service), pause_work(pause_io_service), - pinnedCpuId(-1), - thread( - ((id == MRNTT) ? marionetteMain : main), - std::ref(*this)) +protected: + ComponentThread(ThreadId _id) + : id(_id), name(getThreadName(_id)), + work(io_service) {} +public: + virtual ~ComponentThread() = default; + void cleanup(void); boost::asio::io_service& getIoService(void) { return io_service; } - void initializeTls(void); static const std::shared_ptr getSelf(void); - - static std::shared_ptr getMrntt(); - Mind& getParent() const { return parent; } + static std::shared_ptr getMrntt(); typedef void (mainFn)(ComponentThread &self); - static mainFn main, marionetteMain; - - typedef std::function threadLifetimeMgmtOpCbFn; - // Thread management methods - void startThreadReq(threadLifetimeMgmtOpCbFn callback); - void exitThreadReq(threadLifetimeMgmtOpCbFn callback); - void pauseThreadReq(threadLifetimeMgmtOpCbFn callback); - void resumeThreadReq(threadLifetimeMgmtOpCbFn callback); - - /** - * JOLTs this thread to begin processing after global initialization. - * - * JOLTing is the mechanism that allows threads to enter their main - * event loops and set up TLS vars after all global constructors have - * completed. This prevents race conditions during system startup. - */ - void joltThreadReq(threadLifetimeMgmtOpCbFn callback); // CPU management methods static int getAvailableCpuCount(); - void pinToCpu(int cpuId); - - enum class ThreadOp - { - START, - PAUSE, - RESUME, - EXIT, - JOLT, - N_ITEMS - }; typedef std::function mindShutdownIndOpCbFn; // Intentionally doesn't take a callback. @@ -93,19 +63,9 @@ public: public: ThreadId id; std::string name; - Mind &parent; boost::asio::io_service io_service; boost::asio::io_service::work work; - boost::asio::io_service pause_io_service; - boost::asio::io_service::work pause_work; std::atomic keepLooping; - int pinnedCpuId; - - - /* Always ensure that this is last so that the thread is spawned after - * everything else is constructed. - */ - std::thread thread; static const std::string getThreadName(ThreadId id) { @@ -127,14 +87,87 @@ public: return threadNames[id]; } +}; -private: +class MarionetteThread +: public std::enable_shared_from_this, + public ComponentThread +{ +public: + MarionetteThread() + : ComponentThread(MRNTT), + thread(main, std::ref(*this)) + { + } + + static void main(MarionetteThread& self); + void initializeTls(void); + +public: + std::thread thread; +}; + +class MindThread +: public std::enable_shared_from_this, public ComponentThread +{ +public: + enum class ThreadOp + { + START, + PAUSE, + RESUME, + EXIT, + JOLT, + N_ITEMS + }; + + MindThread(ThreadId _id, Mind& parent) + : ComponentThread(_id), + pinnedCpuId(-1), + pause_work(pause_io_service), + parent(parent), + thread(main, std::ref(*this)) + { + } + + static void main(MindThread& self); + void initializeTls(void); + + Mind& getParent() const { return parent; } + + // Thread management methods + typedef std::function threadLifetimeMgmtOpCbFn; + void startThreadReq(threadLifetimeMgmtOpCbFn callback); + void exitThreadReq(threadLifetimeMgmtOpCbFn callback); + void pauseThreadReq(threadLifetimeMgmtOpCbFn callback); + void resumeThreadReq(threadLifetimeMgmtOpCbFn callback); + + /** + * JOLTs this thread to begin processing after global initialization. + * + * JOLTing is the mechanism that allows threads to enter their main + * event loops and set up TLS vars after all global constructors have + * completed. This prevents race conditions during system startup. + */ + void joltThreadReq(threadLifetimeMgmtOpCbFn callback); + + // CPU management methods + void pinToCpu(int cpuId); + +public: + int pinnedCpuId; + boost::asio::io_service pause_io_service; + boost::asio::io_service::work pause_work; + Mind& parent; + std::thread thread; + +public: class ThreadLifetimeMgmtOp; class MindShutdownIndOp; }; namespace mrntt { -extern std::shared_ptr mrntt; +extern std::shared_ptr mrntt; } } // namespace smo diff --git a/smocore/include/marionette/marionette.h b/smocore/include/marionette/marionette.h index 33acdbd..74c4a16 100644 --- a/smocore/include/marionette/marionette.h +++ b/smocore/include/marionette/marionette.h @@ -3,16 +3,17 @@ #include #include +#include +#include namespace smo { + +class MarionetteThread; + namespace mrntt { extern std::atomic exitCode; -class Marionette -{ -}; - void exitMarionetteLoop(); } // namespace mrntt diff --git a/smocore/include/mind.h b/smocore/include/mind.h index 7999fd6..a597b8a 100644 --- a/smocore/include/mind.h +++ b/smocore/include/mind.h @@ -27,12 +27,12 @@ public: void finalizeBodyReq(mindLifetimeMgmtOpCbFn callback); // ComponentThread access methods - std::shared_ptr getComponentThread( + std::shared_ptr getComponentThread( ComponentThread::ThreadId id) const; - std::shared_ptr getComponentThread( + std::shared_ptr getComponentThread( const std::string& name) const; // Get all this Mind's component threads. - std::vector> getMindThreads() const; + std::vector> getMindThreads() const; // Thread management methods (moved from ComponentThread) typedef std::function mindThreadLifetimeMgmtOpCbFn; @@ -76,7 +76,7 @@ private: bool threadsHaveBeenJolted = false, bodyComponentInitialized = false; // Collection of ComponentThread instances (excluding marionette) - std::vector> componentThreads; + std::vector> componentThreads; class MindLifetimeMgmtOp; class MindThreadLifetimeMgmtOp; diff --git a/smocore/marionette/marionette.cpp b/smocore/marionette/marionette.cpp index 8f8345b..449b487 100644 --- a/smocore/marionette/marionette.cpp +++ b/smocore/marionette/marionette.cpp @@ -13,13 +13,6 @@ namespace smo { -// Global Mind instance -std::shared_ptr globalMind = std::make_shared(); - -// Global marionette thread instance -std::shared_ptr mrntt::mrntt = - std::make_shared(ComponentThread::MRNTT, *globalMind); - CrtCommandLineArgs crtCommandLineArgs(0, nullptr, nullptr); void CrtCommandLineArgs::set(int argc, char *argv[], char *envp[]) @@ -27,8 +20,13 @@ 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(); void exitMarionetteLoop() { @@ -39,7 +37,7 @@ void exitMarionetteLoop() } // namespace mrntt -void ComponentThread::marionetteMain(ComponentThread& self) +void MarionetteThread::main(MarionetteThread& self) { // Wait for CRT's main() to post us the command line args. std::cout << __func__ << ": Waiting for command line JOLT" << std::endl; @@ -111,6 +109,8 @@ void ComponentThread::marionetteMain(ComponentThread& self) * 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) { diff --git a/smocore/mind.cpp b/smocore/mind.cpp index 60617ea..4661150 100644 --- a/smocore/mind.cpp +++ b/smocore/mind.cpp @@ -10,20 +10,25 @@ namespace smo { Mind::Mind(void) : componentThreads{ - 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::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::shared_ptr +std::shared_ptr Mind::getComponentThread(ComponentThread::ThreadId id) const { - // Access the global marionette thread using ComponentThread::getMrntt() - if (id == ComponentThread::MRNTT) { return ComponentThread::getMrntt(); } + if (id == ComponentThread::MRNTT) + { + throw std::runtime_error( + std::string(__func__) + + ": MRNTT is not a MindThread and cannot be returned by " + "getComponentThread"); + } // Search through the vector for the thread with matching id for (auto& thread : componentThreads) { @@ -32,14 +37,20 @@ Mind::getComponentThread(ComponentThread::ThreadId id) const // Throw exception if no thread found throw std::runtime_error(std::string(__func__) + - ": No ComponentThread found with ID " + ": No MindThread found with ID " + std::to_string(static_cast(id))); } -std::shared_ptr +std::shared_ptr Mind::getComponentThread(const std::string& name) const { - if (name == "mrntt") { return ComponentThread::getMrntt(); } + if (name == "mrntt") + { + throw std::runtime_error( + std::string(__func__) + + ": MRNTT is not a MindThread and cannot be returned by " + "getComponentThread"); + } for (auto& thread : componentThreads) { if (thread->name == name) { return thread; } @@ -47,10 +58,10 @@ Mind::getComponentThread(const std::string& name) const // Throw exception if no thread found throw std::runtime_error(std::string(__func__) + - ": No ComponentThread found with name '" + name + "'"); + ": No MindThread found with name '" + name + "'"); } -std::vector> +std::vector> Mind::getMindThreads() const { return componentThreads;