Add LoadableLibraryManager and refactor StimBuffApiManager to use it.

Centralize dlopen/search in LoadableLibraryManager so typed library managers
can share one loaded-shlib registry without duplicating load/unload logic.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-10 21:14:55 -04:00
parent 10234bc422
commit 7eda755c15
7 changed files with 418 additions and 241 deletions
+3
View File
@@ -36,6 +36,9 @@ add_library(smocore STATIC
# SenseApis # SenseApis
stimBuffApis/stimBuffApiManager.cpp stimBuffApis/stimBuffApiManager.cpp
# Loadable shared libraries
loadableLib/loadableLibraryManager.cpp
# ComputeManager # ComputeManager
computeManager/computeManager.cpp computeManager/computeManager.cpp
+9 -6
View File
@@ -11,6 +11,7 @@
#include <deviceManager/deviceManager.h> #include <deviceManager/deviceManager.h>
#include <deviceManager/deviceReattacher.h> #include <deviceManager/deviceReattacher.h>
#include <stimBuffApis/stimBuffApiManager.h> #include <stimBuffApis/stimBuffApiManager.h>
#include <loadableLib/loadableLibraryManager.h>
#include <marionette/marionette.h> #include <marionette/marionette.h>
#include <marionette/marionetteThread.h> #include <marionette/marionetteThread.h>
#include <mind.h> #include <mind.h>
@@ -92,7 +93,7 @@ DeviceManager::attachStimBuffDeviceCReq(
auto &sbam = stim_buff::StimBuffApiManager::getInstance(); auto &sbam = stim_buff::StimBuffApiManager::getInstance();
auto &lib = sbam.getStimBuffApiLibByApiName(spec->stimBuffApi); auto &lib = sbam.getStimBuffApiLibByApiName(spec->stimBuffApi);
if (lib.isBeingDestroyed.load()) if (lib.loadedSharedLibrary->isBeingDestroyed.load())
{ {
throw std::runtime_error( throw std::runtime_error(
std::string(__func__) + ": Library is being destroyed" std::string(__func__) + ": Library is being destroyed"
@@ -103,12 +104,13 @@ DeviceManager::attachStimBuffDeviceCReq(
{ {
throw std::runtime_error( throw std::runtime_error(
std::string(__func__) + ": attachDeviceCReq() is NULL " std::string(__func__) + ": attachDeviceCReq() is NULL "
"for library '" + lib.libraryPath + "'"); "for library '" + lib.loadedSharedLibrary->libraryPath + "'");
} }
/* FIXME Locking here makes no sense. */ /* FIXME Locking here makes no sense. */
sscl::co::CoQutex::ReleaseHandle sbamGuard = sscl::co::CoQutex::ReleaseHandle sbamGuard =
co_await sbam.s.lock.getAcquireInvocationAndSuspensionPolicy(); co_await sbam.s.lock
.getAcquireInvocationAndSuspensionPolicy();
sscl::co::CoQutex::ReleaseHandle libGuard = sscl::co::CoQutex::ReleaseHandle libGuard =
co_await lib.s.lock.getAcquireInvocationAndSuspensionPolicy(); co_await lib.s.lock.getAcquireInvocationAndSuspensionPolicy();
sbamGuard.release(); sbamGuard.release();
@@ -151,7 +153,7 @@ DeviceManager::detachStimBuffDeviceCReq(
auto &sbam = stim_buff::StimBuffApiManager::getInstance(); auto &sbam = stim_buff::StimBuffApiManager::getInstance();
auto &lib = sbam.getStimBuffApiLibByApiName(spec->stimBuffApi); auto &lib = sbam.getStimBuffApiLibByApiName(spec->stimBuffApi);
if (lib.isBeingDestroyed.load()) if (lib.loadedSharedLibrary->isBeingDestroyed.load())
{ {
throw std::runtime_error( throw std::runtime_error(
std::string(__func__) + ": Library is being destroyed" std::string(__func__) + ": Library is being destroyed"
@@ -162,12 +164,13 @@ DeviceManager::detachStimBuffDeviceCReq(
{ {
throw std::runtime_error( throw std::runtime_error(
std::string(__func__) + ": detachDeviceCReq() is NULL " std::string(__func__) + ": detachDeviceCReq() is NULL "
"for library '" + lib.libraryPath + "'"); "for library '" + lib.loadedSharedLibrary->libraryPath + "'");
} }
/* FIXME Locking here makes no sense. */ /* FIXME Locking here makes no sense. */
sscl::co::CoQutex::ReleaseHandle sbamGuard = sscl::co::CoQutex::ReleaseHandle sbamGuard =
co_await sbam.s.lock.getAcquireInvocationAndSuspensionPolicy(); co_await sbam.s.lock
.getAcquireInvocationAndSuspensionPolicy();
sscl::co::CoQutex::ReleaseHandle libGuard = sscl::co::CoQutex::ReleaseHandle libGuard =
co_await lib.s.lock.getAcquireInvocationAndSuspensionPolicy(); co_await lib.s.lock.getAcquireInvocationAndSuspensionPolicy();
sbamGuard.release(); sbamGuard.release();
@@ -0,0 +1,111 @@
#ifndef LOADABLE_LIBRARY_MANAGER_H
#define LOADABLE_LIBRARY_MANAGER_H
#include <dlfcn.h>
#include <atomic>
#include <memory>
#include <optional>
#include <stdexcept>
#include <string>
#include <vector>
#include <spinscale/co/coQutex.h>
#include <spinscale/sharedResourceGroup.h>
namespace smo {
namespace loadable_lib {
class LoadableLibraryManager
{
public:
struct SharedLibraryDeleter
{
void operator()(void* handle) const;
};
struct LoadedSharedLibrary
{
LoadedSharedLibrary(
std::string libraryPathIn,
std::string resolvedPathIn,
std::unique_ptr<void, SharedLibraryDeleter> dlopenHandleIn)
: libraryPath(std::move(libraryPathIn)),
resolvedPath(std::move(resolvedPathIn)),
dlopenHandle(std::move(dlopenHandleIn))
{}
std::string libraryPath;
std::string resolvedPath;
std::unique_ptr<void, SharedLibraryDeleter> dlopenHandle;
std::atomic<bool> isBeingDestroyed{false};
void* getDlopenHandle() const
{
return dlopenHandle.get();
}
};
struct Resources
{
std::vector<std::shared_ptr<LoadedSharedLibrary>> loadedSharedLibraries;
};
static LoadableLibraryManager& getInstance()
{
static LoadableLibraryManager instance;
return instance;
}
std::optional<std::string> searchForLibInSmoSearchPaths(
const std::string& libraryPath) const;
std::shared_ptr<LoadedSharedLibrary> loadSharedLibrary(
const std::string& libraryPath);
void unloadSharedLibrary(
const std::shared_ptr<LoadedSharedLibrary>& loadedLibrary);
template<typename FnPtr>
static FnPtr resolveSymbol(void* handle, const char* symbolName)
{
dlerror();
auto symbol = reinterpret_cast<FnPtr>(dlsym(handle, symbolName));
const char* dlerr = dlerror();
if (dlerr != nullptr)
{
throw std::runtime_error(
std::string("dlsym('") + symbolName + "') failed: " + dlerr);
}
if (symbol == nullptr)
{
throw std::runtime_error(
std::string("dlsym('") + symbolName + "') returned null");
}
return symbol;
}
public:
sscl::SharedResourceGroup<sscl::co::CoQutex, Resources> s;
private:
LoadableLibraryManager()
: s("LoadableLibraryManager")
{}
~LoadableLibraryManager();
LoadableLibraryManager(const LoadableLibraryManager&) = delete;
LoadableLibraryManager& operator=(const LoadableLibraryManager&) = delete;
void appendLoadedSharedLibrary(
const std::shared_ptr<LoadedSharedLibrary>& loadedLibrary);
void removeLoadedSharedLibrary(
const std::shared_ptr<LoadedSharedLibrary>& loadedLibrary);
};
} // namespace loadable_lib
} // namespace smo
#endif // LOADABLE_LIBRARY_MANAGER_H
+13 -22
View File
@@ -3,11 +3,10 @@
#include <string> #include <string>
#include <memory> #include <memory>
#include <atomic>
#include <vector> #include <vector>
#include <dlfcn.h>
#include <functional> #include <functional>
#include <user/senseApiDesc.h> #include <user/senseApiDesc.h>
#include <loadableLib/loadableLibraryManager.h>
#include <spinscale/co/coQutex.h> #include <spinscale/co/coQutex.h>
#include <spinscale/sharedResourceGroup.h> #include <spinscale/sharedResourceGroup.h>
@@ -18,25 +17,15 @@ class StimBuffApiLib
{ {
private: private:
friend class StimBuffApiManager; friend class StimBuffApiManager;
struct DlCloser
{
void operator()(void* handle) const
{
if (handle) {
dlclose(handle);
}
}
};
public: public:
StimBuffApiLib( StimBuffApiLib(
const std::string& path, void *_dlopen_handle, std::shared_ptr<loadable_lib::LoadableLibraryManager::LoadedSharedLibrary>
_loadedSharedLibrary,
SMO_GET_STIM_BUFF_API_DESC_FN_TYPEDEF *descFn) SMO_GET_STIM_BUFF_API_DESC_FN_TYPEDEF *descFn)
: libraryPath(path), : loadedSharedLibrary(std::move(_loadedSharedLibrary)),
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) s("StimBuffApiLib-" + loadedSharedLibrary->libraryPath)
{} {}
void setStimBuffApiDesc(const StimBuffApiDesc &desc) void setStimBuffApiDesc(const StimBuffApiDesc &desc)
@@ -45,16 +34,16 @@ public:
{ {
throw std::runtime_error( throw std::runtime_error(
std::string(__func__) + ": Sanity check failed for stim buff API " std::string(__func__) + ": Sanity check failed for stim buff API "
"descriptor in library '" + libraryPath + "'"); "descriptor in library '"
+ loadedSharedLibrary->libraryPath + "'");
} }
stimBuffApiDesc = desc; stimBuffApiDesc = desc;
} }
public: public:
std::string libraryPath; std::shared_ptr<loadable_lib::LoadableLibraryManager::LoadedSharedLibrary>
std::atomic<bool> isBeingDestroyed; loadedSharedLibrary;
std::unique_ptr<void, DlCloser> dlopen_handle;
/* UNIMPLEMENTED: API-specific cmdline options. These affect this specific /* UNIMPLEMENTED: API-specific cmdline options. These affect this specific
* stim buff api lib's behaviour globally. * stim buff api lib's behaviour globally.
*/ */
@@ -83,8 +72,10 @@ public:
sscl::SharedResourceGroup<sscl::co::CoQutex, StimBuffApiLibResources> s; sscl::SharedResourceGroup<sscl::co::CoQutex, StimBuffApiLibResources> s;
std::string stringify() const { std::string stringify() const
std::string result = "Library Path: " + libraryPath + "\n"; {
std::string result = "Library Path: "
+ loadedSharedLibrary->libraryPath + "\n";
result += "Stim Buff API Descriptor: " + stimBuffApiDesc.stringify() + "\n"; result += "Stim Buff API Descriptor: " + stimBuffApiDesc.stringify() + "\n";
return result; return result;
} }
@@ -21,7 +21,7 @@ class StimBuffApiManager
public: public:
struct Resources struct Resources
{ {
std::vector<std::shared_ptr<StimBuffApiLib>> stimBuffApiLibs; std::vector<std::shared_ptr<StimBuffApiLib>> libs;
}; };
static StimBuffApiManager& getInstance() static StimBuffApiManager& getInstance()
@@ -39,7 +39,7 @@ public:
const std::string& libraryPath, const std::string& libraryPath,
const std::shared_ptr<sscl::ComponentThread>& componentThread); const std::shared_ptr<sscl::ComponentThread>& componentThread);
std::optional<std::shared_ptr<StimBuffApiLib>> getStimBuffApiLib( std::optional<std::shared_ptr<StimBuffApiLib>> findStimBuffApiLibByLibraryPath(
const std::string& libraryPath); const std::string& libraryPath);
std::optional<std::shared_ptr<StimBuffApiLib>> findStimBuffApiLibByApiName( std::optional<std::shared_ptr<StimBuffApiLib>> findStimBuffApiLibByApiName(
const std::string& apiName); const std::string& apiName);
@@ -48,19 +48,21 @@ public:
void loadAllStimBuffApiLibsFromOptions( void loadAllStimBuffApiLibsFromOptions(
const std::shared_ptr<sscl::ComponentThread>& componentThread); const std::shared_ptr<sscl::ComponentThread>& componentThread);
void unloadAllStimBuffApiLibs(void); void unloadAllStimBuffApiLibs(void);
body::BodyViralPostingInvoker<void> initializeStimBuffApiLibCReq( body::BodyViralPostingInvoker<void> initializeStimBuffApiLibCReq(
StimBuffApiLib &lib, bool acquireSbamLock=true); StimBuffApiLib &lib, bool acquireListLock=true);
body::BodyViralPostingInvoker<void> finalizeStimBuffApiLibCReq( body::BodyViralPostingInvoker<void> finalizeStimBuffApiLibCReq(
StimBuffApiLib &lib, bool acquireSbamLock=true); StimBuffApiLib &lib, bool acquireListLock=true);
body::BodyViralPostingInvoker<void> initializeAllStimBuffApiLibsCReq(); body::BodyViralPostingInvoker<void> initializeAllStimBuffApiLibsCReq();
body::BodyViralPostingInvoker<void> finalizeAllStimBuffApiLibsCReq(); body::BodyViralPostingInvoker<void> finalizeAllStimBuffApiLibsCReq();
std::string stringifyLibs() const; std::string stringifyLibs() const;
public:
sscl::SharedResourceGroup<sscl::co::CoQutex, Resources> s;
private: private:
StimBuffApiManager() StimBuffApiManager()
: s("StimBuffApiManager") : s("StimBuffApiManager")
@@ -70,13 +72,6 @@ private:
StimBuffApiManager(const StimBuffApiManager&) = delete; StimBuffApiManager(const StimBuffApiManager&) = delete;
StimBuffApiManager& operator=(const StimBuffApiManager&) = delete; StimBuffApiManager& operator=(const StimBuffApiManager&) = delete;
public:
sscl::SharedResourceGroup<sscl::co::CoQutex, Resources> s;
public:
static std::optional<std::string> searchForLibInSmoSearchPaths(
const std::string& libraryPath);
}; };
} // namespace stim_buff } // namespace stim_buff
@@ -0,0 +1,150 @@
#include <iostream>
#include <stdexcept>
#include <algorithm>
#include <filesystem>
#include <loadableLib/loadableLibraryManager.h>
#include <opts.h>
namespace fs = std::filesystem;
namespace smo {
namespace loadable_lib {
namespace {
std::vector<std::string> buildSmoLibrarySearchPaths()
{
std::vector<std::string> searchPaths = {
fs::current_path().string(),
fs::path("/proc/self/exe").parent_path().string()
};
const auto& options = OptionParser::getOptions();
if (!options.senseApiLibPath.empty())
{
searchPaths.insert(
searchPaths.begin(),
options.senseApiLibPath.begin(),
options.senseApiLibPath.end());
}
return searchPaths;
}
void logLibraryNotInSearchPaths(
const std::string& libraryPath,
const std::vector<std::string>& searchPaths)
{
std::cerr << "searchForLibInSmoSearchPaths: library '"
<< libraryPath << "' isn't in search bespoke search paths: ";
for (const auto& path : searchPaths) {
std::cerr << path << " ";
}
std::cerr << std::endl;
std::cerr << "Trying to load " << libraryPath
<< " from system default search paths\n";
}
std::unique_ptr<void, LoadableLibraryManager::SharedLibraryDeleter>
tryDlopenPath(const std::string& path)
{
dlerror();
void* handle = dlopen(path.c_str(), RTLD_LAZY);
if (!handle) {
return {};
}
return std::unique_ptr<
void, LoadableLibraryManager::SharedLibraryDeleter>(handle);
}
} // namespace
LoadableLibraryManager::~LoadableLibraryManager() = default;
void LoadableLibraryManager::SharedLibraryDeleter::operator()(void* handle) const
{
if (handle) {
dlclose(handle);
}
}
void LoadableLibraryManager::appendLoadedSharedLibrary(
const std::shared_ptr<LoadedSharedLibrary>& loadedLibrary)
{
s.rsrc.loadedSharedLibraries.push_back(loadedLibrary);
}
void LoadableLibraryManager::removeLoadedSharedLibrary(
const std::shared_ptr<LoadedSharedLibrary>& loadedLibrary)
{
auto& loadedLibs = s.rsrc.loadedSharedLibraries;
auto it = std::find(loadedLibs.begin(), loadedLibs.end(), loadedLibrary);
if (it != loadedLibs.end()) {
loadedLibs.erase(it);
}
}
std::optional<std::string> LoadableLibraryManager::searchForLibInSmoSearchPaths(
const std::string& libraryPath) const
{
const std::vector<std::string> searchPaths = buildSmoLibrarySearchPaths();
for (const auto& path : searchPaths)
{
fs::path fullPath = fs::path(path) / libraryPath;
if (fs::exists(fullPath)) {
return fullPath.string();
}
}
logLibraryNotInSearchPaths(libraryPath, searchPaths);
return std::nullopt;
}
std::shared_ptr<LoadableLibraryManager::LoadedSharedLibrary>
LoadableLibraryManager::loadSharedLibrary(const std::string& libraryPath)
{
const std::optional<std::string> fullPath =
searchForLibInSmoSearchPaths(libraryPath);
const std::string resolvedPath = fullPath.value_or(libraryPath);
auto dlopenHandle = tryDlopenPath(resolvedPath);
if (!dlopenHandle && fullPath.has_value()) {
dlopenHandle = tryDlopenPath(libraryPath);
}
if (!dlopenHandle)
{
const char* dlerr = dlerror();
const std::string error = (dlerr
? dlerr
: "Unknown error while opening shlib");
throw std::runtime_error(
std::string("loadSharedLibrary: Cannot load library '")
+ libraryPath + "': " + error);
}
auto loadedLibrary = std::make_shared<LoadedSharedLibrary>(
libraryPath,
resolvedPath,
std::move(dlopenHandle));
appendLoadedSharedLibrary(loadedLibrary);
return loadedLibrary;
}
void LoadableLibraryManager::unloadSharedLibrary(
const std::shared_ptr<LoadedSharedLibrary>& loadedLibrary)
{
if (!loadedLibrary) {
return;
}
removeLoadedSharedLibrary(loadedLibrary);
loadedLibrary->dlopenHandle.reset();
}
} // namespace loadable_lib
} // namespace smo
+97 -173
View File
@@ -1,9 +1,10 @@
#include <iostream> #include <iostream>
#include <stdexcept> #include <stdexcept>
#include <optional> #include <optional>
#include <filesystem> #include <algorithm>
#include <stimBuffApis/stimBuffApiManager.h> #include <stimBuffApis/stimBuffApiManager.h>
#include <stimBuffApis/stimBuffApiLib.h> #include <stimBuffApis/stimBuffApiLib.h>
#include <loadableLib/loadableLibraryManager.h>
#include <body/bodyThread.h> #include <body/bodyThread.h>
#include <componentThread.h> #include <componentThread.h>
#include <opts.h> #include <opts.h>
@@ -13,9 +14,6 @@
#include <marionette/marionette.h> #include <marionette/marionette.h>
#include <computeManager/computeManager.h> #include <computeManager/computeManager.h>
namespace fs = std::filesystem;
namespace smo { namespace smo {
namespace stim_buff { namespace stim_buff {
@@ -32,109 +30,55 @@ void assertBodyThread()
} }
} }
} // namespace std::shared_ptr<sscl::ComponentThread> ComponentThread_getSelf()
/**
* @brief Searches for a library in predefined locations
* @param libraryPath The name or path of the library to find
* @return Optional containing the full path if found in search paths, nullopt
* if not
*
* Searches for the library in the following locations in order:
* 1. Custom path specified by --sense-api-lib-path option (if provided)
* 2. Current working directory
* 3. Directory containing the executable
*
* If the library is not found in any of these locations, returns nullopt and
* falls back to system default library search paths (LD_LIBRARY_PATH, etc.)
*/
static std::optional<std::string> searchForLibInSmoSearchPaths(
const std::string& libraryPath)
{
std::vector<std::string> searchPaths = {
fs::current_path().string(),
fs::path("/proc/self/exe").parent_path().string()
};
const auto& options = OptionParser::getOptions();
if (!options.senseApiLibPath.empty())
{
// Insert all stim buff API library paths at the beginning of search paths
searchPaths.insert(
searchPaths.begin(),
options.senseApiLibPath.begin(),
options.senseApiLibPath.end());
}
for (const auto& path : searchPaths)
{
fs::path fullPath = fs::path(path) / libraryPath;
if (fs::exists(fullPath))
{
return fullPath.string();
}
}
std::cerr << std::string(__func__) + ": library '"
+ libraryPath + "' isn't in search bespoke search paths: ";
for (const auto& path : searchPaths) {
std::cerr << path << " ";
}
std::cerr << std::endl;
std::cerr << "Trying to load " + libraryPath + " from system default "
"search paths\n";
return std::nullopt;
}
/* Local static function to wrap sscl::ComponentThread::getSelf for SmoCallbacks */
static std::shared_ptr<sscl::ComponentThread> ComponentThread_getSelf()
{ {
return sscl::ComponentThread::getSelf(); return sscl::ComponentThread::getSelf();
} }
/* Local static function to wrap OptionParser::getOptions for SmoCallbacks */ OptionParser& OptionParser_getOptions()
static OptionParser& OptionParser_getOptions()
{ {
return OptionParser::getOptions(); return OptionParser::getOptions();
} }
/* Local static functions to wrap ComputeManager methods for SmoCallbacks */ std::shared_ptr<smo::compute::ClBuffer>
static std::shared_ptr<smo::compute::ClBuffer>
ComputeManager_createUseHostPtrBuffer( ComputeManager_createUseHostPtrBuffer(
void* hostPtr, size_t size, cl_mem_flags flags void* hostPtr, size_t size, cl_mem_flags flags)
)
{ {
return smo::compute::ComputeManager::getInstance().createUseHostPtrBuffer( return smo::compute::ComputeManager::getInstance().createUseHostPtrBuffer(
hostPtr, size, flags); hostPtr, size, flags);
} }
static void ComputeManager_releaseUseHostPtrBuffer( void ComputeManager_releaseUseHostPtrBuffer(
std::shared_ptr<smo::compute::ClBuffer> buffer std::shared_ptr<smo::compute::ClBuffer> buffer)
)
{ {
smo::compute::ComputeManager::getInstance().releaseUseHostPtrBuffer( smo::compute::ComputeManager::getInstance().releaseUseHostPtrBuffer(
buffer); buffer);
} }
static std::shared_ptr<smo::compute::ComputeDevice> ComputeManager_getDevice() std::shared_ptr<smo::compute::ComputeDevice> ComputeManager_getDevice()
{ {
return smo::compute::ComputeManager::getInstance().getDevice(); return smo::compute::ComputeManager::getInstance().getDevice();
} }
static void ComputeManager_releaseDevice( void ComputeManager_releaseDevice(
std::shared_ptr<smo::compute::ComputeDevice> device std::shared_ptr<smo::compute::ComputeDevice> device)
)
{ {
smo::compute::ComputeManager::getInstance().releaseDevice(device); smo::compute::ComputeManager::getInstance().releaseDevice(device);
} }
std::optional<std::string> searchForLibInSmoSearchPathsHook(
const std::string& libraryPath)
{
return loadable_lib::LoadableLibraryManager::getInstance()
.searchForLibInSmoSearchPaths(libraryPath);
}
/* Hooks to be provided to stimBuffApiLibs, enabling them to call into Salmanoff /* Hooks to be provided to stimBuffApiLibs, enabling them to call into Salmanoff
* code. * code.
*/ */
static SmoCallbacks smoCallbacks = SmoCallbacks smoCallbacks =
{ {
.searchForLibInSmoSearchPaths = searchForLibInSmoSearchPaths, .searchForLibInSmoSearchPaths = searchForLibInSmoSearchPathsHook,
.ComponentThread_getSelf = ComponentThread_getSelf, .ComponentThread_getSelf = ComponentThread_getSelf,
.OptionParser_getOptions = OptionParser_getOptions, .OptionParser_getOptions = OptionParser_getOptions,
.ComputeManager_createUseHostPtrBuffer = .ComputeManager_createUseHostPtrBuffer =
@@ -146,84 +90,58 @@ static SmoCallbacks smoCallbacks =
}; };
/* Static file-scope threading model object for senseApi libraries */ /* Static file-scope threading model object for senseApi libraries */
static SmoThreadingModelDesc smoThreadingModelDesc = { SmoThreadingModelDesc smoThreadingModelDesc = {
.componentThread = nullptr .componentThread = nullptr
}; };
std::optional<std::string> StimBuffApiManager::searchForLibInSmoSearchPaths( } // namespace
const std::string& libraryPath)
{
return ::smo::stim_buff::searchForLibInSmoSearchPaths(libraryPath);
}
StimBuffApiLib& StimBuffApiManager::loadStimBuffApiLib( StimBuffApiLib& StimBuffApiManager::loadStimBuffApiLib(
const std::string& libraryPath, const std::string& libraryPath,
const std::shared_ptr<sscl::ComponentThread>& componentThread const std::shared_ptr<sscl::ComponentThread>& componentThread)
)
{ {
std::optional<std::string> fullPath = searchForLibInSmoSearchPaths( loadable_lib::LoadableLibraryManager& llm =
libraryPath); loadable_lib::LoadableLibraryManager::getInstance();
std::string resolvedPath = fullPath.value_or(libraryPath);
// Clear any existing error if (findStimBuffApiLibByLibraryPath(libraryPath))
dlerror();
auto dlopen_handle = std::unique_ptr<void, StimBuffApiLib::DlCloser>(
dlopen(resolvedPath.c_str(), RTLD_LAZY));
if (!dlopen_handle && fullPath.has_value())
{
// Fallback to using the supplied libraryPath
dlerror();
dlopen_handle.reset(dlopen(libraryPath.c_str(), RTLD_LAZY));
}
if (!dlopen_handle)
{
const char *dlerr = dlerror();
std::string error = (dlerr
? dlerr
: "Unknown error while opening shlib");
throw std::runtime_error(
std::string(__func__) + ": Cannot load library '"
+ libraryPath + "': "
+ error);
}
// Initialize getSenseApiDescriptor
auto func = reinterpret_cast<SMO_GET_STIM_BUFF_API_DESC_FN_TYPEDEF *>(
dlsym(dlopen_handle.get(), SMO_GET_STIM_BUFF_API_DESC_FN_NAME_STR));
if (!func)
{ {
throw std::runtime_error( throw std::runtime_error(
std::string(__func__) + ": dlsym('" std::string(__func__) + ": StimBuffApiLib already loaded: "
SMO_GET_STIM_BUFF_API_DESC_FN_NAME_STR "') failed for library '" + libraryPath);
+ libraryPath + "'");
} }
// Check if the static threading model obj is null and initialize if needed std::shared_ptr<loadable_lib::LoadableLibraryManager::LoadedSharedLibrary>
loadedLibrary = llm.loadSharedLibrary(libraryPath);
auto descFn = loadable_lib::LoadableLibraryManager::resolveSymbol<
SMO_GET_STIM_BUFF_API_DESC_FN_TYPEDEF *>(
loadedLibrary->getDlopenHandle(),
SMO_GET_STIM_BUFF_API_DESC_FN_NAME_STR);
if (!smoThreadingModelDesc.componentThread) { if (!smoThreadingModelDesc.componentThread) {
smoThreadingModelDesc.componentThread = componentThread; smoThreadingModelDesc.componentThread = componentThread;
} }
const StimBuffApiDesc &libApiDesc = func( const StimBuffApiDesc& libApiDesc = descFn(
smoCallbacks, smoThreadingModelDesc); smoCallbacks, smoThreadingModelDesc);
auto lib = std::make_shared<StimBuffApiLib>( auto lib = std::make_shared<StimBuffApiLib>(loadedLibrary, descFn);
libraryPath, dlopen_handle.release(), func);
lib->setStimBuffApiDesc(libApiDesc); lib->setStimBuffApiDesc(libApiDesc);
getInstance().s.rsrc.stimBuffApiLibs.push_back(lib); s.rsrc.libs.push_back(lib);
return *getInstance().s.rsrc.stimBuffApiLibs.back();
return *lib;
} }
std::optional<std::shared_ptr<StimBuffApiLib>> std::optional<std::shared_ptr<StimBuffApiLib>>
StimBuffApiManager::getStimBuffApiLib(const std::string& libraryPath) StimBuffApiManager::findStimBuffApiLibByLibraryPath(
const std::string& libraryPath)
{ {
auto &libs = getInstance().s.rsrc.stimBuffApiLibs; auto& libs = s.rsrc.libs;
auto it = std::find_if(libs.begin(), libs.end(), auto it = std::find_if(
[&libPath = libraryPath](const std::shared_ptr<StimBuffApiLib>& lib) { libs.begin(), libs.end(),
return lib->libraryPath == libPath; [&libraryPath](const std::shared_ptr<StimBuffApiLib>& lib) {
} return lib->loadedSharedLibrary->libraryPath == libraryPath;
); });
if (it != libs.end()) { return *it; } if (it != libs.end()) { return *it; }
return std::nullopt; return std::nullopt;
@@ -232,12 +150,12 @@ StimBuffApiManager::getStimBuffApiLib(const std::string& libraryPath)
std::optional<std::shared_ptr<StimBuffApiLib>> std::optional<std::shared_ptr<StimBuffApiLib>>
StimBuffApiManager::findStimBuffApiLibByApiName(const std::string& apiName) StimBuffApiManager::findStimBuffApiLibByApiName(const std::string& apiName)
{ {
auto &libs = getInstance().s.rsrc.stimBuffApiLibs; auto& libs = s.rsrc.libs;
auto it = std::find_if(libs.begin(), libs.end(), auto it = std::find_if(
libs.begin(), libs.end(),
[&apiName](const std::shared_ptr<StimBuffApiLib>& lib) { [&apiName](const std::shared_ptr<StimBuffApiLib>& lib) {
return lib->stimBuffApiDesc.name == apiName; return lib->stimBuffApiDesc.name == apiName;
} });
);
if (it != libs.end()) { return *it; } if (it != libs.end()) { return *it; }
return std::nullopt; return std::nullopt;
@@ -258,31 +176,42 @@ StimBuffApiLib &StimBuffApiManager::getStimBuffApiLibByApiName(
void StimBuffApiManager::unloadStimBuffApiLib(const std::string& libraryPath) void StimBuffApiManager::unloadStimBuffApiLib(const std::string& libraryPath)
{ {
auto &libs = getInstance().s.rsrc.stimBuffApiLibs; auto& libs = s.rsrc.libs;
auto it = std::find_if(libs.begin(), libs.end(), auto it = std::find_if(
[&lpath = libraryPath](const std::shared_ptr<StimBuffApiLib>& lib) { libs.begin(), libs.end(),
return lib->libraryPath == lpath; [&libraryPath](const std::shared_ptr<StimBuffApiLib>& lib) {
} return lib->loadedSharedLibrary->libraryPath == libraryPath;
); });
if (it != libs.end()) if (it == libs.end())
{ {
libs.erase(it); std::cerr << std::string(__func__) + ": Library not found: "
<< libraryPath << '\n';
return; return;
} }
std::cerr << std::string(__func__) + ": Library not found: " std::shared_ptr<loadable_lib::LoadableLibraryManager::LoadedSharedLibrary>
<< libraryPath << '\n'; loadedLibrary = (*it)->loadedSharedLibrary;
libs.erase(it);
loadable_lib::LoadableLibraryManager::getInstance()
.unloadSharedLibrary(loadedLibrary);
} }
void StimBuffApiManager::unloadAllStimBuffApiLibs(void) void StimBuffApiManager::unloadAllStimBuffApiLibs(void)
{ {
getInstance().s.rsrc.stimBuffApiLibs.clear(); auto libs = s.rsrc.libs;
s.rsrc.libs.clear();
for (const auto& lib : libs)
{
loadable_lib::LoadableLibraryManager::getInstance()
.unloadSharedLibrary(lib->loadedSharedLibrary);
}
} }
void StimBuffApiManager::loadAllStimBuffApiLibsFromOptions( void StimBuffApiManager::loadAllStimBuffApiLibsFromOptions(
const std::shared_ptr<sscl::ComponentThread>& componentThread const std::shared_ptr<sscl::ComponentThread>& componentThread)
)
{ {
const auto& options = OptionParser::getOptions(); const auto& options = OptionParser::getOptions();
for (const auto& libPath : options.senseApiLibs) { for (const auto& libPath : options.senseApiLibs) {
@@ -293,7 +222,7 @@ void StimBuffApiManager::loadAllStimBuffApiLibsFromOptions(
std::string StimBuffApiManager::stringifyLibs() const std::string StimBuffApiManager::stringifyLibs() const
{ {
std::string result; std::string result;
for (const auto& lib : getInstance().s.rsrc.stimBuffApiLibs) { for (const auto& lib : s.rsrc.libs) {
result += lib->stringify() + "\n"; result += lib->stringify() + "\n";
} }
return result; return result;
@@ -301,23 +230,22 @@ std::string StimBuffApiManager::stringifyLibs() const
body::BodyViralPostingInvoker<void> body::BodyViralPostingInvoker<void>
StimBuffApiManager::initializeStimBuffApiLibCReq( StimBuffApiManager::initializeStimBuffApiLibCReq(
StimBuffApiLib &lib, bool acquireSbamLock) StimBuffApiLib& lib, bool acquireListLock)
{ {
assertBodyThread(); assertBodyThread();
StimBuffApiManager &sbam = getInstance(); std::optional<sscl::co::CoQutex::ReleaseHandle> listGuard;
std::optional<sscl::co::CoQutex::ReleaseHandle> sbamGuard; if (acquireListLock)
if (acquireSbamLock)
{ {
sbamGuard.emplace( listGuard.emplace(
co_await sbam.s.lock.getAcquireInvocationAndSuspensionPolicy()); co_await s.lock.getAcquireInvocationAndSuspensionPolicy());
} }
if (!lib.stimBuffApiDesc.sal_mgmt_libOps.initializeCInd) if (!lib.stimBuffApiDesc.sal_mgmt_libOps.initializeCInd)
{ {
throw std::runtime_error( throw std::runtime_error(
std::string(__func__) + ": initializeCInd() is NULL for library '" std::string(__func__) + ": initializeCInd() is NULL for library '"
+ lib.libraryPath + "'"); + lib.loadedSharedLibrary->libraryPath + "'");
} }
sscl::co::CoQutex::ReleaseHandle libGuard = sscl::co::CoQutex::ReleaseHandle libGuard =
@@ -330,29 +258,28 @@ StimBuffApiManager::initializeStimBuffApiLibCReq(
body::BodyViralPostingInvoker<void> body::BodyViralPostingInvoker<void>
StimBuffApiManager::finalizeStimBuffApiLibCReq( StimBuffApiManager::finalizeStimBuffApiLibCReq(
StimBuffApiLib &lib, bool acquireSbamLock) StimBuffApiLib& lib, bool acquireListLock)
{ {
assertBodyThread(); assertBodyThread();
StimBuffApiManager &sbam = getInstance(); std::optional<sscl::co::CoQutex::ReleaseHandle> listGuard;
std::optional<sscl::co::CoQutex::ReleaseHandle> sbamGuard; if (acquireListLock)
if (acquireSbamLock)
{ {
sbamGuard.emplace( listGuard.emplace(
co_await sbam.s.lock.getAcquireInvocationAndSuspensionPolicy()); co_await s.lock.getAcquireInvocationAndSuspensionPolicy());
} }
if (!lib.stimBuffApiDesc.sal_mgmt_libOps.finalizeCInd) if (!lib.stimBuffApiDesc.sal_mgmt_libOps.finalizeCInd)
{ {
throw std::runtime_error( throw std::runtime_error(
std::string(__func__) + ": finalizeCInd() is NULL for library '" std::string(__func__) + ": finalizeCInd() is NULL for library '"
+ lib.libraryPath + "'"); + lib.loadedSharedLibrary->libraryPath + "'");
} }
sscl::co::CoQutex::ReleaseHandle libGuard = sscl::co::CoQutex::ReleaseHandle libGuard =
co_await lib.s.lock.getAcquireInvocationAndSuspensionPolicy(); co_await lib.s.lock.getAcquireInvocationAndSuspensionPolicy();
lib.isBeingDestroyed.store(true); lib.loadedSharedLibrary->isBeingDestroyed.store(true);
co_await lib.stimBuffApiDesc.sal_mgmt_libOps.finalizeCInd(); co_await lib.stimBuffApiDesc.sal_mgmt_libOps.finalizeCInd();
co_return; co_return;
@@ -363,11 +290,10 @@ StimBuffApiManager::initializeAllStimBuffApiLibsCReq()
{ {
assertBodyThread(); assertBodyThread();
StimBuffApiManager &sbam = getInstance(); sscl::co::CoQutex::ReleaseHandle listGuard =
sscl::co::CoQutex::ReleaseHandle sbamGuard = co_await s.lock.getAcquireInvocationAndSuspensionPolicy();
co_await sbam.s.lock.getAcquireInvocationAndSuspensionPolicy();
for (auto &lib : sbam.s.rsrc.stimBuffApiLibs) { for (auto& lib : s.rsrc.libs) {
co_await initializeStimBuffApiLibCReq(*lib, false); co_await initializeStimBuffApiLibCReq(*lib, false);
} }
@@ -379,17 +305,15 @@ StimBuffApiManager::finalizeAllStimBuffApiLibsCReq()
{ {
assertBodyThread(); assertBodyThread();
StimBuffApiManager &sbam = getInstance(); sscl::co::CoQutex::ReleaseHandle listGuard =
sscl::co::CoQutex::ReleaseHandle sbamGuard = co_await s.lock.getAcquireInvocationAndSuspensionPolicy();
co_await sbam.s.lock.getAcquireInvocationAndSuspensionPolicy();
for (auto &lib : sbam.s.rsrc.stimBuffApiLibs) { for (auto& lib : s.rsrc.libs) {
co_await finalizeStimBuffApiLibCReq(*lib, false); co_await finalizeStimBuffApiLibCReq(*lib, false);
} }
co_return; co_return;
} }
} // namespace stim_buff } // namespace stim_buff
} // namespace smo } // namespace smo