PLCnext API Documentation  22.9.0.33
SharedData.hxx
1 #pragma once
2 #include "Arp/System/Core/Arp.h"
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 
20 namespace 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
26 template<class Derived>
27 class SharedData : public SharedMemory, public AppDomainSingleton<Derived>
28 {
29 public: // 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 
44 protected: // 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 
49 public: // 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 
60 private: // deleted copying/assignment
61  SharedData(const SharedData& arg) = delete;
62  SharedData& operator=(const SharedData& arg) = delete;
63 
64 public: // 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 
94 public: // 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  void Unlock(void)
127  {
128  pthread_mutex_unlock(&this->mutex);
129  }
130 
131  private: // fields
132  pthread_mutex_t mutex;
133  };
134 #else
135  class Mutex
136  {
137  public: // construction
138  Mutex(void) = default;
139  ~Mutex(void) = default;
140 
141  public: // Operations
142  void Lock(void)
143  {
144  this->mutex.lock();
145  }
146  void Unlock(void)
147  {
148  this->mutex.unlock();
149  }
150 
151  private: // fields
152  boost::interprocess::interprocess_mutex mutex;
153  };
154 #endif
155 
156  class NullMutex
157  {
158  public: // construction
159  NullMutex(void) = default;
160  ~NullMutex(void) = default;
161 
162  public: // Operations
163  void Lock(void) {}
164  void Unlock(void) {}
165  };
166 
167 public: // nested type ScopedLock
168  template <class TMutex>
170  {
171  public: // construction
172  ScopedLock(TMutex& mutex): pMutex(&mutex)
173  {
174  this->pMutex->Lock();
175  }
176  ScopedLock(const ScopedLock&) = delete;
177  ScopedLock(ScopedLock&&) = delete;
178  ScopedLock& operator=(const ScopedLock&) = delete;
179  ScopedLock& operator=(ScopedLock&&) = delete;
180 
181  ~ScopedLock(void)
182  {
183  if (this->pMutex)
184  {
185  this->pMutex->Unlock();
186  }
187  }
188 
189  private: // fields
190  TMutex* pMutex;
191  };
192 
193 public: // typedefs on nested types
194  // typedef shared string type
195  using String = boost::interprocess::basic_string<char, std::char_traits<char>, Allocator<char>>;
196  // typedef shared vector type
197  template <class T>
198  using vector = boost::interprocess::vector<T, Allocator<T>>;
199  // typedef shared list type
200  template <class T>
201  using list = boost::interprocess::list<T, Allocator<T>>;
202  // typedef shared map type
203  template <class TKey, class T, class TCompare = std::less<TKey>>
204  using map = boost::interprocess::map<TKey, T, TCompare, Allocator<std::pair<const TKey, T>>>;
205  // typedef shared set type
206  template <class T, class TCompare = std::less<T>>
207  using set = boost::interprocess::set<T, TCompare, Allocator<T>>;
208 };
209 
211 // inline methods of class SharedData<Derived>
212 template<class Derived>
214  : Base(TypeName<DerivedType>().GetSafeName())
215 {
216 }
217 
218 template<class Derived>
219 inline SharedData<Derived>::SharedData(size_t memorySize)
220  : Base(TypeName<DerivedType>().GetSafeName(), memorySize)
221 {
222 }
223 
224 template<class Derived>
225 void SharedData<Derived>::CreateInstance(size_t memorySize)
226 {
227  SingletonBase::CreateInstance(memorySize);
228 }
229 
230 // open an existing one
231 template<class Derived>
232 void SharedData<Derived>::OpenInstance()
233 {
234  SingletonBase::CreateInstance(); // using open version when default ctor is called
235 }
236 
237 //TODO(OR): remove this
238 template<class Derived>
239 void SharedData<Derived>::WaitInstance(const char* waitMessage, uint32 /* timeout */)
240 {
242  {
243  return; // TODO: throw exception, double creation not allowed?
244  }
245  // else
246  bool instanceExists = false;
247  do
248  {
249  try
250  {
251  OpenInstance();
252  instanceExists = true;
253  }
254  catch (...)
255  {
256  Log::Debug(waitMessage);
258  }
259  // TODO: timeout handling
260  } while (!instanceExists);
261 }
262 
263 template<class Derived>
264 void SharedData<Derived>::CloseInstance(bool removeMemory)
265 {
267  {
268  return;
269  }
270  // else
271  if (removeMemory && !SingletonBase::GetInstance().GetName().IsEmpty())
272  {
273  SingletonBase::GetInstance().Remove();
274  }
276 }
277 
278 template<class Derived>
279 void SharedData<Derived>::Remove()
280 {
281  (void)boost::interprocess::shared_memory_object::remove(TypeName<DerivedType>().GetSafeName());
282 }
283 
285 // inline methods of nested class SharedData<Derived>::Allocator<T>
286 
287 
288 }}}} // 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:102
static bool IsCreated(void)
Determines if this singleton instance is created yet.
Definition: AppDomainSingleton.hxx:91
static Derived & CreateInstance(Args &&... args)
Creates this singleton instance.
Definition: AppDomainSingleton.hxx:74
static void DisposeInstance(void)
Disposes this singleton instance.
Definition: AppDomainSingleton.hxx:119
Definition: SharedData.hxx:136
Definition: SharedData.hxx:28
SharedMemoryImpl sharedMemoryImpl
Actual implementation of the shared memory functionality.
Definition: SharedMemory.hpp:141
const Arp::String & GetName(void) const
Returns the name of the memory object.
Definition: SharedMemory.hpp:156
Mutual exclusion object to prevent data from concurrent modifications.
Definition: Mutex.hpp:26
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:56
std::uint32_t uint32
The Arp unsigned integer type of 4 byte size.
Definition: PrimitiveTypes.hpp:35
@ System
System components used by the System, Device, Plc or Io domains.
Root namespace for the PLCnext API