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
43namespace 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*
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
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 */
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
Wrapper(const MemoryObjectMetaInfo &source)
MemoryObject * getMemory() const
Retrieve pointer to shared memory.
std::shared_ptr< SharedMemoryProvider< MemoryObject, MemoryObjectMetaInfo > > pointer_type
Pointer type for convenience.
SharedMemoryProvider(const std::string &newMemoryName, typename MemoryObjectMetaInfo::PointerType info)
Creates a shared memory provider.
int getCapacity() const
Retrieve size of usable shared memory.
~SharedMemoryProvider()
Destructs the shared memory provider.
SharedMemoryScopedReadLockPtr getScopedReadLock() const
Retrieve scoped lock to the shared memory for reading.
SharedMemoryScopedWriteLockPtr getScopedWriteLock() const
Retrieve scoped lock to the shared memory for writing.
int getSize() const
Retrieve size of actual used shared memory.
void unlock()
Unlock shared memory after writing.
void lock()
Lock shared memory for writing.
void setMetaInfo(const typename MemoryObjectMetaInfo::PointerType &info, bool threadSafe=false)
MemoryObjectMetaInfo * getMetaInfo() const
#define ARMARX_INFO_S
Definition Logging.h:202
#define ARMARX_WARNING_S
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:213
This file offers overloads of toIce() and fromIce() functions for STL container types.
std::shared_ptr< SharedMemoryScopedWriteLock > SharedMemoryScopedWriteLockPtr
std::shared_ptr< SharedMemoryScopedReadLock > SharedMemoryScopedReadLockPtr
boost::interprocess::sharable_lock< boost::interprocess::interprocess_upgradable_mutex > SharedMemoryScopedReadLock
boost::interprocess::scoped_lock< boost::interprocess::interprocess_upgradable_mutex > SharedMemoryScopedWriteLock