├── .gitignore ├── LICENSE.md ├── README.rst ├── include ├── atomic_ringbuffer.hpp └── ringbuffer.hpp └── test └── test.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | #### joe made this: https://goel.io/joe 2 | 3 | #####=== C++ ===##### 4 | 5 | # Compiled Object files 6 | *.slo 7 | *.lo 8 | *.o 9 | *.obj 10 | 11 | # Precompiled Headers 12 | *.gch 13 | *.pch 14 | 15 | # Compiled Dynamic libraries 16 | *.so 17 | *.dylib 18 | *.dll 19 | 20 | # Fortran module files 21 | *.mod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | #####=== OSX ===##### 35 | .DS_Store 36 | .AppleDouble 37 | .LSOverride 38 | 39 | # Icon must end with two \r 40 | Icon 41 | 42 | # Thumbnails 43 | ._* 44 | 45 | # Files that might appear on external disk 46 | .Spotlight-V100 47 | .Trashes 48 | 49 | # Directories potentially created on remote AFP share 50 | .AppleDB 51 | .AppleDesktop 52 | Network Trash Folder 53 | Temporary Items 54 | .apdisk 55 | 56 | *.swp 57 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License (MIT) 2 | 3 | Copyright (c) 2015 Dalton M. Woodard 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ========== 2 | ringbuffer 3 | ========== 4 | 5 | A single header C++ implementation of threadsafe and non-threadsafe fixed-size, 6 | templated, STL-style cicular buffers (``atomic_ringbuffer.hpp`` and 7 | ``ringbuffer.hpp``, respectively). Both versions support full RAII/RRID 8 | compliance and the strong exception safety guarantee wherever possible. 9 | 10 | ---- 11 | Info 12 | ---- 13 | 14 | Both files are known to compile with clang++ (ver. Apple LLVM 7.0.0), 15 | and g++ (ver. 5.2.0) on OS X 10.11, under both ``-std=c++11`` and 16 | ``-std=c++14``. 17 | 18 | ------- 19 | License 20 | ------- 21 | 22 | ringbuffer is licensed under the OSI Approved MIT License Copyrighted (c) 2015 23 | by Dalton Woodard. Please see the file LICENSE.md distributed with this 24 | package for details. 25 | -------------------------------------------------------------------------------- /include/atomic_ringbuffer.hpp: -------------------------------------------------------------------------------- 1 | // atomic_ringbuffer 2 | // : a threadsafe, fixed (at compile time) sized circular 3 | // buffer type supporting multiple readers and one 4 | // writer. 5 | // 6 | // Author: Dalton Woodard 7 | // Contact: daltonmwoodar@gmail.com 8 | // License: Please see LICENSE.md 9 | // 10 | 11 | 12 | // note: 13 | // While this type is lasted by a std::array object, 14 | // and can therefore exist entirely on the stack, it 15 | // is meant to be heap allocated to avoid stack size 16 | // limitations. The use of std::array is to provide 17 | // locality of the buffered data with respect to the 18 | // managing ringbuffer object. 19 | // 20 | // note: 21 | // If and only if the stored object type T has strong exception safe 22 | // constructors is the following guaranteed: 23 | // 24 | // If this object is successfull constructed, then throughout the 25 | // object's lifetime: 26 | // 27 | // 1) all view operations are guaranteed as nothrow/noexcept; 28 | // 2) `read` opeartions provide the basic exception safety guarantee; 29 | // 3) `safe_read` operations provide the strong exception safety 30 | // guarantee. 31 | // 32 | // Notice that the read operations modify the data structure (by removing 33 | // elements from the front); hence they are, in actuallity, writes. 34 | // 35 | // All of the above are documented as such in source. 36 | 37 | #ifndef ATOMIC_RINGBUFFER_HPP 38 | #define ATOMIC_RINGBUFFER_HPP 39 | 40 | #include // std::array 41 | #include // std::atomic<..> 42 | #include // std::yield 43 | #include // std::is_default_constructible 44 | #include // std::vector 45 | 46 | namespace atomic_ringbuffer 47 | { 48 | namespace detail 49 | { 50 | template 51 | struct has_reserve 52 | { 53 | struct succeeds {}; 54 | struct fails {}; 55 | 56 | template 57 | struct sfinae {}; 58 | 59 | template 60 | static succeeds test (sfinae*); 61 | 62 | template 63 | static fails test (...); 64 | 65 | static constexpr bool value = 66 | std::is_same(0)), succeeds>::value; 67 | }; 68 | 69 | template 70 | struct memblock 71 | { 72 | alignas (alignof(T)) unsigned char data[sizeof(T)]; 73 | }; 74 | 75 | // a simple multiple reader, single 76 | // writer shared mutex, which spins 77 | // when waiting for a write oprotunity. 78 | // 79 | // -- RAII/RRID compliant 80 | // 81 | class rwlock 82 | { 83 | private: 84 | template 85 | struct slock 86 | { 87 | static constexpr bool LOCKED = true; 88 | static constexpr bool UNLOCKED = false; 89 | 90 | std::atomic_bool held {UNLOCKED}; 91 | 92 | bool lock (void) volatile noexcept 93 | { 94 | while (true) { 95 | for (uint64_t i = 0; i < Delay; ++i) { 96 | if (UNLOCKED == held.exchange (LOCKED)) 97 | return LOCKED; 98 | } 99 | std::this_thread::yield (); 100 | } 101 | } 102 | 103 | bool unlock (void) volatile noexcept 104 | { 105 | return held.exchange (UNLOCKED); 106 | } 107 | }; 108 | 109 | mutable slock<> mut; 110 | mutable std::atomic_size_t readers; 111 | 112 | struct reader_entity 113 | { 114 | private: 115 | rwlock const * parent; 116 | public: 117 | reader_entity (rwlock const * p) noexcept 118 | : parent (p) 119 | { 120 | parent->read_lock (); 121 | } 122 | 123 | reader_entity (reader_entity &&) = default; 124 | reader_entity& operator= (reader_entity &&) = default; 125 | 126 | reader_entity (reader_entity const&) = delete; 127 | reader_entity& operator= (reader_entity const&) = delete; 128 | 129 | ~reader_entity (void) noexcept 130 | { 131 | parent->read_unlock (); 132 | } 133 | }; 134 | 135 | struct writer_entity 136 | { 137 | private: 138 | rwlock const * parent; 139 | public: 140 | writer_entity (rwlock const * p) noexcept 141 | : parent (p) 142 | { 143 | parent->write_lock (); 144 | } 145 | 146 | writer_entity (writer_entity &&) = default; 147 | writer_entity& operator= (writer_entity &&) = default; 148 | 149 | writer_entity (writer_entity const&) = delete; 150 | writer_entity& operator= (writer_entity const&) = delete; 151 | 152 | ~writer_entity (void) noexcept 153 | { 154 | parent->write_unlock (); 155 | } 156 | }; 157 | 158 | void read_lock (void) const noexcept 159 | { 160 | mut.lock (); 161 | readers += 1; 162 | mut.unlock (); 163 | } 164 | 165 | void read_unlock (void) const noexcept 166 | { 167 | if (readers.load()) 168 | readers -= 1; 169 | } 170 | 171 | void write_lock (void) const noexcept 172 | { 173 | mut.lock(); 174 | } 175 | 176 | void write_unlock (void) const noexcept 177 | { 178 | mut.unlock (); 179 | } 180 | 181 | public: 182 | rwlock (void) noexcept = default; 183 | 184 | rwlock (rwlock &&) = default; 185 | rwlock& operator= (rwlock &&) = default; 186 | 187 | rwlock (rwlock const&) = delete; 188 | rwlock& operator= (rwlock const&) = delete; 189 | 190 | ~rwlock (void) noexcept = default; 191 | 192 | reader_entity reader (void) const noexcept 193 | { 194 | return reader_entity (this); 195 | } 196 | 197 | writer_entity writer (void) const noexcept 198 | { 199 | return writer_entity (this); 200 | } 201 | }; 202 | } // namespace detail 203 | 204 | template 205 | class atomic_ringbuffer 206 | { 207 | static_assert (N > 0, "empty ringbuffer disallowed"); 208 | static_assert (std::is_default_constructible::value, 209 | "cannot buffer non-default_constructible objects"); 210 | private: 211 | using backing_type = std::array, N>; 212 | using backing_ptr = typename backing_type::pointer; 213 | 214 | backing_type buff; 215 | 216 | backing_ptr const first; 217 | backing_ptr const last; 218 | 219 | backing_ptr writepos; 220 | backing_ptr readpos; 221 | std::atomic_size_t available; 222 | 223 | detail::rwlock rwlock; 224 | 225 | template 226 | class atomic_rb_iterator : 227 | public std::iterator::type, 229 | std::ptrdiff_t, 230 | U*, 231 | U&> 232 | { 233 | private: 234 | U* iter; 235 | U const* first; 236 | U const* last; 237 | detail::rwlock* rwlock; 238 | 239 | using piter_type = std::iterator::type, 241 | std::ptrdiff_t, 242 | U*, 243 | U&>; 244 | 245 | public: 246 | using difference_type = typename piter_type::difference_type; 247 | using value_type = typename piter_type::value_type; 248 | using pointer = typename piter_type::pointer; 249 | using reference = typename piter_type::reference; 250 | using iterator_category = typename piter_type::iterator_category; 251 | 252 | atomic_rb_iterator (void) = delete; 253 | atomic_rb_iterator 254 | (U* p, U const* f, U const* l, detail::rwlock * lck) 255 | : iter (p), first (f), last (l), rwlock (lck) 256 | {} 257 | 258 | void swap (atomic_rb_iterator & other) 259 | { 260 | std::swap (iter, other.iter); 261 | std::swap (first, other.first); 262 | std::swap (last, other.last); 263 | std::swap (rwlock, other.rwlock); 264 | } 265 | 266 | atomic_rb_iterator& operator++ (void) 267 | { 268 | if (iter == last) 269 | iter = first; 270 | else 271 | iter += 1; 272 | return *this; 273 | } 274 | 275 | atomic_rb_iterator& operator-- (void) 276 | { 277 | if (iter == first) 278 | iter = last; 279 | else 280 | iter -= 1; 281 | return *this; 282 | } 283 | 284 | atomic_rb_iterator operator++ (int) 285 | { 286 | auto tmp (*this); 287 | if (iter == last) 288 | iter = first; 289 | else 290 | iter += 1; 291 | return tmp; 292 | } 293 | 294 | atomic_rb_iterator operator-- (int) 295 | { 296 | auto tmp (*this); 297 | if (iter == first) 298 | iter = last; 299 | else 300 | iter -= 1; 301 | return tmp; 302 | } 303 | 304 | atomic_rb_iterator& operator+= (difference_type n) 305 | { 306 | if (n > N || -n > N) n = n % N; 307 | 308 | if (n >= 0) { 309 | if (iter + n > last) 310 | iter = first + (n - 1 - (last - iter)); 311 | else 312 | iter = iter + n; 313 | } else { 314 | auto m = -n; 315 | if (iter - m < first) 316 | iter = last - (m - 1 - (iter - first)); 317 | else 318 | iter = iter - m; 319 | } 320 | 321 | return *this; 322 | } 323 | 324 | atomic_rb_iterator& operator-= (difference_type n) 325 | { 326 | return this->operator+= (-n); 327 | } 328 | 329 | atomic_rb_iterator operator+ (difference_type n) const 330 | { 331 | auto tmp = *this; 332 | return (tmp += n); 333 | } 334 | 335 | atomic_rb_iterator operator- (difference_type n) const 336 | { 337 | auto tmp = *this; 338 | return (tmp -= n); 339 | } 340 | 341 | difference_type operator- 342 | (atomic_rb_iterator const rhs) const 343 | { 344 | return iter - rhs.iter; 345 | } 346 | 347 | template