12#include <boost/lambda/lambda.hpp>
22 size_t instanceIdHash,
const std::string& mapFileName,
const std::string& dataConsistencyKeyDescriptor)
23 :
DummyBackendBase(mapFileName, dataConsistencyKeyDescriptor), _mapFile(_resolvedMapFileName),
24 _barSizesInBytes(getBarSizesInBytesFromRegisterMapping()) {
27 sharedMemoryManager = std::make_unique<SharedMemoryManager>(*
this, instanceIdHash,
_resolvedMapFileName);
29 catch(boost::interprocess::lock_exception&) {
30 std::cerr <<
"SharedDummyBackend: boost::interprocess error, clearing shared memory segment." << std::endl;
33 boost::interprocess::shared_memory_object::remove(name.c_str());
34 boost::interprocess::named_mutex::remove(name.c_str());
43 sharedMemoryManager->intDispatcherIf.reset();
48 void SharedDummyBackend::setupBarContents() {
49 for(
auto& _barSizesInByte : _barSizesInBytes) {
50 std::string barName = SHARED_MEMORY_BAR_PREFIX +
std::to_string(_barSizesInByte.first);
52 size_t barSizeInWords = (_barSizesInByte.second +
sizeof(int32_t) - 1) /
sizeof(int32_t);
55 std::lock_guard<boost::interprocess::named_mutex> lock(sharedMemoryManager->interprocessMutex);
56 _barContents[_barSizesInByte.first] = sharedMemoryManager->findOrConstructVector(barName, barSizeInWords);
58 catch(boost::interprocess::bad_alloc&) {
60 sharedMemoryManager.reset();
62 std::string errMsg{
"Could not allocate shared memory while constructing registers. "
63 "Please file a bug report at "
64 "https://github.com/ChimeraTK/DeviceAccess."};
83 checkSizeIsMultipleOfWordSize(sizeInBytes);
84 uint64_t wordBaseIndex = address /
sizeof(int32_t);
86 std::lock_guard<boost::interprocess::named_mutex> lock(sharedMemoryManager->interprocessMutex);
87 for(uint64_t wordIndex = 0; wordIndex < sizeInBytes /
sizeof(int32_t); ++wordIndex) {
97 checkSizeIsMultipleOfWordSize(sizeInBytes);
98 uint64_t wordBaseIndex = address /
sizeof(int32_t);
100 std::lock_guard<boost::interprocess::named_mutex> lock(sharedMemoryManager->interprocessMutex);
102 for(uint64_t wordIndex = 0; wordIndex < sizeInBytes /
sizeof(int32_t); ++wordIndex) {
108 std::stringstream info;
109 info <<
"SharedDummyBackend";
113 size_t SharedDummyBackend::getTotalRegisterSizeInBytes()
const {
114 size_t totalRegSize = 0;
115 for(
const auto& pair : _barSizesInBytes) {
116 totalRegSize += pair.second;
121 void SharedDummyBackend::checkSizeIsMultipleOfWordSize(
size_t sizeInBytes) {
122 if(sizeInBytes %
sizeof(int32_t)) {
128 std::string address, std::map<std::string, std::string> parameters) {
136 std::string mapFileName = parameters[
"map"];
137 if(mapFileName.empty()) {
143 return returnInstance<SharedDummyBackend>(address, instanceIdHash, mapFileName, parameters[
"DataConsistencyKeys"]);
147 this->sharedMemoryManager->intDispatcherIf->triggerInterrupt(interruptNumber);
154 SharedDummyBackend::InterruptDispatcherInterface::InterruptDispatcherInterface(
SharedDummyBackend& backend,
155 boost::interprocess::managed_shared_memory& shm, boost::interprocess::named_mutex& shmMutex)
156 : _shmMutex(shmMutex), _backend(backend) {
158 _semBuf = shm.find_or_construct<ShmForSems>(boost::interprocess::unique_instance)();
161 _dispatcherThread = boost::movelib::unique_ptr<InterruptDispatcherThread>(
new InterruptDispatcherThread(
this));
164 SharedDummyBackend::InterruptDispatcherInterface::~InterruptDispatcherInterface() {
166 _dispatcherThread.reset();
172 std::lock_guard<boost::interprocess::named_mutex> lock(_shmMutex);
173 _semBuf->removeSem(_semId);
175 catch(boost::interprocess::interprocess_exception&) {
182 void SharedDummyBackend::InterruptDispatcherInterface::cleanupShm(boost::interprocess::managed_shared_memory& shm) {
185 void SharedDummyBackend::InterruptDispatcherInterface::cleanupShm(
186 boost::interprocess::managed_shared_memory& shm,
PidSet* pidSet) {
187 ShmForSems* semBuf = shm.find_or_construct<ShmForSems>(boost::interprocess::unique_instance)();
188 semBuf->cleanup(pidSet);
191 void SharedDummyBackend::InterruptDispatcherInterface::triggerInterrupt(uint32_t intNumber) {
192 std::list<boost::interprocess::interprocess_semaphore*> semList;
194 std::lock_guard<boost::interprocess::named_mutex> lock(_shmMutex);
197 semList = _semBuf->findSems(intNumber,
true);
200 for(
auto* sem : semList) {
202 std::cout <<
" InterruptDispatcherInterface::triggerInterrupt: post sem for interrupt: " << intNumber
210 SharedDummyBackend::InterruptDispatcherThread::InterruptDispatcherThread(
211 InterruptDispatcherInterface* dispatcherInterf)
212 : _dispatcherInterf(dispatcherInterf), _semId(dispatcherInterf->_semId), _semShm(dispatcherInterf->_semBuf) {
213 _thr = std::thread(&InterruptDispatcherThread::run,
this);
216 SharedDummyBackend::InterruptDispatcherThread::~InterruptDispatcherThread() {
222 catch(boost::system::system_error&) {
227 void SharedDummyBackend::InterruptDispatcherThread::run() {
232 std::map<std::pair<int, int>, std::uint32_t> lastInterruptState;
234 std::lock_guard<boost::interprocess::named_mutex> lock(_dispatcherInterf->_shmMutex);
235 for(
auto& entry : _semShm->interruptEntries) {
236 assert(entry._controllerId == 0);
237 if(!entry.used)
continue;
238 lastInterruptState[std::make_pair(entry._controllerId, entry._intNumber)] = entry._counter;
241 _sem = _semShm->addSem(_semId);
246 InterruptEntry interruptEntries[maxInterruptEntries];
251 std::lock_guard<boost::interprocess::named_mutex> lock(_dispatcherInterf->_shmMutex);
252 std::memcpy(interruptEntries, _semShm->interruptEntries,
sizeof(interruptEntries));
254 for(
auto& entry : interruptEntries) {
255 assert(entry._controllerId == 0);
256 if(!entry.used)
continue;
259 auto key = std::make_pair(entry._controllerId, entry._intNumber);
260 auto it = lastInterruptState.find(key);
261 if(it != lastInterruptState.end()) {
262 while(it->second != entry._counter) {
265 std::cout <<
"existing interrupt event for x,y = " << entry._controllerId <<
", " << entry._intNumber
268 handleInterrupt(entry._intNumber);
276 std::cout <<
"count = " << entry._counter <<
" interrupt events for x,y = " << entry._controllerId <<
", "
277 << entry._intNumber << std::endl;
279 handleInterrupt(entry._intNumber);
280 lastInterruptState[key] = entry._counter;
286 void SharedDummyBackend::InterruptDispatcherThread::stop() noexcept {
291 boost::this_thread::sleep_for(boost::chrono::milliseconds{10});
294 catch(
const boost::thread_interrupted&) {
305 catch(
const boost::system::system_error&) {
313 catch(
const boost::interprocess::interprocess_exception&) {
318 void SharedDummyBackend::InterruptDispatcherThread::handleInterrupt(uint32_t interruptNumber) {
319 SharedDummyBackend& backend = _dispatcherInterf->_backend;
320 auto asyncDomain = boost::dynamic_pointer_cast<async::DomainImpl<std::nullptr_t>>(
321 backend._asyncDomainsContainer.getDomain(interruptNumber));
326 assert(!backend._asyncDomainsContainer.getDomain(interruptNumber));
330 asyncDomain->distribute(
nullptr);
333 SharedDummyBackend::ShmForSems::Sem* SharedDummyBackend::ShmForSems::addSem(SemId semId) {
335 for(
auto& entry : semEntries) {
336 if(entry.used && entry.semId == semId) {
337 throw logic_error(
"error: semId already exists - check assumption about identifiers!");
341 for(
auto& entry : semEntries) {
352 throw runtime_error(
"error: semaphore array full - increase maxSems!");
355 bool SharedDummyBackend::ShmForSems::removeSem(SemId semId) {
357 for(
auto& entry : semEntries) {
358 if(entry.used && entry.semId == semId) {
366 void SharedDummyBackend::ShmForSems::cleanup(
PidSet* pidSet) {
367 for(
auto& entry : semEntries) {
369 if(std::find(std::begin(*pidSet), std::end(*pidSet), (int32_t)entry.semId) == std::end(*pidSet)) {
376 void SharedDummyBackend::ShmForSems::addInterrupt(uint32_t interruptNumber) {
378 for(
auto& entry : interruptEntries) {
379 if(entry.used && entry._controllerId == 0 &&
static_cast<uint32_t
>(entry._intNumber) == interruptNumber) {
387 for(
auto& entry : interruptEntries) {
390 entry._controllerId = 0;
391 entry._intNumber =
static_cast<int>(interruptNumber);
398 throw runtime_error(
"no place left in interruptEntries!");
403 std::list<SharedDummyBackend::ShmForSems::Sem*> SharedDummyBackend::ShmForSems::findSems(
404 uint32_t interruptNumber,
bool update) {
406 for(
auto& entry : semEntries) {
409 ret.push_back(&entry.s);
412 if(update) addInterrupt(interruptNumber);
416 void SharedDummyBackend::ShmForSems::print() {
417 std::cout <<
"shmem contents: " << std::endl;
418 for(
auto& entry : semEntries) {
419 if(entry.used) std::cout <<
"sem : " << entry.semId << std::endl;
421 for(
auto& entry : interruptEntries) {
423 std::cout <<
"interrupt : " << entry._controllerId <<
"," << entry._intNumber <<
" count = " << entry._counter
428 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
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.
std::string _resolvedMapFileName
The resolved absolute path of the map file used for parsing and for any derived naming (e....
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 to_string(const std::string &v)