spinscale: Move thread init/jolt/exit logic into PuppetApplication
This commit is contained in:
@@ -3,6 +3,7 @@ add_library(spinscale SHARED
|
||||
src/lockerAndInvokerBase.cpp
|
||||
src/componentThread.cpp
|
||||
src/component.cpp
|
||||
src/puppetApplication.cpp
|
||||
)
|
||||
|
||||
# Conditionally add qutexAcquisitionHistoryTracker.cpp only when debug locks
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
#ifndef PUPPET_APPLICATION_H
|
||||
#define PUPPET_APPLICATION_H
|
||||
|
||||
#include <config.h>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <spinscale/callback.h>
|
||||
#include <spinscale/componentThread.h>
|
||||
|
||||
namespace smo {
|
||||
|
||||
class PuppetApplication
|
||||
: public std::enable_shared_from_this<PuppetApplication>
|
||||
{
|
||||
public:
|
||||
PuppetApplication(
|
||||
const std::vector<std::shared_ptr<PuppetThread>> &threads);
|
||||
~PuppetApplication() = default;
|
||||
|
||||
// Thread management methods
|
||||
typedef std::function<void()> puppetThreadLifetimeMgmtOpCbFn;
|
||||
void joltAllPuppetThreadsReq(
|
||||
Callback<puppetThreadLifetimeMgmtOpCbFn> callback);
|
||||
void startAllPuppetThreadsReq(
|
||||
Callback<puppetThreadLifetimeMgmtOpCbFn> callback);
|
||||
void pauseAllPuppetThreadsReq(
|
||||
Callback<puppetThreadLifetimeMgmtOpCbFn> callback);
|
||||
void resumeAllPuppetThreadsReq(
|
||||
Callback<puppetThreadLifetimeMgmtOpCbFn> callback);
|
||||
void exitAllPuppetThreadsReq(
|
||||
Callback<puppetThreadLifetimeMgmtOpCbFn> callback);
|
||||
|
||||
protected:
|
||||
// Collection of PuppetThread instances
|
||||
std::vector<std::shared_ptr<PuppetThread>> componentThreads;
|
||||
|
||||
/**
|
||||
* Indicates whether all puppet threads have been JOLTed at least once.
|
||||
*
|
||||
* JOLTing serves two critical purposes:
|
||||
*
|
||||
* 1. **Global Constructor Sequencing**: Since pthreads begin executing while
|
||||
* global constructors are still being executed, globally defined pthreads
|
||||
* cannot depend on global objects having been constructed. JOLTing is done
|
||||
* by the CRT's main thread within main(), which provides a sequencing
|
||||
* guarantee that global constructors have been called.
|
||||
*
|
||||
* 2. **shared_from_this Safety**: shared_from_this() requires a prior
|
||||
* shared_ptr handle to be established. The global list of
|
||||
* shared_ptr<ComponentThread> guarantees that at least one shared_ptr to
|
||||
* each ComponentThread has been initialized before JOLTing occurs.
|
||||
*
|
||||
* This flag ensures that JOLTing happens exactly once and provides
|
||||
* a synchronization point for the entire system initialization.
|
||||
*/
|
||||
bool threadsHaveBeenJolted = false;
|
||||
|
||||
private:
|
||||
class PuppetThreadLifetimeMgmtOp;
|
||||
};
|
||||
|
||||
} // namespace smo
|
||||
|
||||
#endif // PUPPET_APPLICATION_H
|
||||
@@ -0,0 +1,204 @@
|
||||
#include <iostream>
|
||||
#include <spinscale/asynchronousContinuation.h>
|
||||
#include <spinscale/asynchronousLoop.h>
|
||||
#include <spinscale/callback.h>
|
||||
#include <spinscale/puppetApplication.h>
|
||||
#include <spinscale/componentThread.h>
|
||||
|
||||
namespace smo {
|
||||
|
||||
PuppetApplication::PuppetApplication(
|
||||
const std::vector<std::shared_ptr<PuppetThread>> &threads)
|
||||
: componentThreads(threads)
|
||||
{
|
||||
}
|
||||
|
||||
class PuppetApplication::PuppetThreadLifetimeMgmtOp
|
||||
: public NonPostedAsynchronousContinuation<puppetThreadLifetimeMgmtOpCbFn>
|
||||
{
|
||||
public:
|
||||
PuppetThreadLifetimeMgmtOp(
|
||||
PuppetApplication &parent, unsigned int nThreads,
|
||||
Callback<puppetThreadLifetimeMgmtOpCbFn> callback)
|
||||
: NonPostedAsynchronousContinuation<puppetThreadLifetimeMgmtOpCbFn>(callback),
|
||||
loop(nThreads),
|
||||
parent(parent)
|
||||
{}
|
||||
|
||||
public:
|
||||
AsynchronousLoop loop;
|
||||
PuppetApplication &parent;
|
||||
|
||||
public:
|
||||
void joltAllPuppetThreadsReq1(
|
||||
[[maybe_unused]] std::shared_ptr<PuppetThreadLifetimeMgmtOp> context
|
||||
)
|
||||
{
|
||||
loop.incrementSuccessOrFailureDueTo(true);
|
||||
if (!loop.isComplete()) {
|
||||
return;
|
||||
}
|
||||
|
||||
parent.threadsHaveBeenJolted = true;
|
||||
callOriginalCb();
|
||||
}
|
||||
|
||||
void executeGenericOpOnAllPuppetThreadsReq1(
|
||||
[[maybe_unused]] std::shared_ptr<PuppetThreadLifetimeMgmtOp> context
|
||||
)
|
||||
{
|
||||
loop.incrementSuccessOrFailureDueTo(true);
|
||||
if (!loop.isComplete()) {
|
||||
return;
|
||||
}
|
||||
|
||||
callOriginalCb();
|
||||
}
|
||||
|
||||
void exitAllPuppetThreadsReq1(
|
||||
[[maybe_unused]] std::shared_ptr<PuppetThreadLifetimeMgmtOp> context
|
||||
)
|
||||
{
|
||||
loop.incrementSuccessOrFailureDueTo(true);
|
||||
if (!loop.isComplete()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto& thread : parent.componentThreads) {
|
||||
thread->thread.join();
|
||||
}
|
||||
|
||||
callOriginalCb();
|
||||
}
|
||||
};
|
||||
|
||||
void PuppetApplication::joltAllPuppetThreadsReq(
|
||||
Callback<puppetThreadLifetimeMgmtOpCbFn> callback
|
||||
)
|
||||
{
|
||||
if (threadsHaveBeenJolted)
|
||||
{
|
||||
std::cout << "Mrntt: All puppet threads already JOLTed. "
|
||||
<< "Skipping JOLT request." << "\n";
|
||||
callback.callbackFn();
|
||||
return;
|
||||
}
|
||||
|
||||
// If no threads, set flag and call callback immediately
|
||||
if (componentThreads.size() == 0 && callback.callbackFn)
|
||||
{
|
||||
threadsHaveBeenJolted = true;
|
||||
callback.callbackFn();
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a counter to track when all threads have been jolted
|
||||
auto request = std::make_shared<PuppetThreadLifetimeMgmtOp>(
|
||||
*this, componentThreads.size(), callback);
|
||||
|
||||
for (auto& thread : componentThreads)
|
||||
{
|
||||
thread->joltThreadReq(
|
||||
{request, std::bind(
|
||||
&PuppetThreadLifetimeMgmtOp::joltAllPuppetThreadsReq1,
|
||||
request.get(), request)});
|
||||
}
|
||||
}
|
||||
|
||||
void PuppetApplication::startAllPuppetThreadsReq(
|
||||
Callback<puppetThreadLifetimeMgmtOpCbFn> callback
|
||||
)
|
||||
{
|
||||
// If no threads, call callback immediately
|
||||
if (componentThreads.size() == 0 && callback.callbackFn)
|
||||
{
|
||||
callback.callbackFn();
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a counter to track when all threads have started
|
||||
auto request = std::make_shared<PuppetThreadLifetimeMgmtOp>(
|
||||
*this, componentThreads.size(), callback);
|
||||
|
||||
for (auto& thread : componentThreads)
|
||||
{
|
||||
thread->startThreadReq(
|
||||
{request, std::bind(
|
||||
&PuppetThreadLifetimeMgmtOp::executeGenericOpOnAllPuppetThreadsReq1,
|
||||
request.get(), request)});
|
||||
}
|
||||
}
|
||||
|
||||
void PuppetApplication::pauseAllPuppetThreadsReq(
|
||||
Callback<puppetThreadLifetimeMgmtOpCbFn> callback
|
||||
)
|
||||
{
|
||||
// If no threads, call callback immediately
|
||||
if (componentThreads.size() == 0 && callback.callbackFn)
|
||||
{
|
||||
callback.callbackFn();
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a counter to track when all threads have paused
|
||||
auto request = std::make_shared<PuppetThreadLifetimeMgmtOp>(
|
||||
*this, componentThreads.size(), callback);
|
||||
|
||||
for (auto& thread : componentThreads)
|
||||
{
|
||||
thread->pauseThreadReq(
|
||||
{request, std::bind(
|
||||
&PuppetThreadLifetimeMgmtOp::executeGenericOpOnAllPuppetThreadsReq1,
|
||||
request.get(), request)});
|
||||
}
|
||||
}
|
||||
|
||||
void PuppetApplication::resumeAllPuppetThreadsReq(
|
||||
Callback<puppetThreadLifetimeMgmtOpCbFn> callback
|
||||
)
|
||||
{
|
||||
// If no threads, call callback immediately
|
||||
if (componentThreads.size() == 0 && callback.callbackFn)
|
||||
{
|
||||
callback.callbackFn();
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a counter to track when all threads have resumed
|
||||
auto request = std::make_shared<PuppetThreadLifetimeMgmtOp>(
|
||||
*this, componentThreads.size(), callback);
|
||||
|
||||
for (auto& thread : componentThreads)
|
||||
{
|
||||
thread->resumeThreadReq(
|
||||
{request, std::bind(
|
||||
&PuppetThreadLifetimeMgmtOp::executeGenericOpOnAllPuppetThreadsReq1,
|
||||
request.get(), request)});
|
||||
}
|
||||
}
|
||||
|
||||
void PuppetApplication::exitAllPuppetThreadsReq(
|
||||
Callback<puppetThreadLifetimeMgmtOpCbFn> callback
|
||||
)
|
||||
{
|
||||
// If no threads, call callback immediately
|
||||
if (componentThreads.size() == 0 && callback.callbackFn)
|
||||
{
|
||||
callback.callbackFn();
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a counter to track when all threads have exited
|
||||
auto request = std::make_shared<PuppetThreadLifetimeMgmtOp>(
|
||||
*this, componentThreads.size(), callback);
|
||||
|
||||
for (auto& thread : componentThreads)
|
||||
{
|
||||
thread->exitThreadReq(
|
||||
{request, std::bind(
|
||||
&PuppetThreadLifetimeMgmtOp::executeGenericOpOnAllPuppetThreadsReq1,
|
||||
request.get(), request)});
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace smo
|
||||
Reference in New Issue
Block a user