Files
salmanoff/smocore/senseApis/senseApiManager.cpp
T
hayodea 07609c6d6c SenseApiLib:Add isBeingDestroyed atomic flag for getter bailout
Since we have no choice but to access the sh_ptr<SenseApiLib> for
a lib before we can get its Qutex, we use this flag to ensure that
we can know whether the SenseApiLib data structure and its Qutex
are still valid when we enter -- i.e, we ensure that the SenseApiLib
object wasn't destroyed under our feet.
2025-09-30 21:01:00 -04:00

277 lines
7.9 KiB
C++

#include <iostream>
#include <stdexcept>
#include <optional>
#include <filesystem>
#include <senseApis/senseApiManager.h>
#include <senseApis/senseApiLib.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>
namespace fs = std::filesystem;
namespace smo {
namespace sense_api {
/**
* @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 sense 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();
}
/* Hooks to be provided to senseApiLibs, enabling them to call into Salmanoff
* code.
*/
static SmoCallbacks smoCallbacks =
{
.searchForLibInSmoSearchPaths = searchForLibInSmoSearchPaths,
.ComponentThread_getSelf = ComponentThread_getSelf
};
/* Static file-scope threading model object for senseApi libraries */
static SmoThreadingModelDesc smoThreadingModelDesc = {
.componentThread = nullptr
};
std::optional<std::string> SenseApiManager::searchForLibInSmoSearchPaths(
const std::string& libraryPath)
{
return ::smo::sense_api::searchForLibInSmoSearchPaths(libraryPath);
}
SenseApiLib& SenseApiManager::loadSenseApiLib(
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, SenseApiLib::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_SENSE_API_DESC_FN_TYPEDEF *>(
dlsym(dlopen_handle.get(), SMO_GET_SENSE_API_DESC_FN_NAME_STR));
if (!func)
{
throw std::runtime_error(
std::string(__func__) + ": dlsym('"
SMO_GET_SENSE_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 SenseApiDesc &libApiDesc = func(
smoCallbacks, smoThreadingModelDesc);
auto lib = std::make_shared<SenseApiLib>(
libraryPath, dlopen_handle.release(), func);
lib->setSenseApiDesc(libApiDesc);
senseApiLibs.push_back(lib);
return *senseApiLibs.back();
}
std::optional<std::shared_ptr<SenseApiLib>>
SenseApiManager::getSenseApiLib(const std::string& libraryPath)
{
auto it = std::find_if(senseApiLibs.begin(), senseApiLibs.end(),
[&libPath = libraryPath](const std::shared_ptr<SenseApiLib>& lib) {
return lib->libraryPath == libPath;
}
);
if (it != senseApiLibs.end()) { return *it; }
return std::nullopt;
}
std::optional<std::shared_ptr<SenseApiLib>>
SenseApiManager::getSenseApiLibByApiName(const std::string& apiName)
{
auto it = std::find_if(senseApiLibs.begin(), senseApiLibs.end(),
[&apiName](const std::shared_ptr<SenseApiLib>& lib) {
return lib->senseApiDesc.name == apiName;
}
);
if (it != senseApiLibs.end()) { return *it; }
return std::nullopt;
}
void SenseApiManager::unloadSenseApiLib(const std::string& libraryPath)
{
auto it = std::find_if(senseApiLibs.begin(), senseApiLibs.end(),
[&lpath = libraryPath](const std::shared_ptr<SenseApiLib>& lib) {
return lib->libraryPath == lpath;
}
);
if (it != senseApiLibs.end())
{
senseApiLibs.erase(it);
return;
}
std::cerr << std::string(__func__) + ": Library not found: "
<< libraryPath << '\n';
}
void SenseApiManager::unloadAllSenseApiLibs(void)
{
senseApiLibs.clear();
}
void SenseApiManager::loadAllSenseApiLibsFromOptions(
const std::shared_ptr<ComponentThread>& componentThread
)
{
const auto& options = OptionParser::getOptions();
for (const auto& libPath : options.senseApiLibs) {
loadSenseApiLib(libPath, componentThread);
}
}
std::string SenseApiManager::stringifyLibs() const
{
std::string result;
for (const auto& lib : senseApiLibs) {
result += lib->stringify() + "\n";
}
return result;
}
void SenseApiManager::initializeSenseApiLib(SenseApiLib& lib)
{
/** FIXME:
* When we eventually make this method async, this method should acquire
* the SenseApiManager's main CRUD qutex.
*/
if (!lib.senseApiDesc.sal_mgmt_libOps.initializeInd)
{
throw std::runtime_error(
std::string(__func__) + ": initializeInd() is NULL for library '"
+ lib.libraryPath + "'");
}
lib.senseApiDesc.sal_mgmt_libOps.initializeInd();
}
void SenseApiManager::finalizeSenseApiLib(SenseApiLib& lib)
{
/** FIXME:
* When we eventually make this method async, this flag should only be set
* after acquiring the SenseApiManager's main CRUD qutex.
*/
lib.isBeingDestroyed.store(true);
if (!lib.senseApiDesc.sal_mgmt_libOps.finalizeInd)
{
throw std::runtime_error(
std::string(__func__) + ": finalizeInd() is NULL for library '"
+ lib.libraryPath + "'");
}
lib.senseApiDesc.sal_mgmt_libOps.finalizeInd();
}
void SenseApiManager::initializeAllSenseApiLibs(void)
{
for (auto& lib : senseApiLibs) {
initializeSenseApiLib(*lib);
}
}
void SenseApiManager::finalizeAllSenseApiLibs(void)
{
for (auto& lib : senseApiLibs) {
finalizeSenseApiLib(*lib);
}
}
} // namespace sense_api
} // namespace smo