12#include <boost/filesystem.hpp>
13#include <boost/lambda/lambda.hpp>
24 size_t instanceIdHash,
const std::string& mapFileName,
const std::string& dataConsistencyKeyDescriptor)
25 :
DummyBackendBase(mapFileName, dataConsistencyKeyDescriptor), _mapFile(mapFileName),
26 _barSizesInBytes(getBarSizesInBytesFromRegisterMapping()) {
29 sharedMemoryManager = std::make_unique<SharedMemoryManager>(*
this, instanceIdHash, mapFileName);
31 catch(boost::interprocess::lock_exception&) {
32 std::cerr <<
"SharedDummyBackend: boost::interprocess error, clearing shared memory segment." << std::endl;
35 boost::interprocess::shared_memory_object::remove(name.c_str());
36 boost::interprocess::named_mutex::remove(name.c_str());
45 sharedMemoryManager->intDispatcherIf.reset();
50 void SharedDummyBackend::setupBarContents() {
51 for(
auto& _barSizesInByte : _barSizesInBytes) {
52 std::string barName = SHARED_MEMORY_BAR_PREFIX +
std::to_string(_barSizesInByte.first);
54 size_t barSizeInWords = (_barSizesInByte.second +
sizeof(int32_t) - 1) /
sizeof(int32_t);
57 std::lock_guard<boost::interprocess::named_mutex> lock(sharedMemoryManager->interprocessMutex);
58 _barContents[_barSizesInByte.first] = sharedMemoryManager->findOrConstructVector(barName, barSizeInWords);
60 catch(boost::interprocess::bad_alloc&) {
62 sharedMemoryManager.reset();
64 std::string errMsg{
"Could not allocate shared memory while constructing registers. "
65 "Please file a bug report at "
66 "https://github.com/ChimeraTK/DeviceAccess."};
85 checkSizeIsMultipleOfWordSize(sizeInBytes);
86 uint64_t wordBaseIndex = address /
sizeof(int32_t);
88 std::lock_guard<boost::interprocess::named_mutex> lock(sharedMemoryManager->interprocessMutex);
89 for(uint64_t wordIndex = 0; wordIndex < sizeInBytes /
sizeof(int32_t); ++wordIndex) {
99 checkSizeIsMultipleOfWordSize(sizeInBytes);
100 uint64_t wordBaseIndex = address /
sizeof(int32_t);
102 std::lock_guard<boost::interprocess::named_mutex> lock(sharedMemoryManager->interprocessMutex);
104 for(uint64_t wordIndex = 0; wordIndex < sizeInBytes /
sizeof(int32_t); ++wordIndex) {
110 std::stringstream info;
111 info <<
"SharedDummyBackend";
115 size_t SharedDummyBackend::getTotalRegisterSizeInBytes()
const {
116 size_t totalRegSize = 0;
117 for(
const auto& pair : _barSizesInBytes) {
118 totalRegSize += pair.second;
123 void SharedDummyBackend::checkSizeIsMultipleOfWordSize(
size_t sizeInBytes) {
124 if(sizeInBytes %
sizeof(int32_t)) {
130 std::string address, std::map<std::string, std::string> parameters) {
138 std::string mapFileName = parameters[
"map"];
139 if(mapFileName.empty()) {
147 return returnInstance<SharedDummyBackend>(
148 address, instanceIdHash, convertPathRelativeToDmapToAbs(mapFileName), parameters[
"DataConsistencyKeys"]);
151 std::string SharedDummyBackend::convertPathRelativeToDmapToAbs(
const std::string& mapfileName) {
159 return boost::filesystem::canonical(absPathToMapFile).string();
163 this->sharedMemoryManager->intDispatcherIf->triggerInterrupt(interruptNumber);
170 SharedDummyBackend::InterruptDispatcherInterface::InterruptDispatcherInterface(
SharedDummyBackend& backend,
171 boost::interprocess::managed_shared_memory& shm, boost::interprocess::named_mutex& shmMutex)
172 : _shmMutex(shmMutex), _backend(backend) {
174 _semBuf = shm.find_or_construct<ShmForSems>(boost::interprocess::unique_instance)();
177 _dispatcherThread = boost::movelib::unique_ptr<InterruptDispatcherThread>(
new InterruptDispatcherThread(
this));
180 SharedDummyBackend::InterruptDispatcherInterface::~InterruptDispatcherInterface() {
182 _dispatcherThread.reset();
188 std::lock_guard<boost::interprocess::named_mutex> lock(_shmMutex);
189 _semBuf->removeSem(_semId);
191 catch(boost::interprocess::interprocess_exception&) {
198 void SharedDummyBackend::InterruptDispatcherInterface::cleanupShm(boost::interprocess::managed_shared_memory& shm) {
201 void SharedDummyBackend::InterruptDispatcherInterface::cleanupShm(
202 boost::interprocess::managed_shared_memory& shm,
PidSet* pidSet) {
203 ShmForSems* semBuf = shm.find_or_construct<ShmForSems>(boost::interprocess::unique_instance)();
204 semBuf->cleanup(pidSet);
207 void SharedDummyBackend::InterruptDispatcherInterface::triggerInterrupt(uint32_t intNumber) {
208 std::list<boost::interprocess::interprocess_semaphore*> semList;
210 std::lock_guard<boost::interprocess::named_mutex> lock(_shmMutex);
213 semList = _semBuf->findSems(intNumber,
true);
216 for(
auto* sem : semList) {
218 std::cout <<
" InterruptDispatcherInterface::triggerInterrupt: post sem for interrupt: " << intNumber
226 SharedDummyBackend::InterruptDispatcherThread::InterruptDispatcherThread(
227 InterruptDispatcherInterface* dispatcherInterf)
228 : _dispatcherInterf(dispatcherInterf), _semId(dispatcherInterf->_semId), _semShm(dispatcherInterf->_semBuf) {
229 _thr = boost::thread(&InterruptDispatcherThread::run,
this);
232 SharedDummyBackend::InterruptDispatcherThread::~InterruptDispatcherThread() {
238 catch(boost::system::system_error&) {
243 void SharedDummyBackend::InterruptDispatcherThread::run() {
248 std::map<std::pair<int, int>, std::uint32_t> lastInterruptState;
250 std::lock_guard<boost::interprocess::named_mutex> lock(_dispatcherInterf->_shmMutex);
251 for(
auto& entry : _semShm->interruptEntries) {
252 assert(entry._controllerId == 0);
253 if(!entry.used)
continue;
254 lastInterruptState[std::make_pair(entry._controllerId, entry._intNumber)] = entry._counter;
257 _sem = _semShm->addSem(_semId);
262 InterruptEntry interruptEntries[maxInterruptEntries];
267 std::lock_guard<boost::interprocess::named_mutex> lock(_dispatcherInterf->_shmMutex);
268 std::memcpy(interruptEntries, _semShm->interruptEntries,
sizeof(interruptEntries));
270 for(
auto& entry : interruptEntries) {
271 assert(entry._controllerId == 0);
272 if(!entry.used)
continue;
275 auto key = std::make_pair(entry._controllerId, entry._intNumber);
276 auto it = lastInterruptState.find(key);
277 if(it != lastInterruptState.end()) {
278 while(it->second != entry._counter) {
281 std::cout <<
"existing interrupt event for x,y = " << entry._controllerId <<
", " << entry._intNumber
284 handleInterrupt(entry._intNumber);
292 std::cout <<
"count = " << entry._counter <<
" interrupt events for x,y = " << entry._controllerId <<
", "
293 << entry._intNumber << std::endl;
295 handleInterrupt(entry._intNumber);
296 lastInterruptState[key] = entry._counter;
302 void SharedDummyBackend::InterruptDispatcherThread::stop() noexcept {
307 boost::this_thread::sleep_for(boost::chrono::milliseconds{10});
310 catch(
const boost::thread_interrupted&) {
321 catch(
const boost::system::system_error&) {
329 catch(
const boost::interprocess::interprocess_exception&) {
334 void SharedDummyBackend::InterruptDispatcherThread::handleInterrupt(uint32_t interruptNumber) {
335 SharedDummyBackend& backend = _dispatcherInterf->_backend;
336 auto asyncDomain = boost::dynamic_pointer_cast<async::DomainImpl<std::nullptr_t>>(
337 backend._asyncDomainsContainer.getDomain(interruptNumber));
342 assert(!backend._asyncDomainsContainer.getDomain(interruptNumber));
346 asyncDomain->distribute(
nullptr);
349 SharedDummyBackend::ShmForSems::Sem* SharedDummyBackend::ShmForSems::addSem(SemId semId) {
351 for(
auto& entry : semEntries) {
352 if(entry.used && entry.semId == semId) {
353 throw logic_error(
"error: semId already exists - check assumption about identifiers!");
357 for(
auto& entry : semEntries) {
368 throw runtime_error(
"error: semaphore array full - increase maxSems!");
371 bool SharedDummyBackend::ShmForSems::removeSem(SemId semId) {
373 for(
auto& entry : semEntries) {
374 if(entry.used && entry.semId == semId) {
382 void SharedDummyBackend::ShmForSems::cleanup(
PidSet* pidSet) {
383 for(
auto& entry : semEntries) {
385 if(std::find(std::begin(*pidSet), std::end(*pidSet), (int32_t)entry.semId) == std::end(*pidSet)) {
392 void SharedDummyBackend::ShmForSems::addInterrupt(uint32_t interruptNumber) {
394 for(
auto& entry : interruptEntries) {
395 if(entry.used && entry._controllerId == 0 &&
static_cast<uint32_t
>(entry._intNumber) == interruptNumber) {
403 for(
auto& entry : interruptEntries) {
406 entry._controllerId = 0;
407 entry._intNumber =
static_cast<int>(interruptNumber);
414 throw runtime_error(
"no place left in interruptEntries!");
419 std::list<SharedDummyBackend::ShmForSems::Sem*> SharedDummyBackend::ShmForSems::findSems(
420 uint32_t interruptNumber,
bool update) {
422 for(
auto& entry : semEntries) {
425 ret.push_back(&entry.s);
428 if(update) addInterrupt(interruptNumber);
432 void SharedDummyBackend::ShmForSems::print() {
433 std::cout <<
"shmem contents: " << std::endl;
434 for(
auto& entry : semEntries) {
435 if(entry.used) std::cout <<
"sem : " << entry.semId << std::endl;
437 for(
auto& entry : interruptEntries) {
439 std::cout <<
"interrupt : " << entry._controllerId <<
"," << entry._intNumber <<
" count = " << entry._counter
444 std::cout << std::endl;
#define TRY_REGISTER_ACCESS(COMMAND)
std::string getUserName()
boost::interprocess::vector< int32_t, ShmemAllocator > PidSet
boost::interprocess::vector< int32_t, ShmemAllocator > SharedMemoryVector
static BackendFactory & getInstance()
Static function to get an instance of factory.
void setOpenedAndClearException() noexcept
Backends should call this function at the end of a (successful) open() call.
void checkActiveException() final
Function to be called by backends when needing to check for an active exception.
std::atomic< bool > _opened
flag if backend is opened
Base class for DummyBackends, provides common functionality.
The shared dummy device opens a mapping file defining the registers and implements them in shared mem...
void read(uint64_t bar, uint64_t address, int32_t *data, size_t sizeInBytes) override
Read function to be implemented by backends.
SharedDummyBackend(size_t instanceIdHash, const std::string &mapFileName, const std::string &dataConsistencyKeyDescriptor="")
void open() override
Open the device.
std::string readDeviceInfo() override
Return a device information string containing hardware details like the firmware version number or th...
void closeImpl() override
All backends derrived from NumericAddressedBackend must implement closeImpl() instead of close.
VersionNumber triggerInterrupt(uint32_t interruptNumber) override
Simulate the arrival of an interrupt.
static boost::shared_ptr< DeviceBackend > createInstance(std::string address, std::map< std::string, std::string > parameters)
~SharedDummyBackend() override
void write(uint64_t bar, uint64_t address, int32_t const *data, size_t sizeInBytes) override
Write function to be implemented by backends.
Class for generating and holding version numbers without exposing a numeric representation.
Exception thrown when a logic error has occured.
std::size_t shmDummyInstanceIdHash(const std::string &address, const std::map< std::string, std::string > ¶meters)
Generates shm dummy instanceId hash from address and parameter map, Intended for use with parseDevice...
std::string createShmName(std::size_t instanceIdHash, const std::string &mapFileName, const std::string &userName)
Generates shm dummy name from parameter hashes.
std::string extractDirectory(std::string const &path)
Returns the path to the directory containing the file provided as the input parameter.
std::string concatenatePaths(const std::string &path1, const std::string &path2)
Concatenates two given paths using custom rules.
std::string convertToAbsolutePath(std::string const &relativePath)
Converts a relative path to its absolute path.
std::string getDMapFilePath()
Returns the dmap file name which the library currently uses for looking up device(alias) names.
std::string to_string(const std::string &v)