Split CompThread=>MindThr+MrnttThr; alloc globalMind in mrnttMain

We now allocate globalMind locally inside of marionetteMain. Why?

Before now, we had an asymmetric threading situation where the
globalMind's threads were initialized at during global constructor
invocation and not on demand. This meant that we had to shut down
those threads even if we had never got to the point of calling
Mind::initializeReq.

This significantly complicated our shutdown sequence since we had
to factor in the lifetime of the std::thread objects inside of the
ComponentThreads which were inside of the globalMind object.

Now, if we hadn't called Mind::initializeReq, we don't have to
perform any Mind::finalizeReq or adjacent operations. Shutdown is
symmetrically mirrored against the operations we actually performed
during execution.

We introduced some complexity by splitting ComponentThreads into
two derivative types (MindThread and MarionetteThread) but I think
in the long term we'll be able to massage this split into a much
cleaner situation overall.
This commit is contained in:
2025-09-14 11:07:05 -04:00
parent 83af74f4be
commit da0ef64f62
6 changed files with 152 additions and 107 deletions
+85 -52
View File
@@ -16,9 +16,10 @@
namespace smo {
class Mind; // Forward declaration
class MarionetteThread;
class MindThread;
class ComponentThread
: public std::enable_shared_from_this<ComponentThread>
{
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<ComponentThread> getSelf(void);
static std::shared_ptr<ComponentThread> getMrntt();
Mind& getParent() const { return parent; }
static std::shared_ptr<MarionetteThread> getMrntt();
typedef void (mainFn)(ComponentThread &self);
static mainFn main, marionetteMain;
typedef std::function<void()> 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<void()> 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<bool> 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<MarionetteThread>,
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<MindThread>, 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<void()> 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<ComponentThread> mrntt;
extern std::shared_ptr<MarionetteThread> mrntt;
}
} // namespace smo
+5 -4
View File
@@ -3,16 +3,17 @@
#include <cstdint>
#include <atomic>
#include <memory>
#include <componentThread.h>
namespace smo {
class MarionetteThread;
namespace mrntt {
extern std::atomic<int> exitCode;
class Marionette
{
};
void exitMarionetteLoop();
} // namespace mrntt
+4 -4
View File
@@ -27,12 +27,12 @@ public:
void finalizeBodyReq(mindLifetimeMgmtOpCbFn callback);
// ComponentThread access methods
std::shared_ptr<ComponentThread> getComponentThread(
std::shared_ptr<MindThread> getComponentThread(
ComponentThread::ThreadId id) const;
std::shared_ptr<ComponentThread> getComponentThread(
std::shared_ptr<MindThread> getComponentThread(
const std::string& name) const;
// Get all this Mind's component threads.
std::vector<std::shared_ptr<ComponentThread>> getMindThreads() const;
std::vector<std::shared_ptr<MindThread>> getMindThreads() const;
// Thread management methods (moved from ComponentThread)
typedef std::function<void()> mindThreadLifetimeMgmtOpCbFn;
@@ -76,7 +76,7 @@ private:
bool threadsHaveBeenJolted = false,
bodyComponentInitialized = false;
// Collection of ComponentThread instances (excluding marionette)
std::vector<std::shared_ptr<ComponentThread>> componentThreads;
std::vector<std::shared_ptr<MindThread>> componentThreads;
class MindLifetimeMgmtOp;
class MindThreadLifetimeMgmtOp;