ChimeraTK-DeviceAccess 03.29.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 /****************** definitions for across-instance triggering ********/
161
162 // We are using the process id as an id of the semaphore which is to be triggered for the interrupt dispatcher
163 // thread. Since there is only one interrupt dispatcher thread per mapped shared memory region in a process, and
164 // the semaphore is set inside the shared memory, this means we can identify all semaphores per shared memory
165 // this way.
166 // However, this implies the restriction that you must not create more than one backend instance per shared memory
167 // region inside a process. E.g. if you wanted to write a test by tricking the backend factory into creating more
168 // than one backend instance for the same process and shared memory, you will have a problem.
169 using SemId = std::uint32_t;
170
172 struct SemEntry {
173 SemEntry() = default;
174 boost::interprocess::interprocess_semaphore s{0};
175 SemId semId{};
176 bool used = false;
177 };
178
182 struct InterruptEntry {
183 int _controllerId{0};
184 int _intNumber{};
185 std::uint32_t _counter = 0;
186 bool used = false;
187 };
188
190 static const int maxInterruptEntries = 1000;
191
194 struct ShmForSems {
195 // In addition to the semaphores themselves, shm stores a vector of
196 // interrupt numbers and their current counts.
197 // Vector entries are not moved and marked as unused when no longer needed.
198 // We need
199 // - a find function which returns list of appropriate semaphores to be triggered
200 // current concept does not use interrupt number for that
201 // - add/remove functions to add/remove a semaphore
202 // - functions to update interrupt counts
203
204 using Sem = boost::interprocess::interprocess_semaphore;
205
206 ShmForSems() = default;
207 ShmForSems(const ShmForSems&) = delete;
208
210 Sem* addSem(SemId semId);
211 bool removeSem(SemId semId);
213 void cleanup(PidSet* pidSet);
214
217 void addInterrupt(uint32_t interruptNumber);
218
222 std::list<Sem*> findSems(uint32_t interruptNumber = {}, bool update = false);
223
225 void print();
226
227 SemEntry semEntries[SHARED_MEMORY_N_MAX_MEMBER];
228 InterruptEntry interruptEntries[maxInterruptEntries];
229 };
230
231 struct InterruptDispatcherThread;
232
233 class InterruptDispatcherInterface {
234 public:
239 InterruptDispatcherInterface(SharedDummyBackend& backend, boost::interprocess::managed_shared_memory& shm,
240 boost::interprocess::named_mutex& shmMutex);
241
243 ~InterruptDispatcherInterface();
247 static void cleanupShm(boost::interprocess::managed_shared_memory& shm);
248 static void cleanupShm(boost::interprocess::managed_shared_memory& shm, PidSet* pidSet);
249
251 void triggerInterrupt(uint32_t intNumber);
252 boost::interprocess::named_mutex& _shmMutex;
253 SemId _semId;
254 ShmForSems* _semBuf;
255 boost::movelib::unique_ptr<InterruptDispatcherThread> _dispatcherThread;
256 SharedDummyBackend& _backend;
257 };
258
259 struct InterruptDispatcherThread {
261 explicit InterruptDispatcherThread(InterruptDispatcherInterface* dispatcherInterf);
262 InterruptDispatcherThread(const InterruptDispatcherThread&) = delete;
264 ~InterruptDispatcherThread();
265
266 void run();
267 void stop() noexcept;
269 void handleInterrupt(uint32_t interruptNumber);
270
271 private:
272 // plain pointer, because of cyclic dependency
273 InterruptDispatcherInterface* _dispatcherInterf;
274 SemId _semId;
275 ShmForSems* _semShm;
276 ShmForSems::Sem* _sem = nullptr;
277 // This must not be boost::thread since we don't want interruption points.
278 // The problem is that boost::thread::join() is an interruption point for the calling thread. The
279 // InterruptDispatcherThread does not rely on boost interruption points to control its tear down, it uses the
280 // _stop flag for this purpose.
281 std::thread _thr;
282 std::atomic_bool _started{false};
283 std::atomic_bool _stop{false};
284 };
285 };
286} // 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