Files
salmanoff/smocore/stimBuffApis/stimBuffApiManager.cpp
T
hayodea 27b43c6686 Add ComputeManager; add SmoHooks for getting ClDevices, buffers
We added a new centralized OpenCL Compute manager. This can later
be extended to support CUDA, SyCL, etc. SMO can be configured at
build time to choose which API it will use for compute.

Moreover, the ComputeMgr allows us to register buffers which are
available to all cl_contexts.
2025-11-19 22:34:25 -04:00

321 lines
9.4 KiB
C++

#include <iostream>
#include <stdexcept>
#include <optional>
#include <filesystem>
#include <stimBuffApis/stimBuffApiManager.h>
#include <stimBuffApis/stimBuffApiLib.h>
#include <opts.h>
#include <asynchronousBridge.h>
#include <asynchronousContinuation.h>
#include <asynchronousLoop.h>
#include <callback.h>
#include <user/senseApiDesc.h>
#include <mind.h>
#include <deviceManager/deviceManager.h>
#include <marionette/marionette.h>
#include <computeManager/computeManager.h>
namespace fs = std::filesystem;
namespace smo {
namespace stim_buff {
/**
* @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 ComponentThread::getSelf for SmoCallbacks */
static std::shared_ptr<ComponentThread> ComponentThread_getSelf()
{
return ComponentThread::getSelf();
}
/* Local static function to wrap OptionParser::getOptions for SmoCallbacks */
static OptionParser& OptionParser_getOptions()
{
return OptionParser::getOptions();
}
/* Local static functions to wrap ComputeManager methods for SmoCallbacks */
static std::shared_ptr<smo::compute::ClBuffer>
ComputeManager_createUseHostPtrBuffer(
void* hostPtr, size_t size, cl_mem_flags flags
)
{
return smo::compute::ComputeManager::getInstance().createUseHostPtrBuffer(
hostPtr, size, flags);
}
static void ComputeManager_releaseUseHostPtrBuffer(
std::shared_ptr<smo::compute::ClBuffer> buffer
)
{
smo::compute::ComputeManager::getInstance().releaseUseHostPtrBuffer(
buffer);
}
static std::shared_ptr<smo::compute::ComputeDevice> ComputeManager_getDevice()
{
return smo::compute::ComputeManager::getInstance().getDevice();
}
static void ComputeManager_releaseDevice(
std::shared_ptr<smo::compute::ComputeDevice> device
)
{
smo::compute::ComputeManager::getInstance().releaseDevice(device);
}
/* Hooks to be provided to stimBuffApiLibs, enabling them to call into Salmanoff
* code.
*/
static SmoCallbacks smoCallbacks =
{
.searchForLibInSmoSearchPaths = searchForLibInSmoSearchPaths,
.ComponentThread_getSelf = ComponentThread_getSelf,
.OptionParser_getOptions = OptionParser_getOptions,
.ComputeManager_createUseHostPtrBuffer =
ComputeManager_createUseHostPtrBuffer,
.ComputeManager_releaseUseHostPtrBuffer =
ComputeManager_releaseUseHostPtrBuffer,
.ComputeManager_getDevice = ComputeManager_getDevice,
.ComputeManager_releaseDevice = ComputeManager_releaseDevice
};
/* Static file-scope threading model object for senseApi libraries */
static SmoThreadingModelDesc smoThreadingModelDesc = {
.componentThread = nullptr
};
std::optional<std::string> StimBuffApiManager::searchForLibInSmoSearchPaths(
const std::string& libraryPath)
{
return ::smo::stim_buff::searchForLibInSmoSearchPaths(libraryPath);
}
StimBuffApiLib& StimBuffApiManager::loadStimBuffApiLib(
const std::string& libraryPath,
const std::shared_ptr<ComponentThread>& componentThread
)
{
std::optional<std::string> fullPath = searchForLibInSmoSearchPaths(
libraryPath);
std::string resolvedPath = fullPath.value_or(libraryPath);
// Clear any existing error
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(
std::string(__func__) + ": dlsym('"
SMO_GET_STIM_BUFF_API_DESC_FN_NAME_STR "') failed for library '"
+ libraryPath + "'");
}
// Check if the static threading model obj is null and initialize if needed
if (!smoThreadingModelDesc.componentThread) {
smoThreadingModelDesc.componentThread = componentThread;
}
const StimBuffApiDesc &libApiDesc = func(
smoCallbacks, smoThreadingModelDesc);
auto lib = std::make_shared<StimBuffApiLib>(
libraryPath, dlopen_handle.release(), func);
lib->setStimBuffApiDesc(libApiDesc);
stimBuffApiLibs.push_back(lib);
return *stimBuffApiLibs.back();
}
std::optional<std::shared_ptr<StimBuffApiLib>>
StimBuffApiManager::getStimBuffApiLib(const std::string& libraryPath)
{
auto it = std::find_if(stimBuffApiLibs.begin(), stimBuffApiLibs.end(),
[&libPath = libraryPath](const std::shared_ptr<StimBuffApiLib>& lib) {
return lib->libraryPath == libPath;
}
);
if (it != stimBuffApiLibs.end()) { return *it; }
return std::nullopt;
}
std::optional<std::shared_ptr<StimBuffApiLib>>
StimBuffApiManager::getStimBuffApiLibByApiName(const std::string& apiName)
{
auto it = std::find_if(stimBuffApiLibs.begin(), stimBuffApiLibs.end(),
[&apiName](const std::shared_ptr<StimBuffApiLib>& lib) {
return lib->stimBuffApiDesc.name == apiName;
}
);
if (it != stimBuffApiLibs.end()) { return *it; }
return std::nullopt;
}
void StimBuffApiManager::unloadStimBuffApiLib(const std::string& libraryPath)
{
auto it = std::find_if(stimBuffApiLibs.begin(), stimBuffApiLibs.end(),
[&lpath = libraryPath](const std::shared_ptr<StimBuffApiLib>& lib) {
return lib->libraryPath == lpath;
}
);
if (it != stimBuffApiLibs.end())
{
stimBuffApiLibs.erase(it);
return;
}
std::cerr << std::string(__func__) + ": Library not found: "
<< libraryPath << '\n';
}
void StimBuffApiManager::unloadAllStimBuffApiLibs(void)
{
stimBuffApiLibs.clear();
}
void StimBuffApiManager::loadAllStimBuffApiLibsFromOptions(
const std::shared_ptr<ComponentThread>& componentThread
)
{
const auto& options = OptionParser::getOptions();
for (const auto& libPath : options.senseApiLibs) {
loadStimBuffApiLib(libPath, componentThread);
}
}
std::string StimBuffApiManager::stringifyLibs() const
{
std::string result;
for (const auto& lib : stimBuffApiLibs) {
result += lib->stringify() + "\n";
}
return result;
}
void StimBuffApiManager::initializeStimBuffApiLib(StimBuffApiLib& lib)
{
/** FIXME:
* When we eventually make this method async, this method should acquire
* the StimBuffApiManager's main CRUD qutex.
*/
if (!lib.stimBuffApiDesc.sal_mgmt_libOps.initializeInd)
{
throw std::runtime_error(
std::string(__func__) + ": initializeInd() is NULL for library '"
+ lib.libraryPath + "'");
}
lib.stimBuffApiDesc.sal_mgmt_libOps.initializeInd();
}
void StimBuffApiManager::finalizeStimBuffApiLib(StimBuffApiLib& lib)
{
/** FIXME:
* When we eventually make this method async, this flag should only be set
* after acquiring the StimBuffApiManager's main CRUD qutex.
*/
lib.isBeingDestroyed.store(true);
if (!lib.stimBuffApiDesc.sal_mgmt_libOps.finalizeInd)
{
throw std::runtime_error(
std::string(__func__) + ": finalizeInd() is NULL for library '"
+ lib.libraryPath + "'");
}
lib.stimBuffApiDesc.sal_mgmt_libOps.finalizeInd();
}
void StimBuffApiManager::initializeAllStimBuffApiLibs(void)
{
for (auto& lib : stimBuffApiLibs) {
initializeStimBuffApiLib(*lib);
}
}
void StimBuffApiManager::finalizeAllStimBuffApiLibs(void)
{
for (auto& lib : stimBuffApiLibs) {
finalizeStimBuffApiLib(*lib);
}
}
} // namespace stim_buff
} // namespace smo