#include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace smo { Mind::Mind(void) : sscl::PuppetApplication( std::vector>{ std::make_shared( SmoThreadId::DIRECTOR, sscl::PuppetComponent::defaultPuppetMain, director, &MindComponent::preJoltHook), std::make_shared( SmoThreadId::SIMULATOR, sscl::PuppetComponent::defaultPuppetMain, canvas, &MindComponent::preJoltHook), std::make_shared( SmoThreadId::SUBCONSCIOUS, sscl::PuppetComponent::defaultPuppetMain, subconscious, &MindComponent::preJoltHook), std::make_shared( SmoThreadId::BODY, sscl::PuppetComponent::defaultPuppetMain, body, &MindComponent::preJoltHook) #ifndef CONFIG_WORLD_USE_BODY_THREAD , std::make_shared( SmoThreadId::WORLD, sscl::PuppetComponent::defaultPuppetMain, world, &MindComponent::preJoltHook) #endif } ), director(*this, componentThreads[SmoThreadId::DIRECTOR - 1]), canvas(*this, componentThreads[SmoThreadId::SIMULATOR - 1]), subconscious(*this, componentThreads[SmoThreadId::SUBCONSCIOUS - 1]), body(*this, componentThreads[SmoThreadId::BODY - 1]), world(*this, componentThreads[SmoThreadId::WORLD - 1]) { } std::shared_ptr Mind::getComponentThread(sscl::ThreadId id) const { if (id == SmoThreadId::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) { if (thread->id == id) { return std::static_pointer_cast(thread); } } // Throw exception if no thread found throw std::runtime_error(std::string(__func__) + ": No MindThread found with ID " + std::to_string(static_cast(id))); } std::shared_ptr Mind::getComponentThread(const std::string& name) const { 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 std::static_pointer_cast(thread); } } // Throw exception if no thread found throw std::runtime_error(std::string(__func__) + ": No MindThread found with name '" + name + "'"); } std::vector> Mind::getMindThreads() const { std::vector> mindThreads; mindThreads.reserve(componentThreads.size()); for (auto& thread : componentThreads) { mindThreads.push_back(std::static_pointer_cast(thread)); } return mindThreads; } class Mind::MindLifetimeMgmtOp : public sscl::PostedAsynchronousContinuation { public: MindLifetimeMgmtOp( Mind &parent, const std::shared_ptr &caller, sscl::Callback callback) : sscl::PostedAsynchronousContinuation( caller, callback), parent(parent) {} public: Mind &parent; public: void initializeReq1_posted( [[maybe_unused]] std::shared_ptr context ) { /* Jolt the threads, then start them */ parent.joltAllPuppetThreadsReq( {context, std::bind( &MindLifetimeMgmtOp::initializeReq2, context.get(), context)}); } void initializeReq2( [[maybe_unused]] std::shared_ptr context ) { std::cout << "Mrntt: All mind threads JOLTed." << "\n"; parent.startAllPuppetThreadsReq( {context, std::bind( &MindLifetimeMgmtOp::initializeReq3, context.get(), context)}); } void initializeReq3( [[maybe_unused]] std::shared_ptr context ) { std::cout << "Mrntt: All mind threads started." << "\n"; parent.body.initializeReq( {context, std::bind( &MindLifetimeMgmtOp::initializeReq4, context.get(), context, std::placeholders::_1)}); } void initializeReq4( [[maybe_unused]] std::shared_ptr context, bool success ) { std::cout << "Mrntt: Body component initialized." << "\n"; callOriginalCb(success); } void finalizeReq1_posted( [[maybe_unused]] std::shared_ptr context ) { parent.body.finalizeReq( {context, std::bind( &MindLifetimeMgmtOp::finalizeReq2, context.get(), context, std::placeholders::_1)}); } void finalizeReq2( [[maybe_unused]] std::shared_ptr context, bool success ) { if (!success) { std::cerr << "Mrntt: Body component failed to finalize." << "\n"; } else { std::cout << "Mrntt: Body component finalized." << "\n"; } /* If the threads haven't been jolted, we need to do that first, because * otherwise they'll just enter their main loops and wait for control * messages from mrntt after processing the exit request. */ parent.joltAllPuppetThreadsReq( {context, std::bind( &MindLifetimeMgmtOp::finalizeReq3, context.get(), context)}); } void finalizeReq3( [[maybe_unused]] std::shared_ptr context ) { std::cout << "Mrntt: All mind threads JOLTed for finalization." << "\n"; parent.exitAllPuppetThreadsReq( {context, std::bind( &MindLifetimeMgmtOp::finalizeReq4, context.get(), context)}); } void finalizeReq4( [[maybe_unused]] std::shared_ptr context ) { std::cout << "Mrntt: All mind threads exited." << "\n"; callOriginalCb(true); } }; void Mind::initializeReq(sscl::Callback callback) { /* Distribute threads across available CPUs */ try { distributeAndPinThreadsAcrossCpus(); } catch (const std::exception& e) { std::cerr << "Salmanoff couldn't distribute the mind threads across " "the CPUs, so performance may be suboptimal.\n" "Error: " << e.what() << "\n"; } const auto& caller = sscl::ComponentThread::getSelf(); auto request = std::make_shared( *this, caller, callback); mrntt::mrntt.thread->getIoService().post( STC(std::bind( &MindLifetimeMgmtOp::initializeReq1_posted, request.get(), request))); } void Mind::finalizeReq(sscl::Callback callback) { const auto& caller = sscl::ComponentThread::getSelf(); auto request = std::make_shared( *this, caller, callback); mrntt::mrntt.thread->getIoService().post( STC(std::bind( &MindLifetimeMgmtOp::finalizeReq1_posted, request.get(), request))); } } // namespace smo