Libspinscale: Initial top-level SMO port to coroutine framework

We haven't ported everything. Just the top-level methods. We'll
dig in to the leaf stuff later. Surprisingly, this all went without
any real difficulties.

Runs like a charm on first try.
This commit is contained in:
2026-05-24 16:12:29 -04:00
parent c539e6e924
commit cde2737876
44 changed files with 1296 additions and 1530 deletions
+4 -10
View File
@@ -4,8 +4,7 @@
#include <spinscale/puppetApplication.h>
#include <spinscale/component.h>
#include <mindComponent.h>
#include <functional>
#include <spinscale/callback.h>
#include <body/bodyThread.h>
namespace smo {
@@ -20,16 +19,11 @@ public:
Body(Mind &parent, const std::shared_ptr<sscl::PuppetThread> &thread);
~Body() = default;
typedef std::function<void(bool)> bodyLifetimeMgmtOpCbFn;
void initializeReq(sscl::Callback<bodyLifetimeMgmtOpCbFn> callback);
void finalizeReq(sscl::Callback<bodyLifetimeMgmtOpCbFn> callback);
private:
class InitializeReq;
class FinalizeReq;
BodyViralPostingInvoker<bool> initializeCReq();
BodyViralPostingInvoker<bool> finalizeCReq();
};
} // namespace body
} // namespace smo
#endif // _BODY_COMPONENT_H
#endif // _BODY_COMPONENT_H
+30
View File
@@ -0,0 +1,30 @@
#ifndef SMO_BODY_THREAD_H
#define SMO_BODY_THREAD_H
#include <boost/asio/io_service.hpp>
#include <spinscale/co/invokers.h>
#include <spinscale/co/postingPromise.h>
namespace smo {
namespace body {
struct BodyThreadTag
{
static boost::asio::io_service &io_service();
};
template <typename T>
using BodyPostingPromise =
sscl::co::TaggedPostingPromise<T, BodyThreadTag>;
using BodyNonViralPostingInvoker =
sscl::co::NonViralPostingInvoker<BodyPostingPromise>;
template <typename T>
using BodyViralPostingInvoker =
sscl::co::ViralPostingInvoker<BodyPostingPromise, T>;
} // namespace body
} // namespace smo
#endif // SMO_BODY_THREAD_H
+15 -14
View File
@@ -7,7 +7,7 @@
#include <sstream>
#include <user/deviceAttachmentSpec.h>
#include <deviceManager/deviceRole.h>
#include <spinscale/qutex.h>
#include <spinscale/co/coQutex.h>
namespace smo {
namespace device {
@@ -15,25 +15,26 @@ namespace device {
class Device
{
public:
Device(const std::string& identifier)
: deviceIdentifier(identifier), qutex("Device-" + identifier)
explicit Device(const std::string& identifier)
: deviceIdentifier(identifier),
qutex("Device-" + identifier)
{}
std::string stringify() const
{
std::ostringstream os;
os << "Device Identifier: " << deviceIdentifier
<< ", Device Roles: " << deviceRoles.size() << std::endl;
for (const auto& deviceRole : deviceRoles) {
os << " " << deviceRole->deviceAttachmentSpec->stringify();
}
return os.str();
}
std::string stringify() const
{
std::ostringstream os;
os << "Device Identifier: " << deviceIdentifier
<< ", Device Roles: " << deviceRoles.size() << std::endl;
for (const auto& deviceRole : deviceRoles) {
os << " " << deviceRole->deviceAttachmentSpec->stringify();
}
return os.str();
}
public:
std::string deviceIdentifier;
std::vector<std::shared_ptr<DeviceRole>> deviceRoles;
sscl::Qutex qutex;
sscl::co::CoQutex qutex;
};
} // namespace device
+57 -68
View File
@@ -7,15 +7,16 @@
#include <opts.h>
#include <utility>
#include <iostream>
#include <functional>
#include <user/senseApiDesc.h>
#include <user/deviceAttachmentSpec.h>
#include <spinscale/asynchronousLoop.h>
#include <deviceManager/device.h>
#include <deviceManager/deviceRole.h>
#include <deviceManager/deviceReattacher.h>
#include <spinscale/callback.h>
#include <spinscale/qutex.h>
#include <cpsBoundary/stimBuffDeviceAReq.h>
#include <marionette/marionetteThread.h>
#include <spinscale/co/coQutex.h>
#include <spinscale/multiOperationResultSet.h>
#include <spinscale/sharedResourceGroup.h>
namespace smo {
namespace device {
@@ -25,11 +26,18 @@ class DeviceReattacher;
class DeviceManager
{
public:
static DeviceManager& getInstance()
{
static DeviceManager instance;
return instance;
}
struct DeviceAttachmentIndResult
{
bool success = false;
std::shared_ptr<DeviceRole> deviceRole;
std::shared_ptr<DeviceAttachmentSpec> deviceSpec;
};
static DeviceManager& getInstance()
{
static DeviceManager instance;
return instance;
}
void initialize(void)
{};
@@ -39,80 +47,61 @@ public:
void initializeDeviceReattacher();
void finalizeDeviceReattacher();
std::string readDapSpecFile(const std::string& filename);
void collateAllDapSpecs(void);
void parseAllDapSpecs(void);
std::string readDapSpecFile(const std::string& filename);
void collateAllDapSpecs(void);
void parseAllDapSpecs(void);
static const std::string stringifyDeviceSpecs(void);
static const std::string stringifyDeviceSpecs(void);
typedef std::function<void(
bool success, std::shared_ptr<DeviceRole> deviceRole,
std::shared_ptr<DeviceAttachmentSpec> deviceSpec)>
newDeviceAttachmentSpecIndCbFn;
typedef std::function<void(
bool success, std::shared_ptr<DeviceAttachmentSpec> deviceSpec)>
removeDeviceAttachmentSpecReqCbFn;
mrntt::MrnttViralPostingInvoker<DeviceAttachmentIndResult>
newDeviceAttachmentSpecIndCReq(const DeviceAttachmentSpec &spec);
void newDeviceAttachmentSpecInd(
const DeviceAttachmentSpec &spec,
sscl::Callback<newDeviceAttachmentSpecIndCbFn> callback);
void removeDeviceAttachmentSpecReq(
const DeviceAttachmentSpec &spec,
sscl::Callback<removeDeviceAttachmentSpecReqCbFn> callback);
mrntt::MrnttViralPostingInvoker<DeviceAttachmentIndResult>
removeDeviceAttachmentSpecCReq(const DeviceAttachmentSpec &spec);
// Device attachment/detachment methods moved from SenseApiManager
typedef stim_buff::sal_mlo_attachDeviceReqCbFn attachStimBuffDeviceReqCbFn;
typedef stim_buff::sal_mlo_detachDeviceReqCbFn detachStimBuffDeviceReqCbFn;
mrntt::MrnttViralPostingInvoker<cpsBoundary::StimBuffDeviceOpResult>
attachStimBuffDeviceCReq(
const std::shared_ptr<DeviceAttachmentSpec>& spec);
void attachStimBuffDeviceReq(
const std::shared_ptr<DeviceAttachmentSpec>& spec,
sscl::Callback<attachStimBuffDeviceReqCbFn> cb);
void detachStimBuffDeviceReq(
const std::shared_ptr<DeviceAttachmentSpec>& spec,
sscl::Callback<detachStimBuffDeviceReqCbFn> cb);
mrntt::MrnttViralPostingInvoker<cpsBoundary::StimBuffDeviceOpResult>
detachStimBuffDeviceCReq(
const std::shared_ptr<DeviceAttachmentSpec>& spec);
typedef std::function<void(sscl::AsynchronousLoop &results)>
attachAllUnattachedDevicesFromReqCbFn;
typedef std::function<void(sscl::AsynchronousLoop &results)>
detachAllAttachedDeviceRolesCbFn;
mrntt::MrnttViralPostingInvoker<sscl::MultiOperationResultSet>
attachAllUnattachedDevicesFromCReq(
const std::shared_ptr<std::vector<DeviceAttachmentSpec>> &specs);
void attachAllUnattachedDevicesFromReq(
const std::shared_ptr<std::vector<DeviceAttachmentSpec>> &specs,
sscl::Callback<attachAllUnattachedDevicesFromReqCbFn> cb);
void attachAllUnattachedDevicesFromKnownListReq(
sscl::Callback<attachAllUnattachedDevicesFromReqCbFn> cb);
void attachAllUnattachedDevicesFromCmdlineReq(
sscl::Callback<attachAllUnattachedDevicesFromReqCbFn> cb);
void detachAllAttachedDeviceRoles(
sscl::Callback<detachAllAttachedDeviceRolesCbFn> cb);
mrntt::MrnttViralPostingInvoker<sscl::MultiOperationResultSet>
attachAllUnattachedDevicesFromKnownListCReq();
mrntt::MrnttViralPostingInvoker<sscl::MultiOperationResultSet>
attachAllUnattachedDevicesFromCmdlineCReq();
mrntt::MrnttViralPostingInvoker<sscl::MultiOperationResultSet>
detachAllAttachedDeviceRolesCReq();
private:
DeviceManager()
: qutex("DeviceManager"), deviceReattacher(nullptr)
DeviceManager()
: s("DeviceManager")
{}
~DeviceManager();
DeviceManager(const DeviceManager&) = delete;
DeviceManager& operator=(const DeviceManager&) = delete;
~DeviceManager();
DeviceManager(const DeviceManager&) = delete;
DeviceManager& operator=(const DeviceManager&) = delete;
public:
sscl::Qutex qutex;
std::string allDapSpecs;
static std::vector<std::shared_ptr<DeviceAttachmentSpec>>
deviceAttachmentSpecs;
static std::vector<std::shared_ptr<Device>> devices;
static std::vector<std::shared_ptr<DeviceRole>> attachedDeviceRoles;
static std::vector<DeviceAttachmentSpec> commandLineDASpecs;
struct Resources
{
std::vector<std::shared_ptr<DeviceAttachmentSpec>> deviceAttachmentSpecs;
std::vector<std::shared_ptr<Device>> devices;
std::vector<std::shared_ptr<DeviceRole>> attachedDeviceRoles;
std::vector<DeviceAttachmentSpec> commandLineDASpecs;
std::string allDapSpecs;
};
sscl::SharedResourceGroup<sscl::co::CoQutex, Resources> s;
private:
std::unique_ptr<DeviceReattacher> deviceReattacher;
class NewDeviceAttachmentSpecInd;
class RemoveDeviceAttachmentSpecReq;
class AttachStimBuffDeviceReq;
typedef AttachStimBuffDeviceReq DetachStimBuffDeviceReq;
class AttachAllUnattachedDevicesFromReq;
class AttachAllUnattachedDevicesFromKnownListReq;
class DetachAllAttachedDeviceRoles;
};
} // namespace device
@@ -3,8 +3,14 @@
#include <boostAsioLinkageFix.h>
#include <atomic>
#include <chrono>
#include <exception>
#include <functional>
#include <memory>
#include <optional>
#include <boost/asio/deadline_timer.hpp>
#include <marionette/marionetteThread.h>
#include <spinscale/multiOperationResultSet.h>
#include <spinscale/spinLock.h>
namespace smo {
@@ -16,27 +22,35 @@ class DeviceManager;
class DeviceReattacher
{
public:
DeviceReattacher(
DeviceReattacher(
DeviceManager& parent, std::shared_ptr<sscl::ComponentThread> ioThread);
~DeviceReattacher() = default;
~DeviceReattacher() = default;
// Non-copyable
DeviceReattacher(const DeviceReattacher&) = delete;
DeviceReattacher& operator=(const DeviceReattacher&) = delete;
DeviceReattacher(const DeviceReattacher&) = delete;
DeviceReattacher& operator=(const DeviceReattacher&) = delete;
// Control methods
void start();
void stop();
// Control methods
void start();
void stop();
private:
void scheduleNextTimeout();
void onTimeout(const boost::system::error_code& error);
void scheduleNextTimeout();
void onTimeout(const boost::system::error_code& error);
void holdReattachCReq();
DeviceManager &parent;
std::shared_ptr<sscl::ComponentThread> ioThread;
sscl::SpinLock shouldContinueLock;
bool shouldContinue;
boost::asio::deadline_timer timer;
mrntt::MrnttNonViralPostingInvoker reattachKnownListCReq(
std::exception_ptr &exceptionPtr,
std::function<void()> callback);
DeviceManager &parent;
std::shared_ptr<sscl::ComponentThread> ioThread;
sscl::SpinLock shouldContinueLock;
bool shouldContinue;
boost::asio::deadline_timer timer;
std::exception_ptr reattachLifetimeExceptionPtr;
std::optional<mrntt::MrnttNonViralPostingInvoker> reattachCReqInvoker;
bool reattachOpInFlight = false;
std::chrono::steady_clock::time_point lastReattachReqTimestamp{};
};
} // namespace device
+27
View File
@@ -0,0 +1,27 @@
#ifndef SMO_DIRECTOR_THREAD_H
#define SMO_DIRECTOR_THREAD_H
#include <boost/asio/io_service.hpp>
#include <spinscale/co/invokers.h>
#include <spinscale/co/postingPromise.h>
namespace smo {
namespace director {
struct DirectorThreadTag
{
static boost::asio::io_service &io_service();
};
template <typename T>
using DirectorPostingPromise =
sscl::co::TaggedPostingPromise<T, DirectorThreadTag>;
template <typename T>
using DirectorViralPostingInvoker =
sscl::co::ViralPostingInvoker<DirectorPostingPromise, T>;
} // namespace director
} // namespace smo
#endif // SMO_DIRECTOR_THREAD_H
+20 -8
View File
@@ -4,9 +4,12 @@
#include <boost/asio/signal_set.hpp>
#include <cstdint>
#include <atomic>
#include <exception>
#include <functional>
#include <memory>
#include <optional>
#include <spinscale/component.h>
#include <marionette/marionetteThread.h>
namespace sscl {
@@ -26,11 +29,17 @@ public:
{}
~MarionetteComponent() = default;
public:
typedef std::function<void(bool)> mrnttLifetimeMgmtOpCbFn;
void initializeReq(sscl::Callback<mrnttLifetimeMgmtOpCbFn> callback);
void finalizeReq(sscl::Callback<mrnttLifetimeMgmtOpCbFn> callback);
// Intentionally doesn't take a callback.
void holdInitializeCReq(std::function<void()> completion);
void holdFinalizeCReq(std::function<void()> completion);
MrnttNonViralPostingInvoker initializeCReq(
std::exception_ptr &exceptionPtr,
std::function<void()> callback);
MrnttNonViralPostingInvoker finalizeCReq(
std::exception_ptr &exceptionPtr,
std::function<void()> callback);
void exceptionInd();
void handleLoopExceptionHook() override;
@@ -47,11 +56,14 @@ protected:
void handleTryBlock1UnknownException() override;
private:
class MrnttLifetimeMgmtOp;
class TerminationEvent;
std::unique_ptr<boost::asio::signal_set> signals;
bool callShutdownSalmanoff = false;
std::optional<MrnttNonViralPostingInvoker> initializeCReqInvoker;
std::optional<MrnttNonViralPostingInvoker> finalizeCReqInvoker;
public:
std::exception_ptr initializeLifetimeExceptionPtr;
std::exception_ptr finalizeLifetimeExceptionPtr;
};
extern std::shared_ptr<sscl::PuppeteerThread> thread;
@@ -0,0 +1,37 @@
#ifndef SMO_MARIONETTE_THREAD_H
#define SMO_MARIONETTE_THREAD_H
#include <boost/asio/io_service.hpp>
#include <spinscale/co/invokers.h>
#include <spinscale/co/postingPromise.h>
namespace smo {
namespace mrntt {
struct MrnttThreadTag
{
static boost::asio::io_service &io_service() noexcept;
};
template <typename T>
using MrnttPostingPromise =
sscl::co::TaggedPostingPromise<T, MrnttThreadTag>;
using MrnttNonViralPostingInvoker =
sscl::co::NonViralPostingInvoker<MrnttPostingPromise>;
template <typename T>
using MrnttViralPostingInvoker =
sscl::co::ViralPostingInvoker<MrnttPostingPromise, T>;
using MrnttViralNonPostingInvoker =
sscl::co::ViralNonPostingInvoker<void>;
template <typename T>
using MrnttViralNonPostingInvokerT =
sscl::co::ViralNonPostingInvoker<T>;
} // namespace mrntt
} // namespace smo
#endif // SMO_MARIONETTE_THREAD_H
+3 -8
View File
@@ -2,16 +2,15 @@
#define _MIND_H
#include <config.h>
#include <functional>
#include <memory>
#include <string>
#include <spinscale/callback.h>
#include <spinscale/puppetApplication.h>
#include <spinscale/component.h>
#include <componentThread.h>
#include <mindThread.h>
#include <mindComponent.h>
#include <marionette/marionetteThread.h>
#include <director/director.h>
#include <simulator/simulator.h>
#include <body/body.h>
@@ -25,9 +24,8 @@ public:
Mind(void);
~Mind(void) = default;
typedef std::function<void(bool)> mindLifetimeMgmtOpCbFn;
void initializeReq(sscl::Callback<mindLifetimeMgmtOpCbFn> callback);
void finalizeReq(sscl::Callback<mindLifetimeMgmtOpCbFn> callback);
mrntt::MrnttViralNonPostingInvokerT<bool> initializeCReq();
mrntt::MrnttViralNonPostingInvokerT<bool> finalizeCReq();
// ComponentThread access methods
std::shared_ptr<MindThread> getComponentThread(sscl::ThreadId id) const;
@@ -46,9 +44,6 @@ public:
private:
friend class body::Body;
bool bodyComponentInitialized = false;
private:
class MindLifetimeMgmtOp;
};
namespace mind {
@@ -0,0 +1,27 @@
#ifndef SMO_SIMULATOR_THREAD_H
#define SMO_SIMULATOR_THREAD_H
#include <boost/asio/io_service.hpp>
#include <spinscale/co/invokers.h>
#include <spinscale/co/postingPromise.h>
namespace smo {
namespace simulator {
struct SimulatorThreadTag
{
static boost::asio::io_service &io_service();
};
template <typename T>
using SimulatorPostingPromise =
sscl::co::TaggedPostingPromise<T, SimulatorThreadTag>;
template <typename T>
using SimulatorViralPostingInvoker =
sscl::co::ViralPostingInvoker<SimulatorPostingPromise, T>;
} // namespace simulator
} // namespace smo
#endif // SMO_SIMULATOR_THREAD_H
+10 -4
View File
@@ -8,7 +8,8 @@
#include <dlfcn.h>
#include <functional>
#include <user/senseApiDesc.h>
#include <spinscale/qutex.h>
#include <spinscale/co/coQutex.h>
#include <spinscale/sharedResourceGroup.h>
namespace smo {
namespace stim_buff {
@@ -31,10 +32,11 @@ public:
StimBuffApiLib(
const std::string& path, void *_dlopen_handle,
SMO_GET_STIM_BUFF_API_DESC_FN_TYPEDEF *descFn)
: libraryPath(path), qutex("StimBuffApiLib-" + path),
: libraryPath(path),
isBeingDestroyed(false),
dlopen_handle(_dlopen_handle, DlCloser()),
SMO_GET_STIM_BUFF_API_DESC_FN_NAME(descFn)
SMO_GET_STIM_BUFF_API_DESC_FN_NAME(descFn),
s("StimBuffApiLib-" + path)
{}
void setStimBuffApiDesc(const StimBuffApiDesc &desc)
@@ -51,7 +53,6 @@ public:
public:
std::string libraryPath;
sscl::Qutex qutex;
std::atomic<bool> isBeingDestroyed;
std::unique_ptr<void, DlCloser> dlopen_handle;
/* UNIMPLEMENTED: API-specific cmdline options. These affect this specific
@@ -77,6 +78,11 @@ public:
*/
StimBuffApiDesc stimBuffApiDesc;
struct StimBuffApiLibResources
{};
sscl::SharedResourceGroup<sscl::co::CoQutex, StimBuffApiLibResources> s;
std::string stringify() const {
std::string result = "Library Path: " + libraryPath + "\n";
result += "Stim Buff API Descriptor: " + stimBuffApiDesc.stringify() + "\n";
@@ -7,11 +7,10 @@
#include <string>
#include <optional>
#include <functional>
#include <spinscale/asynchronousLoop.h>
#include <stimBuffApis/stimBuffApiLib.h>
#include <user/deviceAttachmentSpec.h>
#include <spinscale/callback.h>
#include <spinscale/qutex.h>
#include <spinscale/co/coQutex.h>
#include <spinscale/sharedResourceGroup.h>
namespace smo {
namespace stim_buff {
@@ -19,6 +18,11 @@ namespace stim_buff {
class StimBuffApiManager
{
public:
struct Resources
{
std::vector<std::shared_ptr<StimBuffApiLib>> stimBuffApiLibs;
};
static StimBuffApiManager& getInstance()
{
static StimBuffApiManager instance;
@@ -54,17 +58,16 @@ public:
private:
StimBuffApiManager()
: qutex("StimBuffApiManager")
: s("StimBuffApiManager")
{}
~StimBuffApiManager() = default;
StimBuffApiManager(const StimBuffApiManager&) = delete;
StimBuffApiManager& operator=(const StimBuffApiManager&) = delete;
std::vector<std::shared_ptr<StimBuffApiLib>> stimBuffApiLibs;
public:
sscl::Qutex qutex;
sscl::SharedResourceGroup<sscl::co::CoQutex, Resources> s;
public:
static std::optional<std::string> searchForLibInSmoSearchPaths(
+25
View File
@@ -0,0 +1,25 @@
#ifndef SMO_SUBCONSCIOUS_THREAD_H
#define SMO_SUBCONSCIOUS_THREAD_H
#include <boost/asio/io_service.hpp>
#include <spinscale/co/invokers.h>
#include <spinscale/co/postingPromise.h>
namespace smo {
struct SubconsciousThreadTag
{
static boost::asio::io_service &io_service();
};
template <typename T>
using SubconsciousPostingPromise =
sscl::co::TaggedPostingPromise<T, SubconsciousThreadTag>;
template <typename T>
using SubconsciousViralPostingInvoker =
sscl::co::ViralPostingInvoker<SubconsciousPostingPromise, T>;
} // namespace smo
#endif // SMO_SUBCONSCIOUS_THREAD_H
+25
View File
@@ -0,0 +1,25 @@
#ifndef SMO_WORLD_THREAD_H
#define SMO_WORLD_THREAD_H
#include <boost/asio/io_service.hpp>
#include <spinscale/co/invokers.h>
#include <spinscale/co/postingPromise.h>
namespace smo {
struct WorldThreadTag
{
static boost::asio::io_service &io_service();
};
template <typename T>
using WorldPostingPromise =
sscl::co::TaggedPostingPromise<T, WorldThreadTag>;
template <typename T>
using WorldViralPostingInvoker =
sscl::co::ViralPostingInvoker<WorldPostingPromise, T>;
} // namespace smo
#endif // SMO_WORLD_THREAD_H