ChimeraTK-DeviceAccess 03.27.00
Loading...
Searching...
No Matches
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
7#include <boost/filesystem.hpp>
8#include <boost/function.hpp>
9#include <boost/interprocess/allocators/allocator.hpp>
10#include <boost/interprocess/containers/vector.hpp>
11#include <boost/interprocess/managed_shared_memory.hpp>
12#include <boost/interprocess/sync/interprocess_semaphore.hpp>
13#include <boost/interprocess/sync/named_mutex.hpp>
14#include <boost/move/unique_ptr.hpp>
15#include <boost/unordered_set.hpp>
16
17#include <list>
18#include <map>
19#include <thread>
20#include <utility>
21#include <vector>
22
23// Define shared-memory compatible vector type and corresponding allocator
25 boost::interprocess::allocator<int32_t, boost::interprocess::managed_shared_memory::segment_manager>;
26using SharedMemoryVector = boost::interprocess::vector<int32_t, ShmemAllocator>;
27using PidSet = boost::interprocess::vector<int32_t, ShmemAllocator>;
28
29namespace ChimeraTK {
30
31 // max. allowed SharedDummyBackend instances using common shared mem segment (global count, over all processes)
33
44 public:
46 size_t instanceIdHash, const std::string& mapFileName, const std::string& dataConsistencyKeyDescriptor = "");
47 ~SharedDummyBackend() override;
48
49 void open() override;
50 void closeImpl() override;
51
52 using DummyBackendBase::read; // use the 32 bit version from the base class
53 using DummyBackendBase::write; // use the 32 bit version from the base class
54 void read(uint64_t bar, uint64_t address, int32_t* data, size_t sizeInBytes) override;
55 void write(uint64_t bar, uint64_t address, int32_t const* data, size_t sizeInBytes) override;
56
57 std::string readDeviceInfo() override;
58
59 static boost::shared_ptr<DeviceBackend> createInstance(
60 std::string address, std::map<std::string, std::string> parameters);
61
62 VersionNumber triggerInterrupt(uint32_t interruptNumber) override;
63
64 private:
66 std::string _mapFile;
67
68 // Bar contents with shared-memory compatible vector type. Plain pointers are
69 // used here since this is what we get from the shared memory allocation.
70 std::map<uint64_t, SharedMemoryVector*> _barContents;
71
72 // Bar sizes
73 std::map<uint64_t, size_t> _barSizesInBytes;
74
75 // Naming of bars as shared memory elements
76 const char* SHARED_MEMORY_BAR_PREFIX = "BAR_";
77
78 class InterruptDispatcherInterface;
79
80 // Helper class to manage the shared memory: automatically construct if
81 // necessary, automatically destroy if last using process closes.
82 class SharedMemoryManager {
83 friend class SharedDummyBackend;
84
85 public:
86 SharedMemoryManager(SharedDummyBackend&, std::size_t instanceIdHash, const std::string&);
87 ~SharedMemoryManager();
88
92 SharedMemoryVector* findOrConstructVector(const std::string& objName, size_t size);
93
99 std::pair<size_t, size_t> getInfoOnMemory();
100
101 private:
102 // Constants to take overhead of managed shared memory into respect
103 // (approx. linear function, meta data for memory and meta data per vector)
104 // Uses overestimates for robustness.
105 static const size_t SHARED_MEMORY_CONST_OVERHEAD = 1000;
106 static const size_t SHARED_MEMORY_OVERHEAD_PER_VECTOR = 160;
107
108 const char* SHARED_MEMORY_PID_SET_NAME = "PidSet";
109 const char* SHARED_MEMORY_REQUIRED_VERSION_NAME = "RequiredVersion";
110
111 SharedDummyBackend& sharedDummyBackend;
112
113 // the name of the segment
114 std::string name;
115
116 // the shared memory segment
117 boost::interprocess::managed_shared_memory segment;
118
119 // the allocator instance
120 const ShmemAllocator sharedMemoryIntAllocator;
121
122 // Pointers to the set of process IDs and the required version
123 // specifier in shared memory
124 PidSet* pidSet{nullptr};
125 // Version number is not used for now, but included in shared memory
126 // to facilitate compatibility checks later
127 unsigned* requiredVersion{nullptr};
128
129 size_t getRequiredMemoryWithOverhead();
137 bool checkPidSetConsistency();
139 void reInitMemory();
140 std::vector<std::string> listNamedElements();
141
142 protected:
143 // interprocess mutex, has to be accessible by SharedDummyBackend class
144 boost::interprocess::named_mutex interprocessMutex;
145
146 boost::movelib::unique_ptr<InterruptDispatcherInterface> intDispatcherIf;
147 }; /* class SharedMemoryManager */
148
149 // Managed shared memory object
150 std::unique_ptr<SharedMemoryManager> sharedMemoryManager;
151
152 // Setup register bars in shared memory
153 void setupBarContents();
154
155 // Helper routines called in init list
156 size_t getTotalRegisterSizeInBytes() const;
157
158 static void checkSizeIsMultipleOfWordSize(size_t sizeInBytes);
159
160 static std::string convertPathRelativeToDmapToAbs(std::string const& mapfileName);
161
162 /****************** definitions for across-instance triggering ********/
163
164 // We are using the process id as an id of the semaphore which is to be triggered for the interrupt dispatcher
165 // thread. Since there is only one interrupt dispatcher thread per mapped shared memory region in a process, and
166 // the semaphore is set inside the shared memory, this means we can identify all semaphores per shared memory
167 // this way.
168 // However, this implies the restriction that you must not create more than one backend instance per shared memory
169 // region inside a process. E.g. if you wanted to write a test by tricking the backend factory into creating more
170 // than one backend instance for the same process and shared memory, you will have a problem.
171 using SemId = std::uint32_t;
172
174 struct SemEntry {
175 SemEntry() = default;
176 boost::interprocess::interprocess_semaphore s{0};
177 SemId semId{};
178 bool used = false;
179 };
180
184 struct InterruptEntry {
185 int _controllerId{0};
186 int _intNumber{};
187 std::uint32_t _counter = 0;
188 bool used = false;
189 };
190
192 static const int maxInterruptEntries = 1000;
193
196 struct ShmForSems {
197 // In addition to the semaphores themselves, shm stores a vector of
198 // interrupt numbers and their current counts.
199 // Vector entries are not moved and marked as unused when no longer needed.
200 // We need
201 // - a find function which returns list of appropriate semaphores to be triggered
202 // current concept does not use interrupt number for that
203 // - add/remove functions to add/remove a semaphore
204 // - functions to update interrupt counts
205
206 using Sem = boost::interprocess::interprocess_semaphore;
207
208 ShmForSems() = default;
209 ShmForSems(const ShmForSems&) = delete;
210
212 Sem* addSem(SemId semId);
213 bool removeSem(SemId semId);
215 void cleanup(PidSet* pidSet);
216
219 void addInterrupt(uint32_t interruptNumber);
220
224 std::list<Sem*> findSems(uint32_t interruptNumber = {}, bool update = false);
225
227 void print();
228
229 SemEntry semEntries[SHARED_MEMORY_N_MAX_MEMBER];
230 InterruptEntry interruptEntries[maxInterruptEntries];
231 };
232
233 struct InterruptDispatcherThread;
234
235 class InterruptDispatcherInterface {
236 public:
241 InterruptDispatcherInterface(SharedDummyBackend& backend, boost::interprocess::managed_shared_memory& shm,
242 boost::interprocess::named_mutex& shmMutex);
243
245 ~InterruptDispatcherInterface();
249 static void cleanupShm(boost::interprocess::managed_shared_memory& shm);
250 static void cleanupShm(boost::interprocess::managed_shared_memory& shm, PidSet* pidSet);
251
253 void triggerInterrupt(uint32_t intNumber);
254 boost::interprocess::named_mutex& _shmMutex;
255 SemId _semId;
256 ShmForSems* _semBuf;
257 boost::movelib::unique_ptr<InterruptDispatcherThread> _dispatcherThread;
258 SharedDummyBackend& _backend;
259 };
260
261 struct InterruptDispatcherThread {
263 explicit InterruptDispatcherThread(InterruptDispatcherInterface* dispatcherInterf);
264 InterruptDispatcherThread(const InterruptDispatcherThread&) = delete;
266 ~InterruptDispatcherThread();
267
268 void run();
269 void stop() noexcept;
271 void handleInterrupt(uint32_t interruptNumber);
272
273 private:
274 // plain pointer, because of cyclic dependency
275 InterruptDispatcherInterface* _dispatcherInterf;
276 SemId _semId;
277 ShmForSems* _semShm;
278 ShmForSems::Sem* _sem = nullptr;
279 // This must not be boost::thread since we don't want interruption points.
280 // The problem is that boost::thread::join() is an interruption point for the calling thread. The
281 // InterruptDispatcherThread does not rely on boost interruption points to control its tear down, it uses the
282 // _stop flag for this purpose.
283 std::thread _thr;
284 std::atomic_bool _started{false};
285 std::atomic_bool _stop{false};
286 };
287 };
288} // namespace ChimeraTK
boost::interprocess::vector< int32_t, ShmemAllocator > PidSet
boost::interprocess::vector< int32_t, ShmemAllocator > SharedMemoryVector
boost::interprocess::allocator< int32_t, boost::interprocess::managed_shared_memory::segment_manager > ShmemAllocator
Base class for DummyBackends, provides common functionality.
void write(uint8_t bar, uint32_t address, int32_t const *data, size_t sizeInBytes) final
You cannot override the write version with 32 bit address any more.
void read(uint8_t bar, uint32_t address, int32_t *data, size_t sizeInBytes) final
You cannot override the read version with 32 bit address any more.
The shared dummy device opens a mapping file defining the registers and implements them in shared mem...
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)
Class for generating and holding version numbers without exposing a numeric representation.
const int SHARED_MEMORY_N_MAX_MEMBER