ChimeraTK-DeviceAccess 03.25.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#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
29 boost::interprocess::allocator<int32_t, boost::interprocess::managed_shared_memory::segment_manager>;
30using SharedMemoryVector = boost::interprocess::vector<int32_t, ShmemAllocator>;
31using PidSet = boost::interprocess::vector<int32_t, ShmemAllocator>;
32
33namespace ChimeraTK {
34
35 // max. allowed SharedDummyBackend instances using common shared mem segment (global count, over all processes)
37
48 public:
50 size_t instanceIdHash, const std::string& mapFileName, const std::string& dataConsistencyKeyDescriptor = "");
51 ~SharedDummyBackend() override;
52
53 void open() override;
54 void closeImpl() override;
55
56 using DummyBackendBase::read; // use the 32 bit version from the base class
57 using DummyBackendBase::write; // use the 32 bit version from the base class
58 void read(uint64_t bar, uint64_t address, int32_t* data, size_t sizeInBytes) override;
59 void write(uint64_t bar, uint64_t address, int32_t const* data, size_t sizeInBytes) override;
60
61 std::string readDeviceInfo() override;
62
63 static boost::shared_ptr<DeviceBackend> createInstance(
64 std::string address, std::map<std::string, std::string> parameters);
65
66 VersionNumber triggerInterrupt(uint32_t interruptNumber) override;
67
68 private:
70 std::string _mapFile;
71
72 // Bar contents with shared-memory compatible vector type. Plain pointers are
73 // used here since this is what we get from the shared memory allocation.
74 std::map<uint64_t, SharedMemoryVector*> _barContents;
75
76 // Bar sizes
77 std::map<uint64_t, size_t> _barSizesInBytes;
78
79 // Naming of bars as shared memory elements
80 const char* SHARED_MEMORY_BAR_PREFIX = "BAR_";
81
82 class InterruptDispatcherInterface;
83
84 // Helper class to manage the shared memory: automatically construct if
85 // necessary, automatically destroy if last using process closes.
86 class SharedMemoryManager {
87 friend class SharedDummyBackend;
88
89 public:
90 SharedMemoryManager(SharedDummyBackend&, std::size_t instanceIdHash, const std::string&);
91 ~SharedMemoryManager();
92
96 SharedMemoryVector* findOrConstructVector(const std::string& objName, size_t size);
97
103 std::pair<size_t, size_t> getInfoOnMemory();
104
105 private:
106 // Constants to take overhead of managed shared memory into respect
107 // (approx. linear function, meta data for memory and meta data per vector)
108 // Uses overestimates for robustness.
109 static const size_t SHARED_MEMORY_CONST_OVERHEAD = 1000;
110 static const size_t SHARED_MEMORY_OVERHEAD_PER_VECTOR = 160;
111
112 const char* SHARED_MEMORY_PID_SET_NAME = "PidSet";
113 const char* SHARED_MEMORY_REQUIRED_VERSION_NAME = "RequiredVersion";
114
115 SharedDummyBackend& sharedDummyBackend;
116
117 // the name of the segment
118 std::string name;
119
120 // the shared memory segment
121 boost::interprocess::managed_shared_memory segment;
122
123 // the allocator instance
124 const ShmemAllocator sharedMemoryIntAllocator;
125
126 // Pointers to the set of process IDs and the required version
127 // specifier in shared memory
128 PidSet* pidSet{nullptr};
129 // Version number is not used for now, but included in shared memory
130 // to facilitate compatibility checks later
131 unsigned* requiredVersion{nullptr};
132
133 size_t getRequiredMemoryWithOverhead();
141 bool checkPidSetConsistency();
143 void reInitMemory();
144 std::vector<std::string> listNamedElements();
145
146 protected:
147 // interprocess mutex, has to be accessible by SharedDummyBackend class
148 boost::interprocess::named_mutex interprocessMutex;
149
150 boost::movelib::unique_ptr<InterruptDispatcherInterface> intDispatcherIf;
151 }; /* class SharedMemoryManager */
152
153 // Managed shared memory object
154 std::unique_ptr<SharedMemoryManager> sharedMemoryManager;
155
156 // Setup register bars in shared memory
157 void setupBarContents();
158
159 // Helper routines called in init list
160 size_t getTotalRegisterSizeInBytes() const;
161
162 static void checkSizeIsMultipleOfWordSize(size_t sizeInBytes);
163
164 static std::string convertPathRelativeToDmapToAbs(std::string const& mapfileName);
165
166 /****************** definitions for across-instance triggering ********/
167
168 // We are using the process id as an id of the semaphore which is to be triggered for the interrupt dispatcher
169 // thread. Since there is only one interrupt dispatcher thread per mapped shared memory region in a process, and
170 // the semaphore is set inside the shared memory, this means we can identify all semaphores per shared memory
171 // this way.
172 // However, this implies the restriction that you must not create more than one backend instance per shared memory
173 // region inside a process. E.g. if you wanted to write a test by tricking the backend factory into creating more
174 // than one backend instance for the same process and shared memory, you will have a problem.
175 using SemId = std::uint32_t;
176
178 struct SemEntry {
179 SemEntry() = default;
180 boost::interprocess::interprocess_semaphore s{0};
181 SemId semId{};
182 bool used = false;
183 };
184
188 struct InterruptEntry {
189 int _controllerId{0};
190 int _intNumber{};
191 std::uint32_t _counter = 0;
192 bool used = false;
193 };
194
196 static const int maxInterruptEntries = 1000;
197
200 struct ShmForSems {
201 // In addition to the semaphores themselves, shm stores a vector of
202 // interrupt numbers and their current counts.
203 // Vector entries are not moved and marked as unused when no longer needed.
204 // We need
205 // - a find function which returns list of appropriate semaphores to be triggered
206 // current concept does not use interrupt number for that
207 // - add/remove functions to add/remove a semaphore
208 // - functions to update interrupt counts
209
210 using Sem = boost::interprocess::interprocess_semaphore;
211
212 ShmForSems() = default;
213 ShmForSems(const ShmForSems&) = delete;
214
216 Sem* addSem(SemId semId);
217 bool removeSem(SemId semId);
219 void cleanup(PidSet* pidSet);
220
223 void addInterrupt(uint32_t interruptNumber);
224
228 std::list<Sem*> findSems(uint32_t interruptNumber = {}, bool update = false);
229
231 void print();
232
233 SemEntry semEntries[SHARED_MEMORY_N_MAX_MEMBER];
234 InterruptEntry interruptEntries[maxInterruptEntries];
235 };
236
237 struct InterruptDispatcherThread;
238
239 class InterruptDispatcherInterface {
240 public:
245 InterruptDispatcherInterface(SharedDummyBackend& backend, boost::interprocess::managed_shared_memory& shm,
246 boost::interprocess::named_mutex& shmMutex);
247
249 ~InterruptDispatcherInterface();
253 static void cleanupShm(boost::interprocess::managed_shared_memory& shm);
254 static void cleanupShm(boost::interprocess::managed_shared_memory& shm, PidSet* pidSet);
255
257 void triggerInterrupt(uint32_t intNumber);
258 boost::interprocess::named_mutex& _shmMutex;
259 SemId _semId;
260 ShmForSems* _semBuf;
261 boost::movelib::unique_ptr<InterruptDispatcherThread> _dispatcherThread;
262 SharedDummyBackend& _backend;
263 };
264
265 struct InterruptDispatcherThread {
267 explicit InterruptDispatcherThread(InterruptDispatcherInterface* dispatcherInterf);
268 InterruptDispatcherThread(const InterruptDispatcherThread&) = delete;
270 ~InterruptDispatcherThread();
271
272 void run();
273 void stop() noexcept;
275 void handleInterrupt(uint32_t interruptNumber);
276
277 private:
278 // plain pointer, because of cyclic dependency
279 InterruptDispatcherInterface* _dispatcherInterf;
280 SemId _semId;
281 ShmForSems* _semShm;
282 ShmForSems::Sem* _sem = nullptr;
283 boost::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