26#include <linux/limits.h>
37 : ctxt(ctxt), serviceThread(nullptr) {}
50 *
this, engineTypeName, idPath, details, clients);
55 std::unique_ptr<Engine> engine,
58 auto [engineIter, _] =
ownedEngines.emplace(idPath, std::move(engine));
62 Engine *enginePtr = engineIter->second.get();
65 if (prefix.size() > 0)
68 for (
const auto &client : clients) {
69 AppIDPath fullClientPath = prefix + client.relPath;
70 for (
const auto &channel : client.channelAssignments)
71 clientEngines[fullClientPath].setEngine(channel.first, enginePtr);
80 std::unique_ptr<Service> &cacheEntry =
serviceCache[make_tuple(&svcType,
id)];
81 if (cacheEntry ==
nullptr) {
88 cacheEntry = std::unique_ptr<Service>(svc);
90 return cacheEntry.get();
96 throw std::runtime_error(
97 "AcceleratorConnection already owns an accelerator");
105 char result[PATH_MAX];
106 ssize_t count = readlink(
"/proc/self/exe", result, PATH_MAX);
108 throw std::runtime_error(
"Could not get executable path");
109 return std::filesystem::path(std::string(result, count));
111 char buffer[MAX_PATH];
112 DWORD length = GetModuleFileNameA(NULL, buffer, MAX_PATH);
114 throw std::runtime_error(
"Could not get executable path");
115 return std::filesystem::path(std::string(buffer, length));
117#eror "Unsupported platform"
126 return std::filesystem::path(std::string(dl_info.dli_fname));
128 HMODULE hModule = NULL;
129 if (!GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
130 GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
131 reinterpret_cast<LPCSTR
>(&
getLibPath), &hModule)) {
133 return std::filesystem::path();
136 char buffer[MAX_PATH];
137 DWORD length = GetModuleFileNameA(hModule, buffer, MAX_PATH);
139 throw std::runtime_error(
"Could not get library path");
141 return std::filesystem::path(std::string(buffer, length));
143#eror "Unsupported platform"
151 Logger &logger = ctxt.getLogger();
152 backend[0] = toupper(backend[0]);
156 std::string backendFileName =
"lib" + backend +
"Backend.so";
158 std::string backendFileName = backend +
"Backend.dll";
160#eror "Unsupported platform"
168 std::filesystem::path backendPath = backendFileName;
169 std::string backendPathStr;
170 logger.
debug(
"CONNECT",
171 "trying to load backend plugin: " + backendPath.string());
172 if (!std::filesystem::exists(backendPath)) {
174 backendPath =
getExePath().parent_path().append(backendFileName);
175 logger.
debug(
"CONNECT",
176 "trying to load backend plugin: " + backendPath.string());
177 if (!std::filesystem::exists(backendPath)) {
179 backendPath =
getLibPath().parent_path().append(backendFileName);
180 logger.
debug(
"CONNECT",
181 "trying to load backend plugin: " + backendPath.string());
182 if (!std::filesystem::exists(backendPath)) {
184 backendPathStr = backendFileName;
185 logger.
debug(
"CONNECT",
186 "trying to load backend plugin: " + backendPathStr);
191 if (backendPathStr.empty())
192 backendPathStr = backendPath.string();
201 void *handle = dlopen(backendPathStr.c_str(), RTLD_NOW | RTLD_GLOBAL);
203 std::string error(dlerror());
204 logger.
error(
"CONNECT",
205 "while attempting to load backend plugin: " + error);
206 throw std::runtime_error(
"While attempting to load backend plugin: " +
212 if (backendPath != std::filesystem::path()) {
213 std::filesystem::path backendPathParent = backendPath.parent_path();
214 if (SetDllDirectoryA(backendPathParent.string().c_str()) == 0)
215 throw std::runtime_error(
"While setting DLL directory: " +
216 std::to_string(GetLastError()));
220 HMODULE handle = LoadLibraryA(backendPathStr.c_str());
222 DWORD error = GetLastError();
223 if (error == ERROR_MOD_NOT_FOUND) {
224 logger.
error(
"CONNECT",
"while attempting to load backend plugin: " +
225 backendPathStr +
" not found");
226 throw std::runtime_error(
"While attempting to load backend plugin: " +
227 backendPathStr +
" not found");
229 logger.
error(
"CONNECT",
"while attempting to load backend plugin: " +
230 std::to_string(error));
231 throw std::runtime_error(
"While attempting to load backend plugin: " +
232 std::to_string(error));
235#eror "Unsupported platform"
237 logger.
info(
"CONNECT",
"loaded backend plugin: " + backendPathStr);
245 static std::map<std::string, BackendCreate> &
get() {
245 static std::map<std::string, BackendCreate> &
get() {
…}
256 if (registry.count(name))
257 throw std::runtime_error(
"Backend already exists in registry");
258 registry[name] = create;
263 const std::string &backend,
264 const std::string &connection) {
266 auto f = registry.find(backend);
267 if (f == registry.end()) {
270 f = registry.find(backend);
271 if (f == registry.end()) {
272 ctxt.getLogger().error(
"CONNECT",
"backend '" + backend +
"' not found");
273 throw std::runtime_error(
"Backend '" + backend +
"' not found");
276 ctxt.getLogger().info(
"CONNECT",
"connecting to backend " + backend +
277 " via '" + connection +
"'");
278 return f->second(ctxt, connection);
285 void start() {
me = std::thread(&Impl::loop,
this); }
293 addListener(std::initializer_list<ReadChannelPort *> listenPorts,
296 void addTask(std::function<
void(
void)> task) {
297 std::lock_guard<std::mutex> g(
m);
312 std::future<MessageData>>>
319void AcceleratorServiceThread::Impl::loop() {
326 std::vector<std::function<void(
void)>> taskListCopy;
334 std::this_thread::yield();
339 std::lock_guard<std::mutex> g(
m);
340 for (
auto &[channel, cbfPair] :
listeners) {
341 assert(channel &&
"Null channel in listener list");
342 std::future<MessageData> &f = cbfPair.second;
343 if (f.wait_for(std::chrono::seconds(0)) == std::future_status::ready) {
344 portUnlockWorkList.emplace_back(channel, cbfPair.first, f.get());
345 f = channel->readAsync();
351 for (
auto [channel, cb, data] : portUnlockWorkList)
352 cb(channel, std::move(data));
355 portUnlockWorkList.clear();
360 std::lock_guard<std::mutex> g(
m);
363 for (
auto &task : taskListCopy)
319void AcceleratorServiceThread::Impl::loop() {
…}
368void AcceleratorServiceThread::Impl::addListener(
369 std::initializer_list<ReadChannelPort *> listenPorts,
371 std::lock_guard<std::mutex> g(m);
372 for (
auto port : listenPorts) {
373 if (listeners.count(port))
374 throw std::runtime_error(
"Port already has a listener");
375 listeners[port] = std::make_pair(callback, port->readAsync());
368void AcceleratorServiceThread::Impl::addListener( {
…}
382 : impl(std::make_unique<
Impl>()) {
398 std::initializer_list<ReadChannelPort *> listenPorts,
401 impl->addListener(listenPorts, callback);
406 impl->addTask([&module]() {
module.poll(); });
assert(baseType &&"element must be base type")
virtual void disconnect()
Disconnect from the accelerator cleanly.
virtual Service * createService(Service::Type service, AppIDPath idPath, std::string implName, const ServiceImplDetails &details, const HWClientDetails &clients)=0
Called by getServiceImpl exclusively.
ServiceClass * getService(AppIDPath id={}, std::string implName={}, ServiceImplDetails details={}, HWClientDetails clients={})
Get a typed reference to a particular service type.
std::map< AppIDPath, BundleEngineMap > clientEngines
Mapping of clients to their servicing engines.
void registerEngine(AppIDPath idPath, std::unique_ptr< Engine > engine, const HWClientDetails &clients)
If createEngine is overridden, this method should be called to register the engine and all of the cha...
std::map< ServiceCacheKey, std::unique_ptr< Service > > serviceCache
std::unique_ptr< AcceleratorServiceThread > serviceThread
std::unique_ptr< Accelerator > ownedAccelerator
Accelerator object owned by this connection.
std::map< AppIDPath, std::unique_ptr< Engine > > ownedEngines
Collection of owned engines.
virtual void createEngine(const std::string &engineTypeName, AppIDPath idPath, const ServiceImplDetails &details, const HWClientDetails &clients)
Create a new engine for channel communication with the accelerator.
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.
Engines implement the actual channel communication between the host and the accelerator.
Represents either the top level or an instance of a hardware module.
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.
std::map< std::string, BackendCreate > backendRegistry
static std::map< std::string, BackendCreate > & get()
static Service * createService(AcceleratorConnection *acc, Service::Type svcType, AppIDPath id, std::string implName, ServiceImplDetails details, HWClientDetails clients)
Create a service instance from the given details.
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)
std::unique_ptr< Engine > createEngine(AcceleratorConnection &conn, const std::string &dmaEngineName, AppIDPath idPath, const ServiceImplDetails &details, const HWClientDetails &clients)
Create an engine by name.
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.