CompThreads: create execOpOnAllMindThreads common helper

This allows us to execute an op on all mind threads without having
to repeatedly write loops. We've implemented wrappers to handle
start, pause, resume, exit and JOLT sequences.
This commit is contained in:
2025-08-03 08:22:45 -04:00
parent 6f6fa77498
commit 1deb92a416
3 changed files with 192 additions and 51 deletions
+155 -35
View File
@@ -214,11 +214,152 @@ void ComponentThread::resumeThreadReq(std::function<void()> callback)
});
}
static int threadsKilledCount;
void ComponentThread::joltThreadReq(std::function<void()> callback)
{
this->getIoService().post([this, caller = getSelf(), callback]()
{
std::cout << "Thread '" << name << "': handling JOLT request." << "\n";
// Stop the main io_service to jolt the thread
io_service.stop();
if (callback) {
caller->getIoService().post(callback);
}
});
}
struct AllMindThreadsOpReqContext {
AllMindThreadsOpReqContext() : nThreadsProcessed(0) {}
int nThreadsProcessed;
};
static const std::string getOpName(ComponentThread::ThreadOp op)
{
if (op < (ComponentThread::ThreadOp)0
|| op > ComponentThread::ThreadOp::JOLT)
{
throw std::runtime_error(std::string(__func__)
+ ": Invalid operation");
}
switch (op)
{
case ComponentThread::ThreadOp::START: return "starting";
case ComponentThread::ThreadOp::PAUSE: return "pausing";
case ComponentThread::ThreadOp::RESUME: return "resuming";
case ComponentThread::ThreadOp::EXIT: return "exiting";
case ComponentThread::ThreadOp::JOLT: return "jolting";
default: return "unknown";
}
}
void ComponentThread::execOpOnAllMindThreadsReq(
ThreadOp op, std::function<void()> callback
)
{
std::shared_ptr<ComponentThread> self = getSelf();
// Check that we're being called from the marionette thread
if (self->id != MRNTT)
{
throw std::runtime_error(std::string(__func__)
+ ": invoked on non-mrntt thread " + self->name);
}
std::cout << "Mrntt: " << getOpName(op) << " all mind threads." << "\n";
auto context = std::make_shared<AllMindThreadsOpReqContext>();
const int N_THREADS_EXCEPT_MRNTT = ComponentThread::N_ITEMS - 1;
for (auto &currThread : ComponentThread::componentThreads)
{
if (currThread->id == ComponentThread::MRNTT)
{ continue; }
auto threadCallback = [context, callback, N_THREADS_EXCEPT_MRNTT, op]()
{
++context->nThreadsProcessed;
if (context->nThreadsProcessed < N_THREADS_EXCEPT_MRNTT)
{ return; }
if (op == ThreadOp::EXIT)
{
// Special cleanup for exit operations
for (auto &currThreadJ : ComponentThread::componentThreads)
{
if (currThreadJ->id == ComponentThread::MRNTT)
{ continue; }
currThreadJ->thread.join();
}
}
std::cout << "Mrntt: all mind threads done " << getOpName(op) << "."
<< "\n";
if (callback) { callback(); }
};
switch (op) {
case ThreadOp::START:
currThread->startThreadReq(threadCallback);
break;
case ThreadOp::PAUSE:
currThread->pauseThreadReq(threadCallback);
break;
case ThreadOp::RESUME:
currThread->resumeThreadReq(threadCallback);
break;
case ThreadOp::EXIT:
currThread->exitThreadReq(threadCallback);
break;
case ThreadOp::JOLT:
currThread->joltThreadReq(threadCallback);
break;
default:
throw std::runtime_error("Invalid operation");
}
}
}
void ComponentThread::startAllMindThreadsReq(std::function<void()> callback)
{
execOpOnAllMindThreadsReq(ThreadOp::START, callback);
}
void ComponentThread::pauseAllMindThreadsReq(std::function<void()> callback)
{
execOpOnAllMindThreadsReq(ThreadOp::PAUSE, callback);
}
void ComponentThread::resumeAllMindThreadsReq(std::function<void()> callback)
{
execOpOnAllMindThreadsReq(ThreadOp::RESUME, callback);
}
void ComponentThread::exitAllMindThreadsReq(std::function<void()> callback)
{
execOpOnAllMindThreadsReq(ThreadOp::EXIT, callback);
}
void ComponentThread::joltAllMindThreadsReq(std::function<void()> callback)
{
execOpOnAllMindThreadsReq(ThreadOp::JOLT, callback);
}
/* This shouldn't take a callback because the caller shouldn't expect to
* Mrntt to send a reply signal to it. Sending this Indication means that
* Mrntt will send the calling thread an exitThreadReq. When the caller
* processes that exitThreadReq(), the caller will exit its event loop and then
* terminate.
*
* Even if Mrntt sent a RDY response, the caller shouldn't actually be executing
* any longer to receive it anyway.
*/
void ComponentThread::exceptionInd(ComponentThread& thread)
{
if (this->id != MRNTT)
if (this->id != ComponentThread::MRNTT)
{
throw std::runtime_error(std::string(__func__)
+ ": invoked on non-mrntt thread " + thread.name);
@@ -226,40 +367,19 @@ void ComponentThread::exceptionInd(ComponentThread& thread)
// Post the exception to the mrntt thread.
this->getIoService().post(
[&thread]()
[&thread]()
{
std::cerr << "Mrntt: Exception occurred: in thread "
<< thread.name << ". Killing Salmanoff." << "\n";
ComponentThread::exitAllMindThreadsReq(
[]()
{
std::cerr << "Mrntt: Exception occurred: in thread "
<< thread.name << ". Killing Salmanoff." << "\n";
threadsKilledCount = 0;
for (auto &currThread : ComponentThread::componentThreads)
{
if (currThread->id == MRNTT)
{ continue; }
currThread->exitThreadReq(
[]()
{
++threadsKilledCount;
if (threadsKilledCount < ComponentThread::N_ITEMS - 1)
{ return; }
for (auto &currThreadJ
: ComponentThread::componentThreads)
{
if (currThreadJ->id == MRNTT)
{ continue; }
currThreadJ->thread.join();
}
mrntt::mrntt->keepLooping = false;
mrntt::mrntt->getIoService().stop();
}
);
}
}
);
mrntt::mrntt->keepLooping = false;
mrntt::mrntt->getIoService().stop();
std::cout << "Mrntt: Signaled main loop to exit." << "\n";
});
});
}
} // namespace smo