SharedMemoryProvider.h
Go to the documentation of this file.
1 /*
2  * This file is part of ArmarX.
3  *
4  * Copyright (C) 2011-2016, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved.
5  *
6  * ArmarX is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  *
10  * ArmarX is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  *
18  * @package VisionX::Core
19  * @author Kai Welke (kai dot welke at kit dot edu)
20  * @date 2012
21  * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
22  * GNU General Public License
23  */
24 
25 #pragma once
26 
27 // boost
28 #include <memory>
29 #ifndef Q_MOC_RUN
30 #include <boost/interprocess/managed_shared_memory.hpp>
31 #include <boost/interprocess/sync/interprocess_upgradable_mutex.hpp>
32 #include <boost/interprocess/sync/sharable_lock.hpp>
33 #include <boost/interprocess/sync/interprocess_condition.hpp>
34 #endif
35 
36 // exceptions
39 #include <ArmarXCore/interface/core/SharedMemory.h>
40 
41 #include <cassert>
42 
43 namespace armarx
44 {
45  using SharedMemoryScopedWriteLock = boost::interprocess::scoped_lock <boost::interprocess::interprocess_upgradable_mutex> ;
46  using SharedMemoryScopedWriteLockPtr = std::shared_ptr<SharedMemoryScopedWriteLock>;
47  using SharedMemoryScopedReadLock = boost::interprocess::sharable_lock <boost::interprocess::interprocess_upgradable_mutex> ;
48  using SharedMemoryScopedReadLockPtr = std::shared_ptr<SharedMemoryScopedReadLock>;
49 
50  /**
51  \page SharedMemoryDoc ArmarX Shared Memory
52  The ArmarX shared memory provides unidirectional shared memory communication. Further, classes are provided which
53  allow unidirectional communication which either uses Ice or shared memory dependent on whether a local or
54  a remote connection is used.
55 
56  The API documentation can be found here: \ref SharedMemory
57 
58  \section SimpleSharedMemory Using simple shared memory
59  The simple shared memory mechanism provides mechanisms to share memory between a SharedMemoryProvider and a SharedMemoryConsumer.
60  Both provider and consumer can be running in different processes in the same machine. The SharedMemoryProvider needs to be
61  constructed before the SharedMemoryConsumer. Otherwise the SharedMemoryConsumer construction will fail. <br/>
62  Methods for synchronization are providing using either a scoped mechanism or lock / unlock methods.
63  \include SimpleSharedMemory.dox
64  \section IceSharedMemory Using combined Ice and shared memory transfer
65  The combined Ice and shared memory mechanism allows to exchange high amounts of data between two processes either
66  via shared memory or using Ice. Whether Ice or shared memory transfer is used is selected automatically using the
67  hardware id of the both machines involved in the communications. If the hardware ids of both sides are the same, shared
68  memory transfer is used. Otherwise Ice transfer is used. <br/>
69  In general, the Provider needs to be started using start() before start() of the component can be called. The correct
70  sequence is guaranteed when calling the constructors in onInitComponent() or equivalent and the start() methods in
71  onConnectComponent(). The sequence is achieve making use of the components dependency mechanism.
72 
73  A component that writes to the IceSharedMemory mechanism can be implemented in the following way:
74  \include IceSharedMemoryWriter.dox
75  In a similar manner, read access to the IceSharedMemory mechanism is provided:
76  \include IceSharedMemoryReader.dox
77 
78  \defgroup SharedMemory
79  \ingroup core-utility
80 
81  * \class SharedMemoryProvider
82  * \ingroup SharedMemory
83  * The SharedMemoryProvider creates a shared memory segment for writing. It provides synchronized write access to the
84  * shared data. The memory content is an array of objects, where the object type is specified as template parameter.
85  * The SharedMemoryProvider needs to be constructed, before a SharedMemoryConsumer can be created.
86  */
87  template <class MemoryObject, class MemoryObjectMetaInfo = MetaInfoSizeBase>
89  {
90  public:
91  class Wrapper : public MemoryObjectMetaInfo
92  {
93  public:
94  Wrapper() {}
95  Wrapper(const MemoryObjectMetaInfo& source) : MemoryObjectMetaInfo(source) {}
96  };
97 
98  /**
99  * Creates a shared memory provider.
100  *
101  * @param memoryName name of the memory for identification in the SharedMemoryConsumer
102  * @param numberElements number of elements of type MemoryObject stored in the memory
103  *
104  * @throw SharedMemoryException
105  */
106  SharedMemoryProvider(const std::string& newMemoryName, typename MemoryObjectMetaInfo::PointerType info)
107  {
108 
109  // sanity check
110  if (info->capacity <= 0)
111  {
112  throw exceptions::local::SharedMemoryException(newMemoryName, "Invalic memory capacity. Capacity must be >0");
113  }
114 
115  this->memoryName = newMemoryName;
116  auto env_c_str = getenv("USER");
117  std::string userName = env_c_str ? env_c_str : "";
118 
119 
120  if (!userName.empty())
121  {
122  this->memoryName += userName;
123  }
124 
125  // remove memory segment.
126  // Remove will fail if memory is still in use by other process.
127  // Consequently memory is only removed when no process has access to the memory
128  boost::interprocess::shared_memory_object::remove(this->memoryName.c_str());
129 
130  // shared memory size = required + mutex memory + 1024 Bytes buffer for
131  // boost allocation algorithm
132 
133  std::size_t shmSize = info->capacity
134  + sizeof(MemoryObjectMetaInfo)
135  + sizeof(boost::interprocess::interprocess_mutex)
136  + sizeof(boost::interprocess::interprocess_condition)
137  + 1024;
138 
139  try
140  {
141  sharedMemorySegment = new boost::interprocess::managed_shared_memory(boost::interprocess::open_or_create,
142  memoryName.c_str(),
143  shmSize);
144  }
145  catch (std::exception& e)
146  {
147  throw exceptions::local::SharedMemoryException(memoryName, "Error creating shared memory segment. Still a consumer running?\nReason: ") << e.what();
148  }
149 
150 
151  sharedMemorySegment->destroy<Wrapper>("Info");
152  this->info = sharedMemorySegment->find_or_construct<Wrapper>("Info")();
153 
154  if (!this->info)
155  {
156  throw exceptions::local::SharedMemoryException(memoryName, "Error constructing shared memory segment.");
157  }
158 
159  *this->info = *info;
160 
161  // get or create the shared image mutex
162  sharedMemorySegment->destroy<boost::interprocess::interprocess_upgradable_mutex>("SharedMutex");
163  memoryMutex = sharedMemorySegment->find_or_construct<boost::interprocess::interprocess_upgradable_mutex>("SharedMutex")();
164 
165  if (!memoryMutex)
166  {
167  throw exceptions::local::SharedMemoryException(memoryName, "Error constructing SharedMutex in shared memory segment.");
168  }
169 
170  sharedMemorySegment->destroy<boost::interprocess::interprocess_condition>("CondSizeChanged");
171  condSizeChanged = sharedMemorySegment->find_or_construct<boost::interprocess::interprocess_condition>("CondSizeChanged")();
172 
173  if (!condSizeChanged)
174  {
175  throw exceptions::local::SharedMemoryException(memoryName, "Error constructing condition variable in shared memory segment.");
176  }
177 
178  // create the managed image memory object
179  sharedMemorySegment->destroy<MemoryObject>("Data");
180  this->data = sharedMemorySegment->find_or_construct<MemoryObject>("Data")[info->capacity]();
181 
182  if (!this->data)
183  {
184  throw exceptions::local::SharedMemoryException(memoryName, "Error constructing data in shared memory segment.");
185  }
186 
187 
188  ARMARX_INFO_S << "Created memory with name " << memoryName;
189  }
190 
191 
192  /**
193  * Destructs the shared memory provider. Removes the shared memory object of not used by another process.
194  */
196  {
197  // remove memory segment
198  boost::interprocess::shared_memory_object::remove(memoryName.c_str());
199  }
200 
201  /**
202  * Retrieve pointer to shared memory. Use getScopedWriteLock() or lock() / unlock() before writing to the memory.
203  *
204  * @return pointer to the shared memory with type MemoryObject
205  */
206  MemoryObject* getMemory() const
207  {
208  return data;
209  }
210 
211  MemoryObjectMetaInfo* getMetaInfo() const
212  {
213  return info;
214  }
215 
216 
217  void setMetaInfo(const typename MemoryObjectMetaInfo::PointerType& info, bool threadSafe = false)
218  {
219  if (!info)
220  {
221  ARMARX_WARNING_S << "New meta info object is NULL - ignoring it";
222  return;
223  }
224 
226  if (threadSafe)
227  {
229  }
230 
231  if (info->capacity != this->info->capacity)
232  {
233  // todo resize
234  throw exceptions::local::SharedMemoryException(memoryName, "capacity is not equal - changing not implemented yet. Old capacity: ") << this->info->capacity << " new capacity: " << info->capacity;
235  }
236  else if (info->size > this->info->capacity)
237  {
238  throw exceptions::local::SharedMemoryException(memoryName, "object size exceeds capacity: size: ") << info->size << " capacity: " << this->info->capacity;
239  }
240 
241  bool sizeChanged = (this->info->size != info->size);
242 
243  if (sizeChanged)
244  {
245  condSizeChanged->notify_all();
246  }
247 
248 
249  *this->info = *info;
250  }
251 
252 
253 
254  /**
255  * Retrieve size of actual used shared memory.
256  *
257  * @return size in bytes
258  */
259  int getSize() const
260  {
261  return info->size;
262  }
263 
264 
265  /**
266  * Retrieve size of usable shared memory.
267  *
268  * @return size in bytes
269  */
270  int getCapacity() const
271  {
272  return info->capacity;
273  }
274 
275 
276  /**
277  * Retrieve scoped lock to the shared memory for writing.
278  *
279  * @return scoped lock to shared memory
280  */
282  {
284 
285  if (memoryMutex)
286  {
287  lock.reset(new SharedMemoryScopedWriteLock(*memoryMutex));
288  }
289 
290  return lock;
291  }
292 
293  /**
294  * Retrieve scoped lock to the shared memory for reading.
295  *
296  * @return shared scoped lock to shared memory
297  */
299  {
301 
302  if (memoryMutex)
303  {
304  lock.reset(new SharedMemoryScopedReadLock(*memoryMutex));
305  }
306 
307  return lock;
308  }
309 
310  /**
311  * Lock shared memory for writing.
312  */
313  void lock()
314  {
315  if (memoryMutex)
316  {
317  memoryMutex->lock();
318  }
319  }
320 
321  /**
322  * Unlock shared memory after writing.
323  */
324  void unlock()
325  {
326  if (memoryMutex)
327  {
328  memoryMutex->unlock();
329  }
330  }
331 
332  /**
333  * Pointer type for convenience.
334  */
335  using pointer_type = std::shared_ptr<SharedMemoryProvider<MemoryObject, MemoryObjectMetaInfo> >;
336 
337  private:
338  /**
339  * Name of memory segment
340  */
341  std::string memoryName;
342 
343  /**
344  * Managed shared memory segment
345  */
346  boost::interprocess::managed_shared_memory* sharedMemorySegment;
347 
348  /**
349  * Upgradable data mutex
350  */
351  mutable boost::interprocess::interprocess_upgradable_mutex* memoryMutex;
352 
353  /**
354  * @brief condSizeChanged whether the size was changed
355  */
356  boost::interprocess::interprocess_condition* condSizeChanged;
357 
358  /**
359  * pointer to shared memory data
360  */
361  MemoryObject* data;
362 
363  MemoryObjectMetaInfo* info;
364  };
365 }
366 
armarx::SharedMemoryProvider::getSize
int getSize() const
Retrieve size of actual used shared memory.
Definition: SharedMemoryProvider.h:259
armarx::SharedMemoryProvider::~SharedMemoryProvider
~SharedMemoryProvider()
Destructs the shared memory provider.
Definition: SharedMemoryProvider.h:195
armarx::SharedMemoryProvider::unlock
void unlock()
Unlock shared memory after writing.
Definition: SharedMemoryProvider.h:324
armarx::SharedMemoryProvider::lock
void lock()
Lock shared memory for writing.
Definition: SharedMemoryProvider.h:313
armarx::SharedMemoryProvider::getScopedReadLock
SharedMemoryScopedReadLockPtr getScopedReadLock() const
Retrieve scoped lock to the shared memory for reading.
Definition: SharedMemoryProvider.h:298
armarx::SharedMemoryProvider::getCapacity
int getCapacity() const
Retrieve size of usable shared memory.
Definition: SharedMemoryProvider.h:270
armarx::SharedMemoryProvider::getScopedWriteLock
SharedMemoryScopedWriteLockPtr getScopedWriteLock() const
Retrieve scoped lock to the shared memory for writing.
Definition: SharedMemoryProvider.h:281
armarx::SharedMemoryProvider::getMetaInfo
MemoryObjectMetaInfo * getMetaInfo() const
Definition: SharedMemoryProvider.h:211
armarx::SharedMemoryProvider::Wrapper::Wrapper
Wrapper()
Definition: SharedMemoryProvider.h:94
armarx::SharedMemoryScopedWriteLockPtr
std::shared_ptr< SharedMemoryScopedWriteLock > SharedMemoryScopedWriteLockPtr
Definition: SharedMemoryProvider.h:46
armarx::SharedMemoryScopedReadLockPtr
std::shared_ptr< SharedMemoryScopedReadLock > SharedMemoryScopedReadLockPtr
Definition: SharedMemoryConsumer.h:43
armarx::exceptions::local::SharedMemoryException
Definition: SharedMemoryExceptions.h:33
armarx::SharedMemoryProvider< unsigned char, MetaPointCloudFormat >::pointer_type
std::shared_ptr< SharedMemoryProvider< unsigned char, MetaPointCloudFormat > > pointer_type
Pointer type for convenience.
Definition: SharedMemoryProvider.h:335
armarx::SharedMemoryProvider::setMetaInfo
void setMetaInfo(const typename MemoryObjectMetaInfo::PointerType &info, bool threadSafe=false)
Definition: SharedMemoryProvider.h:217
boost::source
Vertex source(const detail::edge_base< Directed, Vertex > &e, const PCG &)
Definition: point_cloud_graph.h:681
ARMARX_WARNING_S
#define ARMARX_WARNING_S
Definition: Logging.h:206
armarx::SharedMemoryScopedReadLock
boost::interprocess::sharable_lock< boost::interprocess::interprocess_upgradable_mutex > SharedMemoryScopedReadLock
Definition: SharedMemoryConsumer.h:42
armarx::SharedMemoryScopedWriteLock
boost::interprocess::scoped_lock< boost::interprocess::interprocess_upgradable_mutex > SharedMemoryScopedWriteLock
Definition: SharedMemoryProvider.h:45
armarx::SharedMemoryProvider
Definition: SharedMemoryProvider.h:88
armarx::SharedMemoryProvider::getMemory
MemoryObject * getMemory() const
Retrieve pointer to shared memory.
Definition: SharedMemoryProvider.h:206
SharedMemoryExceptions.h
ARMARX_INFO_S
#define ARMARX_INFO_S
Definition: Logging.h:195
Logging.h
armarx::SharedMemoryProvider::SharedMemoryProvider
SharedMemoryProvider(const std::string &newMemoryName, typename MemoryObjectMetaInfo::PointerType info)
Creates a shared memory provider.
Definition: SharedMemoryProvider.h:106
armarx::memoryMutex
boost::interprocess::interprocess_upgradable_mutex * memoryMutex
Definition: CMakePackageFinder.cpp:230
armarx
This file offers overloads of toIce() and fromIce() functions for STL container types.
Definition: ArmarXTimeserver.cpp:28
armarx::SharedMemoryProvider::Wrapper
Definition: SharedMemoryProvider.h:91
armarx::SharedMemoryProvider::Wrapper::Wrapper
Wrapper(const MemoryObjectMetaInfo &source)
Definition: SharedMemoryProvider.h:95