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_condition.hpp>
32 #include <boost/interprocess/sync/interprocess_upgradable_mutex.hpp>
33 #include <boost/interprocess/sync/sharable_lock.hpp>
34 #endif
35 
36 // exceptions
37 #include <cassert>
38 
41 #include <ArmarXCore/interface/core/SharedMemory.h>
42 
43 namespace armarx
44 {
46  boost::interprocess::scoped_lock<boost::interprocess::interprocess_upgradable_mutex>;
47  using SharedMemoryScopedWriteLockPtr = std::shared_ptr<SharedMemoryScopedWriteLock>;
49  boost::interprocess::sharable_lock<boost::interprocess::interprocess_upgradable_mutex>;
50  using SharedMemoryScopedReadLockPtr = std::shared_ptr<SharedMemoryScopedReadLock>;
51 
52  /**
53  \page SharedMemoryDoc ArmarX Shared Memory
54  The ArmarX shared memory provides unidirectional shared memory communication. Further, classes are provided which
55  allow unidirectional communication which either uses Ice or shared memory dependent on whether a local or
56  a remote connection is used.
57 
58  The API documentation can be found here: \ref SharedMemory
59 
60  \section SimpleSharedMemory Using simple shared memory
61  The simple shared memory mechanism provides mechanisms to share memory between a SharedMemoryProvider and a SharedMemoryConsumer.
62  Both provider and consumer can be running in different processes in the same machine. The SharedMemoryProvider needs to be
63  constructed before the SharedMemoryConsumer. Otherwise the SharedMemoryConsumer construction will fail. <br/>
64  Methods for synchronization are providing using either a scoped mechanism or lock / unlock methods.
65  \include SimpleSharedMemory.dox
66  \section IceSharedMemory Using combined Ice and shared memory transfer
67  The combined Ice and shared memory mechanism allows to exchange high amounts of data between two processes either
68  via shared memory or using Ice. Whether Ice or shared memory transfer is used is selected automatically using the
69  hardware id of the both machines involved in the communications. If the hardware ids of both sides are the same, shared
70  memory transfer is used. Otherwise Ice transfer is used. <br/>
71  In general, the Provider needs to be started using start() before start() of the component can be called. The correct
72  sequence is guaranteed when calling the constructors in onInitComponent() or equivalent and the start() methods in
73  onConnectComponent(). The sequence is achieve making use of the components dependency mechanism.
74 
75  A component that writes to the IceSharedMemory mechanism can be implemented in the following way:
76  \include IceSharedMemoryWriter.dox
77  In a similar manner, read access to the IceSharedMemory mechanism is provided:
78  \include IceSharedMemoryReader.dox
79 
80  \defgroup SharedMemory
81  \ingroup core-utility
82 
83  * \class SharedMemoryProvider
84  * \ingroup SharedMemory
85  * The SharedMemoryProvider creates a shared memory segment for writing. It provides synchronized write access to the
86  * shared data. The memory content is an array of objects, where the object type is specified as template parameter.
87  * The SharedMemoryProvider needs to be constructed, before a SharedMemoryConsumer can be created.
88  */
89  template <class MemoryObject, class MemoryObjectMetaInfo = MetaInfoSizeBase>
91  {
92  public:
93  class Wrapper : public MemoryObjectMetaInfo
94  {
95  public:
97  {
98  }
99 
100  Wrapper(const MemoryObjectMetaInfo& source) : MemoryObjectMetaInfo(source)
101  {
102  }
103  };
104 
105  /**
106  * Creates a shared memory provider.
107  *
108  * @param memoryName name of the memory for identification in the SharedMemoryConsumer
109  * @param numberElements number of elements of type MemoryObject stored in the memory
110  *
111  * @throw SharedMemoryException
112  */
113  SharedMemoryProvider(const std::string& newMemoryName,
114  typename MemoryObjectMetaInfo::PointerType info)
115  {
116 
117  // sanity check
118  if (info->capacity <= 0)
119  {
121  newMemoryName, "Invalic memory capacity. Capacity must be >0");
122  }
123 
124  this->memoryName = newMemoryName;
125  auto env_c_str = getenv("USER");
126  std::string userName = env_c_str ? env_c_str : "";
127 
128 
129  if (!userName.empty())
130  {
131  this->memoryName += userName;
132  }
133 
134  // remove memory segment.
135  // Remove will fail if memory is still in use by other process.
136  // Consequently memory is only removed when no process has access to the memory
137  boost::interprocess::shared_memory_object::remove(this->memoryName.c_str());
138 
139  // shared memory size = required + mutex memory + 1024 Bytes buffer for
140  // boost allocation algorithm
141 
142  std::size_t shmSize = info->capacity + sizeof(MemoryObjectMetaInfo) +
143  sizeof(boost::interprocess::interprocess_mutex) +
144  sizeof(boost::interprocess::interprocess_condition) + 1024;
145 
146  try
147  {
148  sharedMemorySegment = new boost::interprocess::managed_shared_memory(
149  boost::interprocess::open_or_create, memoryName.c_str(), shmSize);
150  }
151  catch (std::exception& e)
152  {
154  memoryName,
155  "Error creating shared memory segment. Still a consumer running?\nReason: ")
156  << e.what();
157  }
158 
159 
160  sharedMemorySegment->destroy<Wrapper>("Info");
161  this->info = sharedMemorySegment->find_or_construct<Wrapper>("Info")();
162 
163  if (!this->info)
164  {
166  memoryName, "Error constructing shared memory segment.");
167  }
168 
169  *this->info = *info;
170 
171  // get or create the shared image mutex
172  sharedMemorySegment->destroy<boost::interprocess::interprocess_upgradable_mutex>(
173  "SharedMutex");
174  memoryMutex =
175  sharedMemorySegment
176  ->find_or_construct<boost::interprocess::interprocess_upgradable_mutex>(
177  "SharedMutex")();
178 
179  if (!memoryMutex)
180  {
182  memoryName, "Error constructing SharedMutex in shared memory segment.");
183  }
184 
185  sharedMemorySegment->destroy<boost::interprocess::interprocess_condition>(
186  "CondSizeChanged");
187  condSizeChanged =
188  sharedMemorySegment->find_or_construct<boost::interprocess::interprocess_condition>(
189  "CondSizeChanged")();
190 
191  if (!condSizeChanged)
192  {
194  memoryName, "Error constructing condition variable in shared memory segment.");
195  }
196 
197  // create the managed image memory object
198  sharedMemorySegment->destroy<MemoryObject>("Data");
199  this->data =
200  sharedMemorySegment->find_or_construct<MemoryObject>("Data")[info->capacity]();
201 
202  if (!this->data)
203  {
205  memoryName, "Error constructing data in shared memory segment.");
206  }
207 
208 
209  ARMARX_INFO_S << "Created memory with name " << memoryName;
210  }
211 
212  /**
213  * Destructs the shared memory provider. Removes the shared memory object of not used by another process.
214  */
216  {
217  // remove memory segment
218  boost::interprocess::shared_memory_object::remove(memoryName.c_str());
219  }
220 
221  /**
222  * Retrieve pointer to shared memory. Use getScopedWriteLock() or lock() / unlock() before writing to the memory.
223  *
224  * @return pointer to the shared memory with type MemoryObject
225  */
226  MemoryObject*
227  getMemory() const
228  {
229  return data;
230  }
231 
232  MemoryObjectMetaInfo*
233  getMetaInfo() const
234  {
235  return info;
236  }
237 
238  void
239  setMetaInfo(const typename MemoryObjectMetaInfo::PointerType& info, bool threadSafe = false)
240  {
241  if (!info)
242  {
243  ARMARX_WARNING_S << "New meta info object is NULL - ignoring it";
244  return;
245  }
246 
248  if (threadSafe)
249  {
251  }
252 
253  if (info->capacity != this->info->capacity)
254  {
255  // todo resize
257  memoryName,
258  "capacity is not equal - changing not implemented yet. Old capacity: ")
259  << this->info->capacity << " new capacity: " << info->capacity;
260  }
261  else if (info->size > this->info->capacity)
262  {
264  memoryName, "object size exceeds capacity: size: ")
265  << info->size << " capacity: " << this->info->capacity;
266  }
267 
268  bool sizeChanged = (this->info->size != info->size);
269 
270  if (sizeChanged)
271  {
272  condSizeChanged->notify_all();
273  }
274 
275 
276  *this->info = *info;
277  }
278 
279  /**
280  * Retrieve size of actual used shared memory.
281  *
282  * @return size in bytes
283  */
284  int
285  getSize() const
286  {
287  return info->size;
288  }
289 
290  /**
291  * Retrieve size of usable shared memory.
292  *
293  * @return size in bytes
294  */
295  int
296  getCapacity() const
297  {
298  return info->capacity;
299  }
300 
301  /**
302  * Retrieve scoped lock to the shared memory for writing.
303  *
304  * @return scoped lock to shared memory
305  */
308  {
310 
311  if (memoryMutex)
312  {
313  lock.reset(new SharedMemoryScopedWriteLock(*memoryMutex));
314  }
315 
316  return lock;
317  }
318 
319  /**
320  * Retrieve scoped lock to the shared memory for reading.
321  *
322  * @return shared scoped lock to shared memory
323  */
326  {
328 
329  if (memoryMutex)
330  {
331  lock.reset(new SharedMemoryScopedReadLock(*memoryMutex));
332  }
333 
334  return lock;
335  }
336 
337  /**
338  * Lock shared memory for writing.
339  */
340  void
342  {
343  if (memoryMutex)
344  {
345  memoryMutex->lock();
346  }
347  }
348 
349  /**
350  * Unlock shared memory after writing.
351  */
352  void
354  {
355  if (memoryMutex)
356  {
357  memoryMutex->unlock();
358  }
359  }
360 
361  /**
362  * Pointer type for convenience.
363  */
364  using pointer_type =
365  std::shared_ptr<SharedMemoryProvider<MemoryObject, MemoryObjectMetaInfo>>;
366 
367  private:
368  /**
369  * Name of memory segment
370  */
371  std::string memoryName;
372 
373  /**
374  * Managed shared memory segment
375  */
376  boost::interprocess::managed_shared_memory* sharedMemorySegment;
377 
378  /**
379  * Upgradable data mutex
380  */
381  mutable boost::interprocess::interprocess_upgradable_mutex* memoryMutex;
382 
383  /**
384  * @brief condSizeChanged whether the size was changed
385  */
386  boost::interprocess::interprocess_condition* condSizeChanged;
387 
388  /**
389  * pointer to shared memory data
390  */
391  MemoryObject* data;
392 
393  MemoryObjectMetaInfo* info;
394  };
395 } // namespace armarx
armarx::SharedMemoryProvider::getSize
int getSize() const
Retrieve size of actual used shared memory.
Definition: SharedMemoryProvider.h:285
armarx::SharedMemoryProvider::~SharedMemoryProvider
~SharedMemoryProvider()
Destructs the shared memory provider.
Definition: SharedMemoryProvider.h:215
armarx::SharedMemoryProvider::unlock
void unlock()
Unlock shared memory after writing.
Definition: SharedMemoryProvider.h:353
armarx::SharedMemoryProvider::lock
void lock()
Lock shared memory for writing.
Definition: SharedMemoryProvider.h:341
armarx::SharedMemoryProvider::getScopedReadLock
SharedMemoryScopedReadLockPtr getScopedReadLock() const
Retrieve scoped lock to the shared memory for reading.
Definition: SharedMemoryProvider.h:325
armarx::SharedMemoryProvider::getCapacity
int getCapacity() const
Retrieve size of usable shared memory.
Definition: SharedMemoryProvider.h:296
armarx::SharedMemoryProvider::getScopedWriteLock
SharedMemoryScopedWriteLockPtr getScopedWriteLock() const
Retrieve scoped lock to the shared memory for writing.
Definition: SharedMemoryProvider.h:307
armarx::SharedMemoryProvider::getMetaInfo
MemoryObjectMetaInfo * getMetaInfo() const
Definition: SharedMemoryProvider.h:233
armarx::SharedMemoryProvider::Wrapper::Wrapper
Wrapper()
Definition: SharedMemoryProvider.h:96
armarx::SharedMemoryScopedWriteLockPtr
std::shared_ptr< SharedMemoryScopedWriteLock > SharedMemoryScopedWriteLockPtr
Definition: SharedMemoryProvider.h:47
armarx::SharedMemoryScopedReadLockPtr
std::shared_ptr< SharedMemoryScopedReadLock > SharedMemoryScopedReadLockPtr
Definition: SharedMemoryConsumer.h:45
armarx::exceptions::local::SharedMemoryException
Definition: SharedMemoryExceptions.h:33
armarx::SharedMemoryProvider::pointer_type
std::shared_ptr< SharedMemoryProvider< MemoryObject, MemoryObjectMetaInfo > > pointer_type
Pointer type for convenience.
Definition: SharedMemoryProvider.h:365
armarx::SharedMemoryProvider::setMetaInfo
void setMetaInfo(const typename MemoryObjectMetaInfo::PointerType &info, bool threadSafe=false)
Definition: SharedMemoryProvider.h:239
boost::source
Vertex source(const detail::edge_base< Directed, Vertex > &e, const PCG &)
Definition: point_cloud_graph.h:661
ARMARX_WARNING_S
#define ARMARX_WARNING_S
Definition: Logging.h:213
armarx::SharedMemoryScopedReadLock
boost::interprocess::sharable_lock< boost::interprocess::interprocess_upgradable_mutex > SharedMemoryScopedReadLock
Definition: SharedMemoryConsumer.h:44
armarx::SharedMemoryProvider
Definition: SharedMemoryProvider.h:90
armarx::SharedMemoryProvider::getMemory
MemoryObject * getMemory() const
Retrieve pointer to shared memory.
Definition: SharedMemoryProvider.h:227
SharedMemoryExceptions.h
ARMARX_INFO_S
#define ARMARX_INFO_S
Definition: Logging.h:202
Logging.h
armarx::SharedMemoryProvider::SharedMemoryProvider
SharedMemoryProvider(const std::string &newMemoryName, typename MemoryObjectMetaInfo::PointerType info)
Creates a shared memory provider.
Definition: SharedMemoryProvider.h:113
armarx::memoryMutex
boost::interprocess::interprocess_upgradable_mutex * memoryMutex
Definition: CMakePackageFinder.cpp:244
armarx::SharedMemoryScopedWriteLock
boost::interprocess::scoped_lock< boost::interprocess::interprocess_upgradable_mutex > SharedMemoryScopedWriteLock
Definition: SharedMemoryProvider.h:46
armarx
This file offers overloads of toIce() and fromIce() functions for STL container types.
Definition: ArmarXTimeserver.cpp:27
armarx::SharedMemoryProvider::Wrapper
Definition: SharedMemoryProvider.h:93
armarx::SharedMemoryProvider::Wrapper::Wrapper
Wrapper(const MemoryObjectMetaInfo &source)
Definition: SharedMemoryProvider.h:100