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 <type_traits>
55 #include <iostream>
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  template<class...Ts>
85  TripleBuffer(const Ts& ...ts) :
86  buffer{T(ts...), T(ts...), T(ts...)}
87  {}
88 
89  /// @return the write buffer
91  {
92  DEBUG_MODE_PRINT << "access write buffer\n";
93  return buffer[getWriteBufferIndex()];
94  }
95  /// @return the write buffer
96  const T& getWriteBuffer() const
97  {
98  DEBUG_MODE_PRINT << "access write buffer\n";
99  return buffer[getWriteBufferIndex()];
100  }
101  /// @return the current read buffer
102  const T& getReadBuffer() const
103  {
104  DEBUG_MODE_PRINT << "access read buffer\n";
105  return buffer[getReadBufferIndex() ];
106  }
107  /// @return the most up to date read buffer
108  const T& getUpToDateReadBuffer() const
109  {
111  return getReadBuffer();
112  }
113 
114  /// @return the current read buffer (sometimes required for more complex initialization)
116  {
117  DEBUG_MODE_PRINT << "access non const read buffer\n";
118  return buffer[getReadBufferIndex() ];
119  }
120  /// @return the current read buffer (sometimes required for more complex initialization)
122  {
123  DEBUG_MODE_PRINT << "access non const hidden buffer\n";
124  return buffer[getHiddenBufferIndex()];
125  }
126 
127  /**
128  * @brief Swaps in the hidden buffer if it contains new data.
129  * @return True if new data is available
130  */
131  bool updateReadBuffer() const
132  {
133  uint_fast8_t flagsNow(flags.load(std::memory_order_consume));
134 
135  if (!hasNewWrite(flagsNow))
136  {
137  DEBUG_MODE_PRINT << "update read buffer. no update available\n";
138  return false;
139  }
140 
141  while (!flags.compare_exchange_weak(flagsNow, flagSwapReadWithHidden(flagsNow), std::memory_order_release, std::memory_order_consume));
142  DEBUG_MODE_PRINT << "update read buffer. got update\n";
143  return true;
144  }
145 
146  void commitWrite()
147  {
148  DEBUG_MODE_PRINT << "commit write buffer.\n";
150  }
151 
152  template<class U = T>
154  {
155  buffer[0] = init;
156  buffer[1] = init;
157  buffer[2] = init;
158  }
159  void reinitAllBuffers(T&& writeBuff, T&& hiddenBuff, T&& readBuff)
160  {
161  buffer[getWriteBufferIndex() ] = std::move(writeBuff);
162  buffer[getHiddenBufferIndex()] = std::move(hiddenBuff);
163  buffer[getReadBufferIndex() ] = std::move(readBuff);
164  }
165  void setDebugMode(bool mode)
166  {
167  std::cout << '[' << simox::system_thread_id() << "] [0x" << this
168  << "] set debug mode to " << mode << '\n';
169  debugMode = mode;
170  }
171 
172 #undef DEBUG_MODE_PRINT
173  protected:
174  // non-copyable behavior
175  TripleBuffer(const TripleBuffer&) = delete;
176  TripleBuffer& operator=(const TripleBuffer&) = delete;
177 
178  // /////////////////////////////////////////////////////////////////////////////// //
179  // get indices
180  uint_fast8_t getReadBufferIndex() const
181  {
182  return (flags.load(std::memory_order_consume) & readBufferIndexMask) >> readBufferIndexShift ;
183  }
184  uint_fast8_t getWriteBufferIndex() const
185  {
186  return (flags.load(std::memory_order_consume) & writeBufferIndexMask) >> writeBufferIndexShift;
187  }
188  uint_fast8_t getHiddenBufferIndex() const
189  {
190  return (flags.load(std::memory_order_consume) & hiddenBufferIndexMask) >> hiddenBufferIndexShift;
191  }
192 
193  // /////////////////////////////////////////////////////////////////////////////// //
194  //buffer operations
195  /// @brief Swap the write buffer with the hidden buffer
197  {
198  uint_fast8_t flagsNow(flags.load(std::memory_order_consume));
199  while (!flags.compare_exchange_weak(flagsNow, flagSwapWriteWithHidden(flagsNow), std::memory_order_release, std::memory_order_consume));
200  }
201  // void swapReadAndHiddenBuffer(); is done by updateReadBuffer()
202 
203  // /////////////////////////////////////////////////////////////////////////////// //
204  //flag opertions
205  // check if the newWrite bit is 1
206  bool hasNewWrite(uint_fast8_t flags) const
207  {
208  return flags & dirtyBitMask;
209  }
210  /// swap read and hidden indexes
211  /**
212  * @brief swap read and hidden indexes of given flags (set dirty to 0)
213  * @param flags the current flags
214  * @return the new flags
215  */
216  static uint_fast8_t flagSwapReadWithHidden(uint_fast8_t flags)
217  {
218  return 0 //set dirty bit to 0
219  | ((flags & hiddenBufferIndexMask) >> hiddenToReadShift) // hidden index now is read index
220  | ((flags & readBufferIndexMask) << hiddenToReadShift) // read index now is hidden index
221  | (flags & writeBufferIndexMask); // keep write index
222  }
223 
224  /**
225  * @brief swap write and hidden indexes of given flags (set dirty to 1)
226  * @param flags the current flags
227  * @return the new flags
228  */
229  static uint_fast8_t flagSwapWriteWithHidden(uint_fast8_t flags)
230  {
231  return dirtyBitMask //set dirty bit to 1
232  | ((flags & hiddenBufferIndexMask) << hiddenToWriteShift) // hidden index now is write index
233  | ((flags & writeBufferIndexMask) >> hiddenToWriteShift) // write index now is hidden index
234  | (flags & readBufferIndexMask); // keep read index
235  }
236 
237  // /////////////////////////////////////////////////////////////////////////////// //
238  // constants
239  static const uint_fast8_t readBufferIndexShift = 0;
240  static const uint_fast8_t hiddenBufferIndexShift = 2;
241  static const uint_fast8_t writeBufferIndexShift = 4;
242  static const uint_fast8_t dirtyBitShift = 7;
243 
246 
247  static const uint_fast8_t readBufferIndexMask = 3 << readBufferIndexShift ; //0b00000011;
248  static const uint_fast8_t hiddenBufferIndexMask = 3 << hiddenBufferIndexShift; //0b00001100;
249  static const uint_fast8_t writeBufferIndexMask = 3 << writeBufferIndexShift ; //0b00110000;
250  static const uint_fast8_t dirtyBitMask = 1 << dirtyBitShift ; //0b10000000;
251  // initially dirty = 0, write 0, hidden = 1, read = 2
252  static const uint_fast8_t initialFlags = 2 + (1 << 2); //0b00000110;
253 
254  // /////////////////////////////////////////////////////////////////////////////// //
255  // data
256  mutable std::atomic_uint_fast8_t flags {initialFlags};
257  T buffer[3];
258  std::atomic_bool debugMode{false};
259  };
260 
261  /**
262  * @see TripleBuffer
263  * @brief Same as the TripleBuffer, but partial writes of the data structure are ok.
264  * The write operation may be a bit slower and memory consumption may be 1/3 higher.
265  */
266  template <typename T>
268  {
269  public:
271  writeBuffer(T())
272  {}
273 
275  tripleBuffer {init},
276  writeBuffer(init)
277  {}
278 
279  /// @return the write buffer
281  {
282  return writeBuffer;
283  }
284 
285  /// @return the write buffer
286  const T& getWriteBuffer() const
287  {
288  return writeBuffer;
289  }
290  /// @return the current read buffer
291  const T& getReadBuffer() const
292  {
293  return tripleBuffer.getReadBuffer();
294  }
295  /// @return the most up to date read buffer
296  const T& getUpToDateReadBuffer() const
297  {
298  return tripleBuffer.getUpToDateReadBuffer();
299  }
300 
301  /// @return the current read buffer (sometimes required for more complex initialization)
303  {
304  return tripleBuffer._getNonConstReadBuffer();
305  }
306  /// @return the current read buffer (sometimes required for more complex initialization)
308  {
309  return tripleBuffer._getNonConstHiddenBuffer();
310  }
311  /// @return the current read buffer (sometimes required for more complex initialization)
313  {
314  return tripleBuffer.getReadBuffer();
315  }
316 
317  /**
318  * @brief Swaps in the hidden buffer if it contains new data.
319  * @return True if new data is available
320  */
321  bool updateReadBuffer() const
322  {
323  return tripleBuffer.updateReadBuffer();
324  }
325 
326  void commitWrite()
327  {
328  tripleBuffer.getWriteBuffer() = writeBuffer;
329  tripleBuffer.commitWrite();
330  }
331 
332  void reinitAllBuffers(const T& init)
333  {
334  writeBuffer = init;
335  tripleBuffer.reinitAllBuffers(init);
336  }
337 
338  void setDebugMode(bool mode)
339  {
340  tripleBuffer.setDebugMode(mode);
341  }
342  private:
343  TripleBuffer<T> tripleBuffer;
344  T writeBuffer;
345  };
346 }
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:229
armarx::TripleBuffer::getWriteBuffer
T & getWriteBuffer()
Definition: TripleBuffer.h:90
DEBUG_MODE_PRINT
#define DEBUG_MODE_PRINT
Definition: TripleBuffer.h:74
armarx::TripleBuffer::getWriteBufferIndex
uint_fast8_t getWriteBufferIndex() const
Definition: TripleBuffer.h:184
armarx::TripleBuffer::writeBufferIndexShift
static const uint_fast8_t writeBufferIndexShift
Definition: TripleBuffer.h:241
armarx::TripleBuffer::readBufferIndexShift
static const uint_fast8_t readBufferIndexShift
Definition: TripleBuffer.h:239
armarx::TripleBuffer::writeBufferIndexMask
static const uint_fast8_t writeBufferIndexMask
Definition: TripleBuffer.h:249
armarx::TripleBuffer::getHiddenBufferIndex
uint_fast8_t getHiddenBufferIndex() const
Definition: TripleBuffer.h:188
armarx::TripleBuffer::commitWrite
void commitWrite()
Definition: TripleBuffer.h:146
armarx::WriteBufferedTripleBuffer::commitWrite
void commitWrite()
Definition: TripleBuffer.h:326
armarx::WriteBufferedTripleBuffer::WriteBufferedTripleBuffer
WriteBufferedTripleBuffer(const T &init)
Definition: TripleBuffer.h:274
armarx::TripleBuffer::initialFlags
static const uint_fast8_t initialFlags
Definition: TripleBuffer.h:252
armarx::WriteBufferedTripleBuffer::_getNonConstHiddenBuffer
T & _getNonConstHiddenBuffer()
Definition: TripleBuffer.h:307
armarx::TripleBuffer::hasNewWrite
bool hasNewWrite(uint_fast8_t flags) const
Definition: TripleBuffer.h:206
armarx::WriteBufferedTripleBuffer::updateReadBuffer
bool updateReadBuffer() const
Swaps in the hidden buffer if it contains new data.
Definition: TripleBuffer.h:321
armarx::TripleBuffer::getReadBufferIndex
uint_fast8_t getReadBufferIndex() const
Definition: TripleBuffer.h:180
armarx::TripleBuffer::dirtyBitShift
static const uint_fast8_t dirtyBitShift
Definition: TripleBuffer.h:242
armarx::TripleBuffer::getWriteBuffer
const T & getWriteBuffer() const
Definition: TripleBuffer.h:96
armarx::TripleBuffer::setDebugMode
void setDebugMode(bool mode)
Definition: TripleBuffer.h:165
armarx::WriteBufferedTripleBuffer::setDebugMode
void setDebugMode(bool mode)
Definition: TripleBuffer.h:338
armarx::WriteBufferedTripleBuffer::_getNonConstHiddenWriteBuffer
T & _getNonConstHiddenWriteBuffer()
Definition: TripleBuffer.h:312
armarx::TripleBuffer::hiddenToReadShift
static const uint_fast8_t hiddenToReadShift
Definition: TripleBuffer.h:244
cxxopts::value
std::shared_ptr< Value > value()
Definition: cxxopts.hpp:926
armarx::TripleBuffer::reinitAllBuffers
void reinitAllBuffers(T &&writeBuff, T &&hiddenBuff, T &&readBuff)
Definition: TripleBuffer.h:159
armarx::WriteBufferedTripleBuffer::WriteBufferedTripleBuffer
WriteBufferedTripleBuffer()
Definition: TripleBuffer.h:270
armarx::WriteBufferedTripleBuffer::_getNonConstReadBuffer
T & _getNonConstReadBuffer()
Definition: TripleBuffer.h:302
armarx::TripleBuffer::flags
std::atomic_uint_fast8_t flags
Definition: TripleBuffer.h:256
armarx::TripleBuffer::getReadBuffer
const T & getReadBuffer() const
Definition: TripleBuffer.h:102
armarx::WriteBufferedTripleBuffer::getWriteBuffer
T & getWriteBuffer()
Definition: TripleBuffer.h:280
armarx::TripleBuffer::TripleBuffer
TripleBuffer(const Ts &...ts)
Definition: TripleBuffer.h:85
armarx::TripleBuffer::flagSwapReadWithHidden
static uint_fast8_t flagSwapReadWithHidden(uint_fast8_t flags)
swap read and hidden indexes
Definition: TripleBuffer.h:216
armarx::TripleBuffer::TripleBuffer
TripleBuffer(T &&initR, T &&initH, T &&initW)
Definition: TripleBuffer.h:78
armarx::WriteBufferedTripleBuffer::getWriteBuffer
const T & getWriteBuffer() const
Definition: TripleBuffer.h:286
armarx::TripleBuffer::hiddenBufferIndexMask
static const uint_fast8_t hiddenBufferIndexMask
Definition: TripleBuffer.h:248
armarx::TripleBuffer::dirtyBitMask
static const uint_fast8_t dirtyBitMask
Definition: TripleBuffer.h:250
armarx::TripleBuffer::buffer
T buffer[3]
Definition: TripleBuffer.h:257
armarx::WriteBufferedTripleBuffer::reinitAllBuffers
void reinitAllBuffers(const T &init)
Definition: TripleBuffer.h:332
armarx::TripleBuffer::hiddenToWriteShift
static const uint_fast8_t hiddenToWriteShift
Definition: TripleBuffer.h:245
armarx::TripleBuffer::debugMode
std::atomic_bool debugMode
Definition: TripleBuffer.h:258
armarx::TripleBuffer::hiddenBufferIndexShift
static const uint_fast8_t hiddenBufferIndexShift
Definition: TripleBuffer.h:240
armarx::WriteBufferedTripleBuffer
Same as the TripleBuffer, but partial writes of the data structure are ok. The write operation may be...
Definition: TripleBuffer.h:267
armarx::TripleBuffer::operator=
TripleBuffer & operator=(const TripleBuffer &)=delete
armarx::WriteBufferedTripleBuffer::getReadBuffer
const T & getReadBuffer() const
Definition: TripleBuffer.h:291
armarx::TripleBuffer::_getNonConstReadBuffer
T & _getNonConstReadBuffer()
Definition: TripleBuffer.h:115
T
float T
Definition: UnscentedKalmanFilterTest.cpp:35
armarx::TripleBuffer::getUpToDateReadBuffer
const T & getUpToDateReadBuffer() const
Definition: TripleBuffer.h:108
armarx::WriteBufferedTripleBuffer::getUpToDateReadBuffer
const T & getUpToDateReadBuffer() const
Definition: TripleBuffer.h:296
armarx::TripleBuffer::_getNonConstHiddenBuffer
T & _getNonConstHiddenBuffer()
Definition: TripleBuffer.h:121
armarx::TripleBuffer::reinitAllBuffers
std::enable_if< std::is_copy_constructible< U >::value >::type reinitAllBuffers(const T &init)
Definition: TripleBuffer.h:153
armarx::TripleBuffer::swapWriteAndHiddenBuffer
void swapWriteAndHiddenBuffer()
Swap the write buffer with the hidden buffer.
Definition: TripleBuffer.h:196
armarx::TripleBuffer::readBufferIndexMask
static const uint_fast8_t readBufferIndexMask
Definition: TripleBuffer.h:247
armarx
This file offers overloads of toIce() and fromIce() functions for STL container types.
Definition: ArmarXTimeserver.cpp:28
armarx::TripleBuffer::updateReadBuffer
bool updateReadBuffer() const
Swaps in the hidden buffer if it contains new data.
Definition: TripleBuffer.h:131
armarx::TripleBuffer
A simple triple buffer for lockfree comunication between a single writer and a single reader.
Definition: TripleBuffer.h:71