ChimeraTK-DeviceAccess  03.18.00
SharedDummyBackend.h
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: Deutsches Elektronen-Synchrotron DESY, MSK, ChimeraTK Project <chimeratk-support@desy.de>
2 // SPDX-License-Identifier: LGPL-3.0-or-later
3 #pragma once
4 
5 #include "DummyBackendBase.h"
6 #include "Exception.h"
8 #include "ProcessManagement.h"
9 
10 #include <boost/filesystem.hpp>
11 #include <boost/function.hpp>
12 #include <boost/interprocess/allocators/allocator.hpp>
13 #include <boost/interprocess/containers/vector.hpp>
14 #include <boost/interprocess/managed_shared_memory.hpp>
15 #include <boost/interprocess/sync/interprocess_semaphore.hpp>
16 #include <boost/interprocess/sync/named_mutex.hpp>
17 #include <boost/move/unique_ptr.hpp>
18 #include <boost/unordered_set.hpp>
19 
20 #include <list>
21 #include <map>
22 #include <mutex>
23 #include <set>
24 #include <utility>
25 #include <vector>
26 
27 // Define shared-memory compatible vector type and corresponding allocator
28 using ShmemAllocator =
29  boost::interprocess::allocator<int32_t, boost::interprocess::managed_shared_memory::segment_manager>;
30 using SharedMemoryVector = boost::interprocess::vector<int32_t, ShmemAllocator>;
31 using PidSet = boost::interprocess::vector<int32_t, ShmemAllocator>;
32 
33 namespace ChimeraTK {
34 
35  // max. allowed SharedDummyBackend instances using common shared mem segment (global count, over all processes)
37 
48  public:
49  SharedDummyBackend(const std::string& instanceId, const std::string& mapFileName);
50  ~SharedDummyBackend() override;
51 
52  void open() override;
53  void closeImpl() override;
54 
55  using DummyBackendBase::read; // use the 32 bit version from the base class
56  using DummyBackendBase::write; // use the 32 bit version from the base class
57  void read(uint64_t bar, uint64_t address, int32_t* data, size_t sizeInBytes) override;
58  void write(uint64_t bar, uint64_t address, int32_t const* data, size_t sizeInBytes) override;
59 
60  std::string readDeviceInfo() override;
61 
62  static boost::shared_ptr<DeviceBackend> createInstance(
63  std::string address, std::map<std::string, std::string> parameters);
64 
65  VersionNumber triggerInterrupt(uint32_t interruptNumber) override;
66 
67  private:
69  std::string _mapFile;
70 
71  // Bar contents with shared-memory compatible vector type. Plain pointers are
72  // used here since this is what we get from the shared memory allocation.
73  std::map<uint64_t, SharedMemoryVector*> _barContents;
74 
75  // Bar sizes
76  std::map<uint64_t, size_t> _barSizesInBytes;
77 
78  // Naming of bars as shared memory elements
79  const char* SHARED_MEMORY_BAR_PREFIX = "BAR_";
80 
81  class InterruptDispatcherInterface;
82 
83  // Helper class to manage the shared memory: automatically construct if
84  // necessary, automatically destroy if last using process closes.
85  class SharedMemoryManager {
86  friend class SharedDummyBackend;
87 
88  public:
89  SharedMemoryManager(SharedDummyBackend&, const std::string&, const std::string&);
90  ~SharedMemoryManager();
91 
95  SharedMemoryVector* findOrConstructVector(const std::string& objName, size_t size);
96 
102  std::pair<size_t, size_t> getInfoOnMemory();
103 
104  private:
105  // Constants to take overhead of managed shared memory into respect
106  // (approx. linear function, meta data for memory and meta data per vector)
107  // Uses overestimates for robustness.
108  static const size_t SHARED_MEMORY_CONST_OVERHEAD = 1000;
109  static const size_t SHARED_MEMORY_OVERHEAD_PER_VECTOR = 160;
110 
111  const char* SHARED_MEMORY_PID_SET_NAME = "PidSet";
112  const char* SHARED_MEMORY_REQUIRED_VERSION_NAME = "RequiredVersion";
113 
114  SharedDummyBackend& sharedDummyBackend;
115 
116  // Hashes to assure match of shared memory accessing processes
117  std::string userHash;
118  std::string mapFileHash;
119  std::string instanceIdHash;
120 
121  // the name of the segment
122  std::string name;
123 
124  // the shared memory segment
125  boost::interprocess::managed_shared_memory segment;
126 
127  // the allocator instance
128  const ShmemAllocator sharedMemoryIntAllocator;
129 
130  // Pointers to the set of process IDs and the required version
131  // specifier in shared memory
132  PidSet* pidSet{nullptr};
133  // Version number is not used for now, but included in shared memory
134  // to facilitate compatibility checks later
135  unsigned* requiredVersion{nullptr};
136 
137  size_t getRequiredMemoryWithOverhead();
145  bool checkPidSetConsistency();
147  void reInitMemory();
148  std::vector<std::string> listNamedElements();
149 
150  protected:
151  // interprocess mutex, has to be accessible by SharedDummyBackend class
152  boost::interprocess::named_mutex interprocessMutex;
153 
154  boost::movelib::unique_ptr<InterruptDispatcherInterface> intDispatcherIf;
155  }; /* class SharedMemoryManager */
156 
157  // Managed shared memory object
158  SharedMemoryManager sharedMemoryManager;
159 
160  // Setup register bars in shared memory
161  void setupBarContents();
162 
163  // Helper routines called in init list
164  size_t getTotalRegisterSizeInBytes() const;
165 
166  static void checkSizeIsMultipleOfWordSize(size_t sizeInBytes);
167 
168  static std::string convertPathRelativeToDmapToAbs(std::string const& mapfileName);
169 
170  /****************** definitions for across-instance triggering ********/
171 
172  // We are using the process id as an id of the semaphore which is to be triggered for the interrupt dispatcher
173  // thread. Since there is only one interrupt dispatcher thread per mapped shared memory region in a process, and
174  // the semaphore is set inside the shared memory, this means we can identify all semaphores per shared memory
175  // this way.
176  // However, this implies the restriction that you must not create more than one backend instance per shared memory
177  // region inside a process. E.g. if you wanted to write a test by tricking the backend factory into creating more
178  // than one backend instance for the same process and shared memory, you will have a problem.
179  using SemId = std::uint32_t;
180 
182  struct SemEntry {
183  SemEntry() = default;
184  boost::interprocess::interprocess_semaphore s{0};
185  SemId semId{};
186  bool used = false;
187  };
188 
192  struct InterruptEntry {
193  int _controllerId{0};
194  int _intNumber{};
195  std::uint32_t _counter = 0;
196  bool used = false;
197  };
198 
200  static const int maxInterruptEntries = 1000;
201 
204  struct ShmForSems {
205  // In addition to the semaphores themselves, shm stores a vector of
206  // interrupt numbers and their current counts.
207  // Vector entries are not moved and marked as unused when no longer needed.
208  // We need
209  // - a find function which returns list of appropriate semaphores to be triggered
210  // current concept does not use interrupt number for that
211  // - add/remove functions to add/remove a semaphore
212  // - functions to update interrupt counts
213 
214  using Sem = boost::interprocess::interprocess_semaphore;
215 
216  ShmForSems() = default;
217  ShmForSems(const ShmForSems&) = delete;
218 
220  Sem* addSem(SemId semId);
221  bool removeSem(SemId semId);
223  void cleanup(PidSet* pidSet);
224 
227  void addInterrupt(uint32_t interruptNumber);
228 
232  std::list<Sem*> findSems(uint32_t interruptNumber = {}, bool update = false);
233 
235  void print();
236 
237  SemEntry semEntries[SHARED_MEMORY_N_MAX_MEMBER];
238  InterruptEntry interruptEntries[maxInterruptEntries];
239  };
240 
241  struct InterruptDispatcherThread;
242 
243  class InterruptDispatcherInterface {
244  public:
249  InterruptDispatcherInterface(SharedDummyBackend& backend, boost::interprocess::managed_shared_memory& shm,
250  boost::interprocess::named_mutex& shmMutex);
251 
253  ~InterruptDispatcherInterface();
257  static void cleanupShm(boost::interprocess::managed_shared_memory& shm);
258  static void cleanupShm(boost::interprocess::managed_shared_memory& shm, PidSet* pidSet);
259 
261  void triggerInterrupt(uint32_t intNumber);
262  boost::interprocess::named_mutex& _shmMutex;
263  SemId _semId;
264  ShmForSems* _semBuf;
265  boost::movelib::unique_ptr<InterruptDispatcherThread> _dispatcherThread;
266  SharedDummyBackend& _backend;
267  };
268 
269  struct InterruptDispatcherThread {
271  explicit InterruptDispatcherThread(InterruptDispatcherInterface* dispatcherInterf);
272  InterruptDispatcherThread(const InterruptDispatcherThread&) = delete;
274  ~InterruptDispatcherThread();
275 
276  void run();
277  void stop() noexcept;
279  void handleInterrupt(uint32_t interruptNumber);
280 
281  private:
282  // plain pointer, because of cyclic dependency
283  InterruptDispatcherInterface* _dispatcherInterf;
284  SemId _semId;
285  ShmForSems* _semShm;
286  ShmForSems::Sem* _sem = nullptr;
287  boost::thread _thr;
288  std::atomic_bool _started{false};
289  std::atomic_bool _stop{false};
290  };
291  };
292 } // namespace ChimeraTK
ChimeraTK::DummyBackendBase::read
void read([[maybe_unused]] uint8_t bar, [[maybe_unused]] uint32_t address, [[maybe_unused]] int32_t *data, [[maybe_unused]] size_t sizeInBytes) final
You cannot override the read version with 32 bit address any more.
Definition: DummyBackendBase.h:55
ChimeraTK::SharedDummyBackend::SharedDummyBackend
SharedDummyBackend(const std::string &instanceId, const std::string &mapFileName)
Definition: SharedDummyBackend.cc:22
ChimeraTK::SharedDummyBackend::write
void write(uint64_t bar, uint64_t address, int32_t const *data, size_t sizeInBytes) override
Write function to be implemented by backends.
Definition: SharedDummyBackend.cc:79
ChimeraTK::DummyBackendBase
Base class for DummyBackends, provides common functionality.
Definition: DummyBackendBase.h:31
ChimeraTK::SharedDummyBackend
The shared dummy device opens a mapping file defining the registers and implements them in shared mem...
Definition: SharedDummyBackend.h:47
MirrorRequestType::stop
@ stop
ChimeraTK::SharedDummyBackend::closeImpl
void closeImpl() override
All backends derrived from NumericAddressedBackend must implement closeImpl() instead of close.
Definition: SharedDummyBackend.cc:61
ChimeraTK::SharedDummyBackend::open
void open() override
Open the device.
Definition: SharedDummyBackend.cc:57
ChimeraTK::SharedDummyBackend::~SharedDummyBackend
~SharedDummyBackend() override
Definition: SharedDummyBackend.cc:28
ChimeraTK::SharedDummyBackend::triggerInterrupt
VersionNumber triggerInterrupt(uint32_t interruptNumber) override
Simulate the arrival of an interrupt.
Definition: SharedDummyBackend.cc:139
ChimeraTK::DummyBackendBase::write
void write([[maybe_unused]] uint8_t bar, [[maybe_unused]] uint32_t address, [[maybe_unused]] int32_t const *data, [[maybe_unused]] size_t sizeInBytes) final
You cannot override the write version with 32 bit address any more.
Definition: DummyBackendBase.h:63
NumericAddressedRegisterCatalogue.h
ChimeraTK::SHARED_MEMORY_N_MAX_MEMBER
const int SHARED_MEMORY_N_MAX_MEMBER
Definition: SharedDummyBackend.h:36
ProcessManagement.h
ChimeraTK::VersionNumber
Class for generating and holding version numbers without exposing a numeric representation.
Definition: VersionNumber.h:23
ShmemAllocator
boost::interprocess::allocator< int32_t, boost::interprocess::managed_shared_memory::segment_manager > ShmemAllocator
Definition: SharedDummyBackend.h:29
PidSet
boost::interprocess::vector< int32_t, ShmemAllocator > PidSet
Definition: SharedDummyBackend.h:31
Exception.h
SharedMemoryVector
boost::interprocess::vector< int32_t, ShmemAllocator > SharedMemoryVector
Definition: SharedDummyBackend.h:30
DummyBackendBase.h
ChimeraTK
Definition: DummyBackend.h:16
ChimeraTK::SharedDummyBackend::createInstance
static boost::shared_ptr< DeviceBackend > createInstance(std::string address, std::map< std::string, std::string > parameters)
Definition: SharedDummyBackend.cc:114
ChimeraTK::SharedDummyBackend::readDeviceInfo
std::string readDeviceInfo() override
Return a device information string containing hardware details like the firmware version number or th...
Definition: SharedDummyBackend.cc:94
ChimeraTK::SharedDummyBackend::read
void read(uint64_t bar, uint64_t address, int32_t *data, size_t sizeInBytes) override
Read function to be implemented by backends.
Definition: SharedDummyBackend.cc:65