Add Mrntt component; init globalMind in mrntt.initializeReq
This makes the initialization sequence much cleaner and conceptually well encapsulated. We also now dynamically allocate the Mind objects. They're allocated dynamically by Mrntt inside of initializeReq. This means that we no longer have to worry about jolting and cleaning up the running threads of global mind object even when we never explicitly called Mind.initializeReq. Along with other conceptual improvements to our abstractions, this patch also gets us to a real "end of program initialization" point for the first time.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
#include <iostream>
|
||||
#include <asynchronousContinuation.h>
|
||||
#include <asynchronousLoop.h>
|
||||
#include <component.h>
|
||||
#include <componentThread.h>
|
||||
#include <mindManager/mindManager.h>
|
||||
#include <marionette/marionette.h>
|
||||
|
||||
namespace smo {
|
||||
namespace mrntt {
|
||||
|
||||
class MarionetteComponent::MrnttLifetimeMgmtOp
|
||||
: public AsynchronousContinuation<mrnttLifetimeMgmtOpCbFn>,
|
||||
public ContinuationTarget
|
||||
{
|
||||
public:
|
||||
MrnttLifetimeMgmtOp(
|
||||
MarionetteComponent &parent, const std::shared_ptr<ComponentThread> &caller,
|
||||
mrnttLifetimeMgmtOpCbFn callback)
|
||||
: AsynchronousContinuation<mrnttLifetimeMgmtOpCbFn>(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<MrnttLifetimeMgmtOp> 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<Mind>();
|
||||
smo::mind::globalMind->initializeReq(
|
||||
std::bind(
|
||||
&MrnttLifetimeMgmtOp::initializeReq2,
|
||||
this, context, std::placeholders::_1));
|
||||
}
|
||||
|
||||
void initializeReq2(
|
||||
std::shared_ptr<MrnttLifetimeMgmtOp> 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<MrnttLifetimeMgmtOp> 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<MrnttLifetimeMgmtOp> 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<MrnttLifetimeMgmtOp>(
|
||||
*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<MrnttLifetimeMgmtOp>(
|
||||
*this, mrntt, callback);
|
||||
|
||||
mrntt->getIoService().post(
|
||||
std::bind(
|
||||
&MrnttLifetimeMgmtOp::finalizeReq1_posted,
|
||||
request.get(), request));
|
||||
}
|
||||
|
||||
} // namespace mrntt
|
||||
} // namespace smo
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <typeinfo>
|
||||
#include <boost/asio/signal_set.hpp>
|
||||
#include <asynchronousBridge.h>
|
||||
#include <mind.h>
|
||||
#include <mindManager/mindManager.h>
|
||||
#include <componentThread.h>
|
||||
#include <marionette/marionette.h>
|
||||
#include <salmanoff.h>
|
||||
@@ -20,21 +20,46 @@ void CrtCommandLineArgs::set(int argc, char *argv[], char *envp[])
|
||||
crtCommandLineArgs = CrtCommandLineArgs(argc, argv, envp);
|
||||
}
|
||||
|
||||
// Global Mind instance
|
||||
std::shared_ptr<Mind> globalMind;
|
||||
|
||||
namespace mrntt {
|
||||
std::atomic<int> exitCode;
|
||||
// Global marionette thread instance
|
||||
std::shared_ptr<MarionetteThread> mrntt = std::make_shared<MarionetteThread>();
|
||||
std::shared_ptr<MarionetteThread> thread =
|
||||
std::make_shared<MarionetteThread>();
|
||||
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<Mind>();
|
||||
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();
|
||||
@@ -1,7 +1,7 @@
|
||||
#include <iostream>
|
||||
#include <mindManager/mindManager.h>
|
||||
#include <deviceManager/deviceManager.h>
|
||||
#include <senseApis/senseApiManager.h>
|
||||
#include <asynchronousContinuation.h>
|
||||
#include <salmanoff.h>
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user