26 #include <linux/limits.h>
37 :
ctxt(
ctxt), serviceThread(nullptr) {}
51 std::unique_ptr<Service> &cacheEntry =
serviceCache[make_tuple(&svcType,
id)];
52 if (cacheEntry ==
nullptr) {
55 svc = ServiceRegistry::createService(
this, svcType,
id, implName, details,
59 cacheEntry = std::unique_ptr<Service>(svc);
61 return cacheEntry.get();
74 char result[PATH_MAX];
75 ssize_t count = readlink(
"/proc/self/exe", result, PATH_MAX);
77 throw std::runtime_error(
"Could not get executable path");
78 return std::filesystem::path(std::string(result, count));
80 char buffer[MAX_PATH];
81 DWORD length = GetModuleFileNameA(NULL, buffer, MAX_PATH);
83 throw std::runtime_error(
"Could not get executable path");
84 return std::filesystem::path(std::string(buffer, length));
86 #eror "Unsupported platform"
95 return std::filesystem::path(std::string(dl_info.dli_fname));
97 HMODULE hModule = NULL;
98 if (!GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
99 GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
100 reinterpret_cast<LPCSTR
>(&
getLibPath), &hModule)) {
102 return std::filesystem::path();
105 char buffer[MAX_PATH];
106 DWORD length = GetModuleFileNameA(hModule, buffer, MAX_PATH);
108 throw std::runtime_error(
"Could not get library path");
110 return std::filesystem::path(std::string(buffer, length));
112 #eror "Unsupported platform"
121 backend[0] = toupper(backend[0]);
125 std::string backendFileName =
"lib" + backend +
"Backend.so";
127 std::string backendFileName = backend +
"Backend.dll";
129 #eror "Unsupported platform"
137 std::filesystem::path backendPath = backendFileName;
138 std::string backendPathStr;
139 logger.
debug(
"CONNECT",
140 "trying to load backend plugin: " + backendPath.string());
141 if (!std::filesystem::exists(backendPath)) {
143 backendPath =
getExePath().parent_path().append(backendFileName);
144 logger.
debug(
"CONNECT",
145 "trying to load backend plugin: " + backendPath.string());
146 if (!std::filesystem::exists(backendPath)) {
148 backendPath =
getLibPath().parent_path().append(backendFileName);
149 logger.
debug(
"CONNECT",
150 "trying to load backend plugin: " + backendPath.string());
151 if (!std::filesystem::exists(backendPath)) {
153 backendPathStr = backendFileName;
154 logger.
debug(
"CONNECT",
155 "trying to load backend plugin: " + backendPathStr);
160 if (backendPathStr.empty())
161 backendPathStr = backendPath.string();
170 void *handle = dlopen(backendPathStr.c_str(), RTLD_NOW | RTLD_GLOBAL);
172 std::string error(dlerror());
173 logger.
error(
"CONNECT",
174 "while attempting to load backend plugin: " + error);
175 throw std::runtime_error(
"While attempting to load backend plugin: " +
181 if (backendPath != std::filesystem::path()) {
182 std::filesystem::path backendPathParent = backendPath.parent_path();
183 if (SetDllDirectoryA(backendPathParent.string().c_str()) == 0)
184 throw std::runtime_error(
"While setting DLL directory: " +
185 std::to_string(GetLastError()));
189 HMODULE handle = LoadLibraryA(backendPathStr.c_str());
191 DWORD error = GetLastError();
192 if (error == ERROR_MOD_NOT_FOUND) {
193 logger.
error(
"CONNECT",
"while attempting to load backend plugin: " +
194 backendPathStr +
" not found");
195 throw std::runtime_error(
"While attempting to load backend plugin: " +
196 backendPathStr +
" not found");
198 logger.
error(
"CONNECT",
"while attempting to load backend plugin: " +
199 std::to_string(error));
200 throw std::runtime_error(
"While attempting to load backend plugin: " +
201 std::to_string(error));
204 #eror "Unsupported platform"
206 logger.
info(
"CONNECT",
"loaded backend plugin: " + backendPathStr);
214 static std::map<std::string, BackendCreate> &
get() {
225 if (registry.count(name))
226 throw std::runtime_error(
"Backend already exists in registry");
227 registry[name] = create;
232 const std::string &backend,
233 const std::string &connection) {
235 auto f = registry.find(backend);
236 if (f == registry.end()) {
239 f = registry.find(backend);
240 if (f == registry.end()) {
241 ctxt.getLogger().error(
"CONNECT",
"backend '" + backend +
"' not found");
242 throw std::runtime_error(
"Backend '" + backend +
"' not found");
245 ctxt.getLogger().info(
"CONNECT",
"connecting to backend " + backend +
246 " via '" + connection +
"'");
247 return f->second(
ctxt, connection);
262 addListener(std::initializer_list<ReadChannelPort *> listenPorts,
265 void addTask(std::function<
void(
void)> task) {
266 std::lock_guard<std::mutex> g(
m);
281 std::future<MessageData>>>
295 std::vector<std::function<void(
void)>> taskListCopy;
302 std::this_thread::sleep_for(std::chrono::microseconds(100));
307 std::lock_guard<std::mutex> g(
m);
308 for (
auto &[channel, cbfPair] :
listeners) {
309 assert(channel &&
"Null channel in listener list");
310 std::future<MessageData> &f = cbfPair.second;
311 if (f.wait_for(std::chrono::seconds(0)) == std::future_status::ready) {
312 portUnlockWorkList.emplace_back(channel, cbfPair.first, f.get());
313 f = channel->readAsync();
319 for (
auto [channel, cb, data] : portUnlockWorkList)
320 cb(channel, std::move(data));
323 portUnlockWorkList.clear();
328 std::lock_guard<std::mutex> g(
m);
331 for (
auto &task : taskListCopy)
337 std::initializer_list<ReadChannelPort *> listenPorts,
339 std::lock_guard<std::mutex> g(m);
340 for (
auto port : listenPorts) {
341 if (listeners.count(port))
342 throw std::runtime_error(
"Port already has a listener");
343 listeners[port] = std::make_pair(callback, port->readAsync());
350 : impl(std::make_unique<
Impl>()) {
366 std::initializer_list<ReadChannelPort *> listenPorts,
369 impl->addListener(listenPorts, callback);
374 impl->addTask([&module]() { module.
poll(); });
assert(baseType &&"element must be base type")
virtual void disconnect()
Disconnect from the accelerator cleanly.
ServiceClass * getService(AppIDPath id={}, std::string implName={}, ServiceImplDetails details={}, HWClientDetails clients={})
Get a typed reference to a particular service type.
std::map< ServiceCacheKey, std::unique_ptr< Service > > serviceCache
std::unique_ptr< AcceleratorServiceThread > serviceThread
virtual Service * createService(Service::Type service, AppIDPath idPath, std::string implName, const ServiceImplDetails &details, const HWClientDetails &clients)=0
Called by getServiceImpl exclusively.
std::vector< std::unique_ptr< Accelerator > > ownedAccelerators
List of accelerator objects owned by this connection.
virtual ~AcceleratorConnection()
AcceleratorServiceThread * getServiceThread()
Return a pointer to the accelerator 'service' thread (or threads).
AcceleratorConnection(Context &ctxt)
Accelerator * takeOwnership(std::unique_ptr< Accelerator > accel)
Assume ownership of an accelerator object.
Background thread which services various requests.
void stop()
Instruct the service thread to stop running.
void addListener(std::initializer_list< ReadChannelPort * > listenPorts, std::function< void(ReadChannelPort *, MessageData)> callback)
When there's data on any of the listenPorts, call the callback.
AcceleratorServiceThread()
std::unique_ptr< Impl > impl
void addPoll(HWModule &module)
Poll this module.
~AcceleratorServiceThread()
Top level accelerator class.
AcceleratorConnections, Accelerators, and Manifests must all share a context.
Represents either the top level or an instance of a hardware module.
bool poll()
Master poll method.
virtual void error(const std::string &subsystem, const std::string &msg, const std::map< std::string, std::any > *details=nullptr)
Report an error.
virtual void info(const std::string &subsystem, const std::string &msg, const std::map< std::string, std::any > *details=nullptr)
Report an informational message.
void debug(const std::string &subsystem, const std::string &msg, const std::map< std::string, std::any > *details=nullptr)
Report a debug message.
A logical chunk of data representing serialized data.
A ChannelPort which reads data from the accelerator.
static std::map< std::string, BackendCreate > & get()
std::map< std::string, BackendCreate > backendRegistry
Parent class of all APIs modeled as 'services'.
const std::type_info & Type
void registerBackend(const std::string &name, BackendCreate create)
std::function< std::unique_ptr< AcceleratorConnection >(Context &, std::string)> BackendCreate
Backends can register themselves to be connected via a connection string.
std::unique_ptr< AcceleratorConnection > connect(Context &ctxt, const std::string &backend, const std::string &connection)
static std::filesystem::path getExePath()
Get the path to the currently running executable.
std::map< std::string, std::any > ServiceImplDetails
static void loadBackend(Context &ctxt, std::string backend)
Load a backend plugin dynamically.
static std::filesystem::path getLibPath()
Get the path to the currently running shared library.
std::vector< HWClientDetail > HWClientDetails
std::map< ReadChannelPort *, std::pair< std::function< void(ReadChannelPort *, MessageData)>, std::future< MessageData > > > listeners
void addTask(std::function< void(void)> task)
void addListener(std::initializer_list< ReadChannelPort * > listenPorts, std::function< void(ReadChannelPort *, MessageData)> callback)
When there's data on any of the listenPorts, call the callback.
std::vector< std::function< void(void)> > taskList
Tasks which should be called on every loop iteration.