#include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace smo { CrtCommandLineArgs crtCommandLineArgs(0, nullptr, nullptr); void CrtCommandLineArgs::set(int argc, char *argv[], char *envp[]) { crtCommandLineArgs = CrtCommandLineArgs(argc, argv, envp); } namespace mrntt { std::atomic exitCode; // Global marionette thread instance std::shared_ptr thread = std::make_shared(); MarionetteComponent mrntt(thread); void exitMarionetteLoop() { 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'; // Fallthrough. } 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({nullptr, std::bind( &mrntt::marionetteFinalizeReqCb, std::placeholders::_1)}); } } // namespace mrntt void MarionetteThread::main(MarionetteThread& self) { std::string threadName = "smo:" + self.name; pthread_setname_np(pthread_self(), threadName.c_str()); // Wait for CRT's main() to post us the command line args. std::cout << __func__ << ": Waiting for command line JOLT" << std::endl; self.getIoService().run(); self.initializeTls(); mrntt::exitCode = EXIT_SUCCESS; static boost::asio::signal_set signals(self.getIoService(), SIGINT); bool callShutdownSalmanoff = false; try { // Register SIGINT (Ctrl+C) and SIGSEGV handlers signals.async_wait([](const boost::system::error_code& ec, int signal) { if (ec) { return; } switch (signal) { case SIGINT: std::cerr << "SIGINT (Ctrl+C) received. Initiating " "shutdown..." << '\n'; break; case SIGSEGV: std::cerr << "FATAL: Segmentation fault detected. " "Initiating shutdown..." << '\n'; break; default: break; } mrntt::mrntt.finalizeReq({nullptr, std::bind( &mrntt::marionetteFinalizeReqCb, std::placeholders::_1)}); } ); OptionParser &options = OptionParser::getOptions(); std::cout << __func__ << ": " << PACKAGE_NAME << " " << PACKAGE_VERSION << std::endl; options.parseArguments( crtCommandLineArgs.argc, crtCommandLineArgs.argv, crtCommandLineArgs.envp); std::cout << __func__ << ": " << options.stringifyOptions() << std::endl; if (options.printUsage) { 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; // Create new Mind instance just before initializeReq mrntt::mrntt.initializeReq({nullptr, std::bind( &mrntt::marionetteInitializeReqCb, std::placeholders::_1)}); 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 * need to post messages to our own event loop to initiate our own * orderly exit. So we loop here to re-enter the event loop, both * to receive the ACK messages from the mind threads, and to post * messages to our own event loop to initiate our own orderly exit. */ for (self.keepLooping = true; self.keepLooping;) { bool sendExceptionInd = false; try { /** EXPLANATION: * This reset() call is crucial for async bridging patterns * to work. * When the outermost thread's io_service is stop()ped (e.g., * from JOLT sequence), it won't process any new work until * reset() is called, even if nested async operations try to * post work to it. This means async bridges invoked from * the outermost thread main sequence won't work until this * reset() call. */ self.getIoService().reset(); self.getIoService().run(); } catch (const std::exception& e) { sendExceptionInd = true; std::cerr << self.name << ":" << __func__ << ": Exception occurred: " << e.what() << "\n"; } catch (...) { sendExceptionInd = true; std::cerr << self.name << ":" << __func__ << ": Unknown exception occurred" << "\n"; } if (sendExceptionInd) { mrntt::exitCode = EXIT_FAILURE; mrntt::mrntt.exceptionInd(); } } std::cout << __func__ << ": Exited event loop" << "\n"; } catch (const OptionParser::Exception& e) { std::ostream *out = &std::cout; std::string outUsageMsg; if (typeid(e) == typeid(OptionsParserError)) { mrntt::exitCode = EXIT_FAILURE; out = &std::cerr; outUsageMsg = std::string(__func__) + ": "; } *out << outUsageMsg << e.what() << std::endl; } catch (const std::exception& e) { std::cerr << __func__ << ": Exception occurred: " << e.what() << std::endl; mrntt::exitCode = EXIT_FAILURE; } catch (...) { std::cerr << __func__ << ": Unknown exception occurred" << std::endl; mrntt::exitCode = EXIT_FAILURE; } if (callShutdownSalmanoff) { shutdownSalmanoff(); } } } // namespace smo