PLCnext API Documentation 24.0.0.71
SharedData.hxx
1#pragma once
3#include "Arp/System/Core/TypeName.hxx"
4#include "Arp/System/Core/AppDomainSingleton.hxx"
5#include "Arp/System/Commons/Ipc/SharedMemory.hpp"
6#include "Arp/System/Commons/Threading/Thread.hpp"
7#include "boost/interprocess/allocators/allocator.hpp"
8#include "boost/interprocess/containers/string.hpp"
9#include "boost/interprocess/containers/vector.hpp"
10#include "boost/interprocess/containers/list.hpp"
11#include "boost/interprocess/containers/map.hpp"
12#include "boost/interprocess/containers/set.hpp"
13#include <algorithm> // using std::replace
14#include <new> // using std::new(p)
15
16#ifdef ARP_PLATFORM_LINUX
17 #include <pthread.h>
18#endif
19
20namespace Arp { namespace System { namespace Commons { namespace Ipc
21{
22
23// This class uses CRTP (Curiously recurring template pattern) to implement an own SharedData singleton
24// for each derived class.
25// For CRTP see: https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern
26template<class Derived>
27class SharedData : public SharedMemory, public AppDomainSingleton<Derived>
28{
29public: // typedefs
30 using Base = SharedMemory;
31 using DerivedType = Derived;
33 using SharedMemory = boost::interprocess::managed_shared_memory;
34 using SegmentManager = SharedMemory::segment_manager;
35 using SharedMemoryPermission = boost::interprocess::permissions;
36
38 template <class T>
39 using Ptr = boost::interprocess::offset_ptr<T>;
40
41 template <class T>
42 using ConstPtr = boost::interprocess::offset_ptr<const T>;
43
44protected: // construction/destruction
45 SharedData(void); // opens an existing instance, throws if not exist
46 SharedData(size_t memorySize); // creates an non-existing instance, throws if yet exists
47 ~SharedData(void) = default;
48
49public: // static operations
50 ARP_CXX_SYMBOL_EXPORT
51 static void CreateInstance(size_t memorySize);
52 static void OpenInstance(void);
53 static void CloseInstance(bool removeMemory = false);
54 static void Remove(void);
55
56 //TODO(OR): remove this
57 // wait til shared data instance created and open it
58 static void WaitInstance(const char* waitMessage, uint32 timeout = -1);
59
60private: // deleted copying/assignment
61 SharedData(const SharedData& arg) = delete;
62 SharedData& operator=(const SharedData& arg) = delete;
63
64public: // nested type Allocator
65 template <class T>
66 class Allocator : public boost::interprocess::allocator<T, SegmentManager>
67 {
68 public: // typedef
69 using AllocatorBase = boost::interprocess::allocator<T, SegmentManager>;
70 using segment_manager = SegmentManager;
71 using value_type = typename AllocatorBase::value_type;
72 using pointer = typename AllocatorBase::pointer;
73 using const_pointer = typename AllocatorBase::const_pointer;
74 using void_pointer = typename AllocatorBase::void_pointer;
75 using reference = typename AllocatorBase::reference;
76 using const_reference = typename AllocatorBase::const_reference;
77 using size_type = typename AllocatorBase::size_type;
78 using difference_type = typename AllocatorBase::difference_type;
79 using version = typename AllocatorBase::version;
80
81 public: // construction
82 Allocator(void) : AllocatorBase(SharedData::GetInstance().sharedMemoryImpl.get_segment_manager()) {}
83
84 public: // meta-programming
85 template<class U>
86 struct rebind
87 {
88 typedef Allocator<U> other;
89 };
90 };
91 // declare Allocator as friend of this class
92 template<class T> friend class Allocator;
93
94public: // nested type Mutex
95#ifdef ARP_PLATFORM_LINUX
96 class Mutex
97 {
98 public: // construction
99 Mutex(void)
100 {
101 pthread_mutexattr_t attr;
102 pthread_mutexattr_init(&attr);
103
104 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
105 pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
106 pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT);
107
108 pthread_mutex_init(&this->mutex, &attr);
109
110 pthread_mutexattr_destroy(&attr);
111 }
112 Mutex(const Mutex&) = delete;
113 Mutex(Mutex&&) = delete;
114 Mutex& operator=(const Mutex&) = delete;
115 Mutex& operator=(Mutex&&) = delete;
116 ~Mutex(void)
117 {
118 pthread_mutex_destroy(&this->mutex);
119 }
120
121 public: // Operations
122 void Lock(void)
123 {
124 pthread_mutex_lock(&this->mutex);
125 }
126 bool TryLock(void)
127 {
128 return (pthread_mutex_trylock(&this->mutex) == 0);
129 }
130 void Unlock(void)
131 {
132 pthread_mutex_unlock(&this->mutex);
133 }
134
135 private: // fields
136 pthread_mutex_t mutex;
137 };
138#else
139 class Mutex
140 {
141 public: // construction
142 Mutex(void) = default;
143 ~Mutex(void) = default;
144
145 public: // Operations
146 void Lock(void)
147 {
148 this->mutex.lock();
149 }
150 bool TryLock(void)
151 {
152 return this->mutex.try_lock();
153 }
154 void Unlock(void)
155 {
156 this->mutex.unlock();
157 }
158
159 private: // fields
160 boost::interprocess::interprocess_mutex mutex;
161 };
162#endif
163
165 {
166 public: // construction
167 NullMutex(void) = default;
168 ~NullMutex(void) = default;
169
170 public: // Operations
171 void Lock(void) {}
172 void Unlock(void) {}
173 };
174
175public: // nested type ScopedLock
176 template <class TMutex>
178 {
179 public: // construction
180 ScopedLock(TMutex& mutex): pMutex(&mutex)
181 {
182 this->pMutex->Lock();
183 }
184 ScopedLock(const ScopedLock&) = delete;
185 ScopedLock(ScopedLock&&) = delete;
186 ScopedLock& operator=(const ScopedLock&) = delete;
187 ScopedLock& operator=(ScopedLock&&) = delete;
188
189 ~ScopedLock(void)
190 {
191 if (this->pMutex)
192 {
193 this->pMutex->Unlock();
194 }
195 }
196
197 private: // fields
198 TMutex* pMutex;
199 };
200
201public: // typedefs on nested types
202 // typedef shared string type
203 using String = boost::interprocess::basic_string<char, std::char_traits<char>, Allocator<char>>;
204 // typedef shared vector type
205 template <class T>
206 using vector = boost::interprocess::vector<T, Allocator<T>>;
207 // typedef shared list type
208 template <class T>
209 using list = boost::interprocess::list<T, Allocator<T>>;
210 // typedef shared map type
211 template <class TKey, class T, class TCompare = std::less<TKey>>
212 using map = boost::interprocess::map<TKey, T, TCompare, Allocator<std::pair<const TKey, T>>>;
213 // typedef shared set type
214 template <class T, class TCompare = std::less<T>>
215 using set = boost::interprocess::set<T, TCompare, Allocator<T>>;
216};
217
219// inline methods of class SharedData<Derived>
220template<class Derived>
222 : Base(TypeName<DerivedType>().GetSafeName())
223{
224}
225
226template<class Derived>
227inline SharedData<Derived>::SharedData(size_t memorySize)
228 : Base(TypeName<DerivedType>().GetSafeName(), memorySize)
229{
230}
231
232template<class Derived>
233void SharedData<Derived>::CreateInstance(size_t memorySize)
234{
236}
237
238// open an existing one
239template<class Derived>
240void SharedData<Derived>::OpenInstance()
241{
242 SingletonBase::CreateInstance(); // using open version when default ctor is called
243}
244
245//TODO(OR): remove this
246template<class Derived>
247void SharedData<Derived>::WaitInstance(const char* waitMessage, uint32 /* timeout */)
248{
250 {
251 return; // TODO: throw exception, double creation not allowed?
252 }
253 // else
254 bool instanceExists = false;
255 do
256 {
257 try
258 {
259 OpenInstance();
260 instanceExists = true;
261 }
262 catch (...)
263 {
264 Log::Debug(waitMessage);
266 }
267 // TODO: timeout handling
268 } while (!instanceExists);
269}
270
271template<class Derived>
272void SharedData<Derived>::CloseInstance(bool removeMemory)
273{
275 {
276 return;
277 }
278 // else
279 if (removeMemory && !SingletonBase::GetInstance().GetName().IsEmpty())
280 {
282 }
284}
285
286template<class Derived>
287void SharedData<Derived>::Remove()
288{
289 (void)boost::interprocess::shared_memory_object::remove(TypeName<DerivedType>().GetSafeName());
290}
291
293// inline methods of nested class SharedData<Derived>::Allocator<T>
294
295
296}}}} // end of namespace Arp::System::Commons::Ipc
This class implements the singleton pattern for singletons with process wide scope.
Definition: AppDomainSingleton.hxx:25
static Derived & GetInstance(void)
Gets a reference of the singleton instance.
Definition: AppDomainSingleton.hxx:107
static bool IsCreated(void)
Determines if this singleton instance is created yet.
Definition: AppDomainSingleton.hxx:96
static Derived & CreateInstance(Args &&... args)
Creates this singleton instance.
Definition: AppDomainSingleton.hxx:79
static void DisposeInstance(void)
Disposes this singleton instance.
Definition: AppDomainSingleton.hxx:130
Definition: SharedData.hxx:140
Definition: SharedData.hxx:28
SharedMemoryImpl sharedMemoryImpl
Actual implementation of the shared memory functionality.
Definition: SharedMemory.hpp:146
const Arp::String & GetName(void) const
Returns the name of the memory object.
Definition: SharedMemory.hpp:161
static void Sleep(size_t milliseconds)
Suspends the execution of the calling thread.
This (meta programming) class provides the C++ typename of the as template argument passed type.
Definition: TypeName.hxx:67
std::uint32_t uint32
The Arp unsigned integer type of 4 byte size.
Definition: PrimitiveTypes.hpp:36
@ System
System components used by the System, Device, Plc or Io domains.
Root namespace for the PLCnext API