TripleBuffer.h
Go to the documentation of this file.
1 // Modified by Raphael Grimm, 2017
2 // changed interface / renamed functions
3 //
4 // Modified by Adrian Gierakowski, 2014
5 //
6 // added (const T&) constructor to enable custom buffer initialisation
7 // added getReadRef and getWriteRef to enable by reference access to
8 // objects contained in the buffer
9 //
10 // minore code cleanup
11 //
12 //
13 // ORIGINAL COPYRIGHT NOTICE
14 //==============================================================================
15 // Name : TripleBuffer.hxx
16 // Author : AndrĂ© Pacheco Neves
17 // Version : 1.0 (27/01/13)
18 // Copyright : Copyright (c) 2013, AndrĂ© Pacheco Neves
19 // All rights reserved.
20 
21 /*
22 Redistribution and use in source and binary forms, with or without
23 modification, are permitted provided that the following conditions are met:
24 - Redistributions of source code must retain the above copyright
25 notice, this list of conditions and the following disclaimer.
26 - Redistributions in binary form must reproduce the above copyright
27 notice, this list of conditions and the following disclaimer in the
28 documentation and/or other materials provided with the distribution.
29 - Neither the name of the <organization> nor the
30 names of its contributors may be used to endorse or promote products
31 derived from this software without specific prior written permission.
32 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
33 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
34 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
35 DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
36 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
37 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
38 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
39 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
40 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
41 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42 */
43 
44 // Description :
45 // Template class for a TripleBuffer as a concurrency mechanism,
46 // using atomic operations
47 // Credits :
48 // http://remis-thoughts.blogspot.pt/2012/01/triple-buffering-as-concurrency_30.html
49 //==============================================================================
50 
51 #pragma once
52 
53 #include <atomic>
54 #include <iostream>
55 #include <type_traits>
56 
57 #include <SimoxUtility/threads/system_thread_id.h>
58 
59 namespace armarx
60 {
61  /**
62  * @brief A simple triple buffer for lockfree comunication between a single writer and a single reader.
63  *
64  * If more than one reader/writer is required, you have to synchronize the access.
65  * Writers must not have: writes to the same data cell (except it is an atomic et al) and no writes are allowed during swapping of the buffer.
66  * Readers must not read when the buffer is swapped.
67  * If a writer only writes parts of the data structure, data loss could occure! (use a WriteBufferedTripleBuffer instead)
68  *
69  */
70  template <typename T>
72  {
73  public:
74 #define DEBUG_MODE_PRINT \
75  if (debugMode.load()) \
76  std::cout << '[' << simox::system_thread_id() << "] [" << this << "] "
77 
78  TripleBuffer(T&& initR, T&& initH, T&& initW)
79  {
80  buffer[getReadBufferIndex()] = std::move(initR);
81  buffer[getHiddenBufferIndex()] = std::move(initH);
82  buffer[getWriteBufferIndex()] = std::move(initW);
83  }
84 
85  template <class... Ts>
86  TripleBuffer(const Ts&... ts) : buffer{T(ts...), T(ts...), T(ts...)}
87  {
88  }
89 
90  /// @return the write buffer
91  T&
93  {
94  DEBUG_MODE_PRINT << "access write buffer\n";
95  return buffer[getWriteBufferIndex()];
96  }
97 
98  /// @return the write buffer
99  const T&
101  {
102  DEBUG_MODE_PRINT << "access write buffer\n";
103  return buffer[getWriteBufferIndex()];
104  }
105 
106  /// @return the current read buffer
107  const T&
109  {
110  DEBUG_MODE_PRINT << "access read buffer\n";
111  return buffer[getReadBufferIndex()];
112  }
113 
114  /// @return the most up to date read buffer
115  const T&
117  {
119  return getReadBuffer();
120  }
121 
122  /// @return the current read buffer (sometimes required for more complex initialization)
123  T&
125  {
126  DEBUG_MODE_PRINT << "access non const read buffer\n";
127  return buffer[getReadBufferIndex()];
128  }
129 
130  /// @return the current read buffer (sometimes required for more complex initialization)
131  T&
133  {
134  DEBUG_MODE_PRINT << "access non const hidden buffer\n";
135  return buffer[getHiddenBufferIndex()];
136  }
137 
138  /**
139  * @brief Swaps in the hidden buffer if it contains new data.
140  * @return True if new data is available
141  */
142  bool
144  {
145  uint_fast8_t flagsNow(flags.load(std::memory_order_consume));
146 
147  if (!hasNewWrite(flagsNow))
148  {
149  DEBUG_MODE_PRINT << "update read buffer. no update available\n";
150  return false;
151  }
152 
153  while (!flags.compare_exchange_weak(flagsNow,
154  flagSwapReadWithHidden(flagsNow),
155  std::memory_order_release,
156  std::memory_order_consume))
157  ;
158  DEBUG_MODE_PRINT << "update read buffer. got update\n";
159  return true;
160  }
161 
162  void
164  {
165  DEBUG_MODE_PRINT << "commit write buffer.\n";
167  }
168 
169  template <class U = T>
171  reinitAllBuffers(const T& init)
172  {
173  buffer[0] = init;
174  buffer[1] = init;
175  buffer[2] = init;
176  }
177 
178  void
179  reinitAllBuffers(T&& writeBuff, T&& hiddenBuff, T&& readBuff)
180  {
181  buffer[getWriteBufferIndex()] = std::move(writeBuff);
182  buffer[getHiddenBufferIndex()] = std::move(hiddenBuff);
183  buffer[getReadBufferIndex()] = std::move(readBuff);
184  }
185 
186  void
187  setDebugMode(bool mode)
188  {
189  std::cout << '[' << simox::system_thread_id() << "] [0x" << this
190  << "] set debug mode to " << mode << '\n';
191  debugMode = mode;
192  }
193 
194 #undef DEBUG_MODE_PRINT
195  protected:
196  // non-copyable behavior
197  TripleBuffer(const TripleBuffer&) = delete;
198  TripleBuffer& operator=(const TripleBuffer&) = delete;
199 
200  // /////////////////////////////////////////////////////////////////////////////// //
201  // get indices
202  uint_fast8_t
204  {
205  return (flags.load(std::memory_order_consume) & readBufferIndexMask) >>
207  }
208 
209  uint_fast8_t
211  {
212  return (flags.load(std::memory_order_consume) & writeBufferIndexMask) >>
214  }
215 
216  uint_fast8_t
218  {
219  return (flags.load(std::memory_order_consume) & hiddenBufferIndexMask) >>
221  }
222 
223  // /////////////////////////////////////////////////////////////////////////////// //
224  //buffer operations
225  /// @brief Swap the write buffer with the hidden buffer
226  void
228  {
229  uint_fast8_t flagsNow(flags.load(std::memory_order_consume));
230  while (!flags.compare_exchange_weak(flagsNow,
231  flagSwapWriteWithHidden(flagsNow),
232  std::memory_order_release,
233  std::memory_order_consume))
234  ;
235  }
236 
237  // void swapReadAndHiddenBuffer(); is done by updateReadBuffer()
238 
239  // /////////////////////////////////////////////////////////////////////////////// //
240  //flag opertions
241  // check if the newWrite bit is 1
242  bool
243  hasNewWrite(uint_fast8_t flags) const
244  {
245  return flags & dirtyBitMask;
246  }
247 
248  /// swap read and hidden indexes
249  /**
250  * @brief swap read and hidden indexes of given flags (set dirty to 0)
251  * @param flags the current flags
252  * @return the new flags
253  */
254  static uint_fast8_t
256  {
257  return 0 //set dirty bit to 0
258  | ((flags & hiddenBufferIndexMask) >>
259  hiddenToReadShift) // hidden index now is read index
261  << hiddenToReadShift) // read index now is hidden index
262  | (flags & writeBufferIndexMask); // keep write index
263  }
264 
265  /**
266  * @brief swap write and hidden indexes of given flags (set dirty to 1)
267  * @param flags the current flags
268  * @return the new flags
269  */
270  static uint_fast8_t
272  {
273  return dirtyBitMask //set dirty bit to 1
275  << hiddenToWriteShift) // hidden index now is write index
276  | ((flags & writeBufferIndexMask) >>
277  hiddenToWriteShift) // write index now is hidden index
278  | (flags & readBufferIndexMask); // keep read index
279  }
280 
281  // /////////////////////////////////////////////////////////////////////////////// //
282  // constants
283  static const uint_fast8_t readBufferIndexShift = 0;
284  static const uint_fast8_t hiddenBufferIndexShift = 2;
285  static const uint_fast8_t writeBufferIndexShift = 4;
286  static const uint_fast8_t dirtyBitShift = 7;
287 
289  static const uint_fast8_t hiddenToWriteShift =
291 
292  static const uint_fast8_t readBufferIndexMask = 3 << readBufferIndexShift; //0b00000011;
293  static const uint_fast8_t hiddenBufferIndexMask = 3 << hiddenBufferIndexShift; //0b00001100;
294  static const uint_fast8_t writeBufferIndexMask = 3 << writeBufferIndexShift; //0b00110000;
295  static const uint_fast8_t dirtyBitMask = 1 << dirtyBitShift; //0b10000000;
296  // initially dirty = 0, write 0, hidden = 1, read = 2
297  static const uint_fast8_t initialFlags = 2 + (1 << 2); //0b00000110;
298 
299  // /////////////////////////////////////////////////////////////////////////////// //
300  // data
301  mutable std::atomic_uint_fast8_t flags{initialFlags};
302  T buffer[3];
303  std::atomic_bool debugMode{false};
304  };
305 
306  /**
307  * @see TripleBuffer
308  * @brief Same as the TripleBuffer, but partial writes of the data structure are ok.
309  * The write operation may be a bit slower and memory consumption may be 1/3 higher.
310  */
311  template <typename T>
313  {
314  public:
315  WriteBufferedTripleBuffer() : writeBuffer(T())
316  {
317  }
318 
319  WriteBufferedTripleBuffer(const T& init) : tripleBuffer{init}, writeBuffer(init)
320  {
321  }
322 
323  /// @return the write buffer
324  T&
326  {
327  return writeBuffer;
328  }
329 
330  /// @return the write buffer
331  const T&
333  {
334  return writeBuffer;
335  }
336 
337  /// @return the current read buffer
338  const T&
340  {
341  return tripleBuffer.getReadBuffer();
342  }
343 
344  /// @return the most up to date read buffer
345  const T&
347  {
348  return tripleBuffer.getUpToDateReadBuffer();
349  }
350 
351  /// @return the current read buffer (sometimes required for more complex initialization)
352  T&
354  {
355  return tripleBuffer._getNonConstReadBuffer();
356  }
357 
358  /// @return the current read buffer (sometimes required for more complex initialization)
359  T&
361  {
362  return tripleBuffer._getNonConstHiddenBuffer();
363  }
364 
365  /// @return the current read buffer (sometimes required for more complex initialization)
366  T&
368  {
369  return tripleBuffer.getReadBuffer();
370  }
371 
372  /**
373  * @brief Swaps in the hidden buffer if it contains new data.
374  * @return True if new data is available
375  */
376  bool
378  {
379  return tripleBuffer.updateReadBuffer();
380  }
381 
382  void
384  {
385  tripleBuffer.getWriteBuffer() = writeBuffer;
386  tripleBuffer.commitWrite();
387  }
388 
389  void
390  reinitAllBuffers(const T& init)
391  {
392  writeBuffer = init;
393  tripleBuffer.reinitAllBuffers(init);
394  }
395 
396  void
397  setDebugMode(bool mode)
398  {
399  tripleBuffer.setDebugMode(mode);
400  }
401 
402  private:
403  TripleBuffer<T> tripleBuffer;
404  T writeBuffer;
405  };
406 } // namespace armarx
armarx::TripleBuffer::flagSwapWriteWithHidden
static uint_fast8_t flagSwapWriteWithHidden(uint_fast8_t flags)
swap write and hidden indexes of given flags (set dirty to 1)
Definition: TripleBuffer.h:271
armarx::TripleBuffer::getWriteBuffer
T & getWriteBuffer()
Definition: TripleBuffer.h:92
DEBUG_MODE_PRINT
#define DEBUG_MODE_PRINT
Definition: TripleBuffer.h:74
armarx::TripleBuffer::getWriteBufferIndex
uint_fast8_t getWriteBufferIndex() const
Definition: TripleBuffer.h:210
armarx::TripleBuffer::writeBufferIndexShift
static const uint_fast8_t writeBufferIndexShift
Definition: TripleBuffer.h:285
armarx::TripleBuffer::readBufferIndexShift
static const uint_fast8_t readBufferIndexShift
Definition: TripleBuffer.h:283
armarx::TripleBuffer::writeBufferIndexMask
static const uint_fast8_t writeBufferIndexMask
Definition: TripleBuffer.h:294
armarx::TripleBuffer::getHiddenBufferIndex
uint_fast8_t getHiddenBufferIndex() const
Definition: TripleBuffer.h:217
armarx::TripleBuffer::commitWrite
void commitWrite()
Definition: TripleBuffer.h:163
armarx::WriteBufferedTripleBuffer::commitWrite
void commitWrite()
Definition: TripleBuffer.h:383
armarx::WriteBufferedTripleBuffer::WriteBufferedTripleBuffer
WriteBufferedTripleBuffer(const T &init)
Definition: TripleBuffer.h:319
armarx::TripleBuffer::initialFlags
static const uint_fast8_t initialFlags
Definition: TripleBuffer.h:297
armarx::WriteBufferedTripleBuffer::_getNonConstHiddenBuffer
T & _getNonConstHiddenBuffer()
Definition: TripleBuffer.h:360
armarx::TripleBuffer::hasNewWrite
bool hasNewWrite(uint_fast8_t flags) const
Definition: TripleBuffer.h:243
armarx::WriteBufferedTripleBuffer::updateReadBuffer
bool updateReadBuffer() const
Swaps in the hidden buffer if it contains new data.
Definition: TripleBuffer.h:377
armarx::TripleBuffer::getReadBufferIndex
uint_fast8_t getReadBufferIndex() const
Definition: TripleBuffer.h:203
armarx::TripleBuffer::dirtyBitShift
static const uint_fast8_t dirtyBitShift
Definition: TripleBuffer.h:286
armarx::TripleBuffer::getWriteBuffer
const T & getWriteBuffer() const
Definition: TripleBuffer.h:100
armarx::TripleBuffer::setDebugMode
void setDebugMode(bool mode)
Definition: TripleBuffer.h:187
armarx::WriteBufferedTripleBuffer::setDebugMode
void setDebugMode(bool mode)
Definition: TripleBuffer.h:397
armarx::WriteBufferedTripleBuffer::_getNonConstHiddenWriteBuffer
T & _getNonConstHiddenWriteBuffer()
Definition: TripleBuffer.h:367
armarx::TripleBuffer::hiddenToReadShift
static const uint_fast8_t hiddenToReadShift
Definition: TripleBuffer.h:288
cxxopts::value
std::shared_ptr< Value > value()
Definition: cxxopts.hpp:855
armarx::TripleBuffer::reinitAllBuffers
void reinitAllBuffers(T &&writeBuff, T &&hiddenBuff, T &&readBuff)
Definition: TripleBuffer.h:179
armarx::WriteBufferedTripleBuffer::WriteBufferedTripleBuffer
WriteBufferedTripleBuffer()
Definition: TripleBuffer.h:315
armarx::WriteBufferedTripleBuffer::_getNonConstReadBuffer
T & _getNonConstReadBuffer()
Definition: TripleBuffer.h:353
armarx::TripleBuffer::flags
std::atomic_uint_fast8_t flags
Definition: TripleBuffer.h:301
armarx::TripleBuffer::getReadBuffer
const T & getReadBuffer() const
Definition: TripleBuffer.h:108
armarx::WriteBufferedTripleBuffer::getWriteBuffer
T & getWriteBuffer()
Definition: TripleBuffer.h:325
armarx::TripleBuffer::flagSwapReadWithHidden
static uint_fast8_t flagSwapReadWithHidden(uint_fast8_t flags)
swap read and hidden indexes
Definition: TripleBuffer.h:255
armarx::TripleBuffer::TripleBuffer
TripleBuffer(T &&initR, T &&initH, T &&initW)
Definition: TripleBuffer.h:78
armarx::WriteBufferedTripleBuffer::getWriteBuffer
const T & getWriteBuffer() const
Definition: TripleBuffer.h:332
armarx::TripleBuffer::hiddenBufferIndexMask
static const uint_fast8_t hiddenBufferIndexMask
Definition: TripleBuffer.h:293
armarx::TripleBuffer::dirtyBitMask
static const uint_fast8_t dirtyBitMask
Definition: TripleBuffer.h:295
armarx::TripleBuffer::buffer
T buffer[3]
Definition: TripleBuffer.h:302
armarx::WriteBufferedTripleBuffer::reinitAllBuffers
void reinitAllBuffers(const T &init)
Definition: TripleBuffer.h:390
armarx::TripleBuffer::hiddenToWriteShift
static const uint_fast8_t hiddenToWriteShift
Definition: TripleBuffer.h:289
armarx::TripleBuffer::TripleBuffer
TripleBuffer(const Ts &... ts)
Definition: TripleBuffer.h:86
armarx::TripleBuffer::debugMode
std::atomic_bool debugMode
Definition: TripleBuffer.h:303
armarx::TripleBuffer::hiddenBufferIndexShift
static const uint_fast8_t hiddenBufferIndexShift
Definition: TripleBuffer.h:284
armarx::WriteBufferedTripleBuffer
Same as the TripleBuffer, but partial writes of the data structure are ok. The write operation may be...
Definition: TripleBuffer.h:312
armarx::TripleBuffer::operator=
TripleBuffer & operator=(const TripleBuffer &)=delete
armarx::WriteBufferedTripleBuffer::getReadBuffer
const T & getReadBuffer() const
Definition: TripleBuffer.h:339
armarx::TripleBuffer::_getNonConstReadBuffer
T & _getNonConstReadBuffer()
Definition: TripleBuffer.h:124
T
float T
Definition: UnscentedKalmanFilterTest.cpp:38
armarx::TripleBuffer::getUpToDateReadBuffer
const T & getUpToDateReadBuffer() const
Definition: TripleBuffer.h:116
armarx::WriteBufferedTripleBuffer::getUpToDateReadBuffer
const T & getUpToDateReadBuffer() const
Definition: TripleBuffer.h:346
armarx::TripleBuffer::_getNonConstHiddenBuffer
T & _getNonConstHiddenBuffer()
Definition: TripleBuffer.h:132
armarx::TripleBuffer::reinitAllBuffers
std::enable_if< std::is_copy_constructible< U >::value >::type reinitAllBuffers(const T &init)
Definition: TripleBuffer.h:171
armarx::TripleBuffer::swapWriteAndHiddenBuffer
void swapWriteAndHiddenBuffer()
Swap the write buffer with the hidden buffer.
Definition: TripleBuffer.h:227
armarx::TripleBuffer::readBufferIndexMask
static const uint_fast8_t readBufferIndexMask
Definition: TripleBuffer.h:292
armarx
This file offers overloads of toIce() and fromIce() functions for STL container types.
Definition: ArmarXTimeserver.cpp:27
armarx::TripleBuffer::updateReadBuffer
bool updateReadBuffer() const
Swaps in the hidden buffer if it contains new data.
Definition: TripleBuffer.h:143
armarx::TripleBuffer
A simple triple buffer for lockfree comunication between a single writer and a single reader.
Definition: TripleBuffer.h:71