Embedded Template Library 1.0
Loading...
Searching...
No Matches
queue_spsc_atomic.h
Go to the documentation of this file.
1
2
3/******************************************************************************
4The MIT License(MIT)
5
6Embedded Template Library.
7https://github.com/ETLCPP/etl
8https://www.etlcpp.com
9
10Copyright(c) 2018 John Wellbelove
11
12Permission is hereby granted, free of charge, to any person obtaining a copy
13of this software and associated documentation files(the "Software"), to deal
14in the Software without restriction, including without limitation the rights
15to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
16copies of the Software, and to permit persons to whom the Software is
17furnished to do so, subject to the following conditions :
18
19The above copyright notice and this permission notice shall be included in all
20copies or substantial portions of the Software.
21
22THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
25AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28SOFTWARE.
29******************************************************************************/
30
31#ifndef ETL_SPSC_QUEUE_ATOMIC_INCLUDED
32#define ETL_SPSC_QUEUE_ATOMIC_INCLUDED
33
34#include "platform.h"
35#include "alignment.h"
36#include "atomic.h"
37#include "integral_limits.h"
38#include "memory_model.h"
39#include "parameter_type.h"
40#include "placement_new.h"
41#include "utility.h"
42
43#include <stddef.h>
44#include <stdint.h>
45
46#if ETL_HAS_ATOMIC
47
48namespace etl
49{
50 template <size_t Memory_Model = etl::memory_model::MEMORY_MODEL_LARGE>
51 class queue_spsc_atomic_base
52 {
53 public:
54
56 typedef typename etl::size_type_lookup<Memory_Model>::type size_type;
57
58 //*************************************************************************
62 //*************************************************************************
63 bool empty() const
64 {
65 return read.load(etl::memory_order_acquire) == write.load(etl::memory_order_acquire);
66 }
67
68 //*************************************************************************
72 //*************************************************************************
73 bool full() const
74 {
75 size_type next_index = get_next_index(write.load(etl::memory_order_acquire), Reserved);
76
77 return (next_index == read.load(etl::memory_order_acquire));
78 }
79
80 //*************************************************************************
83 //*************************************************************************
84 size_type size() const
85 {
86 size_type write_index = write.load(etl::memory_order_acquire);
87 size_type read_index = read.load(etl::memory_order_acquire);
88
89 size_type n;
90
91 if (write_index >= read_index)
92 {
93 n = write_index - read_index;
94 }
95 else
96 {
97 n = Reserved - read_index + write_index;
98 }
99
100 return n;
101 }
102
103 //*************************************************************************
106 //*************************************************************************
107 size_type available() const
108 {
109 return Reserved - size() - 1;
110 }
111
112 //*************************************************************************
114 //*************************************************************************
115 size_type capacity() const
116 {
117 return Reserved - 1;
118 }
119
120 //*************************************************************************
122 //*************************************************************************
123 size_type max_size() const
124 {
125 return Reserved - 1;
126 }
127
128 protected:
129
130 queue_spsc_atomic_base(size_type reserved_)
131 : write(0)
132 , read(0)
133 , Reserved(reserved_)
134 {
135 }
136
137 //*************************************************************************
139 //*************************************************************************
140 static size_type get_next_index(size_type index, size_type maximum)
141 {
142 ++index;
143
144 if (index == maximum) ETL_UNLIKELY
145 {
146 index = 0;
147 }
148
149 return index;
150 }
151
152 etl::atomic<size_type> write;
153 etl::atomic<size_type> read;
154 const size_type Reserved;
155
156 private:
157
158 //*************************************************************************
160 //*************************************************************************
161 #if defined(ETL_POLYMORPHIC_SPSC_QUEUE_ATOMIC) || defined(ETL_POLYMORPHIC_CONTAINERS)
162
163 public:
164
165 virtual ~queue_spsc_atomic_base() {}
166 #else
167
168 protected:
169
170 ~queue_spsc_atomic_base() {}
171 #endif
172 };
173
174 //***************************************************************************
184 //***************************************************************************
185 template <typename T, const size_t Memory_Model = etl::memory_model::MEMORY_MODEL_LARGE>
186 class iqueue_spsc_atomic : public queue_spsc_atomic_base<Memory_Model>
187 {
188 private:
189
190 typedef typename etl::queue_spsc_atomic_base<Memory_Model> base_t;
191
192 public:
193
194 typedef T value_type;
195 typedef T& reference;
196 typedef const T& const_reference;
197 #if ETL_USING_CPP11
198 typedef T&& rvalue_reference;
199 #endif
200 typedef typename base_t::size_type size_type;
201
202 using base_t::get_next_index;
203 using base_t::read;
204 using base_t::Reserved;
205 using base_t::write;
206
207 //*************************************************************************
209 //*************************************************************************
210 bool push(const_reference value)
211 {
212 size_type write_index = write.load(etl::memory_order_relaxed);
213 size_type next_index = get_next_index(write_index, Reserved);
214
215 if (next_index != read.load(etl::memory_order_acquire))
216 {
217 ::new (&p_buffer[write_index]) T(value);
218
219 write.store(next_index, etl::memory_order_release);
220
221 return true;
222 }
223
224 // Queue is full.
225 return false;
226 }
227
228 #if ETL_USING_CPP11 && ETL_NOT_USING_STLPORT && !defined(ETL_QUEUE_ATOMIC_FORCE_CPP03_IMPLEMENTATION)
229 //*************************************************************************
231 //*************************************************************************
232 bool push(rvalue_reference value)
233 {
234 size_type write_index = write.load(etl::memory_order_relaxed);
235 size_type next_index = get_next_index(write_index, Reserved);
236
237 if (next_index != read.load(etl::memory_order_acquire))
238 {
239 ::new (&p_buffer[write_index]) T(etl::move(value));
240
241 write.store(next_index, etl::memory_order_release);
242
243 return true;
244 }
245
246 // Queue is full.
247 return false;
248 }
249 #endif
250
251 #if ETL_USING_CPP11 && ETL_NOT_USING_STLPORT && !defined(ETL_QUEUE_ATOMIC_FORCE_CPP03_IMPLEMENTATION)
252 //*************************************************************************
256 //*************************************************************************
257 template <typename... Args>
258 bool emplace(Args&&... args)
259 {
260 size_type write_index = write.load(etl::memory_order_relaxed);
261 size_type next_index = get_next_index(write_index, Reserved);
262
263 if (next_index != read.load(etl::memory_order_acquire))
264 {
265 ::new (&p_buffer[write_index]) T(etl::forward<Args>(args)...);
266
267 write.store(next_index, etl::memory_order_release);
268
269 return true;
270 }
271
272 // Queue is full.
273 return false;
274 }
275 #else
276 //*************************************************************************
280 //*************************************************************************
281 bool emplace()
282 {
283 size_type write_index = write.load(etl::memory_order_relaxed);
284 size_type next_index = get_next_index(write_index, Reserved);
285
286 if (next_index != read.load(etl::memory_order_acquire))
287 {
288 ::new (&p_buffer[write_index]) T();
289
290 write.store(next_index, etl::memory_order_release);
291
292 return true;
293 }
294
295 // Queue is full.
296 return false;
297 }
298
299 //*************************************************************************
303 //*************************************************************************
304 template <typename T1>
305 bool emplace(const T1& value1)
306 {
307 size_type write_index = write.load(etl::memory_order_relaxed);
308 size_type next_index = get_next_index(write_index, Reserved);
309
310 if (next_index != read.load(etl::memory_order_acquire))
311 {
312 ::new (&p_buffer[write_index]) T(value1);
313
314 write.store(next_index, etl::memory_order_release);
315
316 return true;
317 }
318
319 // Queue is full.
320 return false;
321 }
322
323 //*************************************************************************
327 //*************************************************************************
328 template <typename T1, typename T2>
329 bool emplace(const T1& value1, const T2& value2)
330 {
331 size_type write_index = write.load(etl::memory_order_relaxed);
332 size_type next_index = get_next_index(write_index, Reserved);
333
334 if (next_index != read.load(etl::memory_order_acquire))
335 {
336 ::new (&p_buffer[write_index]) T(value1, value2);
337
338 write.store(next_index, etl::memory_order_release);
339
340 return true;
341 }
342
343 // Queue is full.
344 return false;
345 }
346
347 //*************************************************************************
351 //*************************************************************************
352 template <typename T1, typename T2, typename T3>
353 bool emplace(const T1& value1, const T2& value2, const T3& value3)
354 {
355 size_type write_index = write.load(etl::memory_order_relaxed);
356 size_type next_index = get_next_index(write_index, Reserved);
357
358 if (next_index != read.load(etl::memory_order_acquire))
359 {
360 ::new (&p_buffer[write_index]) T(value1, value2, value3);
361
362 write.store(next_index, etl::memory_order_release);
363
364 return true;
365 }
366
367 // Queue is full.
368 return false;
369 }
370
371 //*************************************************************************
375 //*************************************************************************
376 template <typename T1, typename T2, typename T3, typename T4>
377 bool emplace(const T1& value1, const T2& value2, const T3& value3, const T4& value4)
378 {
379 size_type write_index = write.load(etl::memory_order_relaxed);
380 size_type next_index = get_next_index(write_index, Reserved);
381
382 if (next_index != read.load(etl::memory_order_acquire))
383 {
384 ::new (&p_buffer[write_index]) T(value1, value2, value3, value4);
385
386 write.store(next_index, etl::memory_order_release);
387
388 return true;
389 }
390
391 // Queue is full.
392 return false;
393 }
394 #endif
395
396 //*************************************************************************
398 //*************************************************************************
399 bool front(reference value)
400 {
401 size_type read_index = read.load(etl::memory_order_relaxed);
402
403 if (read_index == write.load(etl::memory_order_acquire))
404 {
405 // Queue is empty
406 return false;
407 }
408
409 value = p_buffer[read_index];
410
411 return true;
412 }
413
414 //*************************************************************************
416 //*************************************************************************
417 bool pop(reference value)
418 {
419 size_type read_index = read.load(etl::memory_order_relaxed);
420
421 if (read_index == write.load(etl::memory_order_acquire))
422 {
423 // Queue is empty
424 return false;
425 }
426
427 size_type next_index = get_next_index(read_index, Reserved);
428
429 #if ETL_USING_CPP11 && ETL_NOT_USING_STLPORT && !defined(ETL_QUEUE_LOCKABLE_FORCE_CPP03_IMPLEMENTATION)
430 value = etl::move(p_buffer[read_index]);
431 #else
432 value = p_buffer[read_index];
433 #endif
434
435 p_buffer[read_index].~T();
436
437 read.store(next_index, etl::memory_order_release);
438
439 return true;
440 }
441
442 //*************************************************************************
444 //*************************************************************************
445 bool pop()
446 {
447 size_type read_index = read.load(etl::memory_order_relaxed);
448
449 if (read_index == write.load(etl::memory_order_acquire))
450 {
451 // Queue is empty
452 return false;
453 }
454
455 size_type next_index = get_next_index(read_index, Reserved);
456
457 p_buffer[read_index].~T();
458
459 read.store(next_index, etl::memory_order_release);
460
461 return true;
462 }
463
464 //*************************************************************************
466 //*************************************************************************
467 reference front()
468 {
469 size_type read_index = read.load(etl::memory_order_relaxed);
470
471 return p_buffer[read_index];
472 }
473
474 //*************************************************************************
476 //*************************************************************************
477 const_reference front() const
478 {
479 size_type read_index = read.load(etl::memory_order_relaxed);
480
481 return p_buffer[read_index];
482 }
483
484 //*************************************************************************
488 //*************************************************************************
489 void clear()
490 {
491 if ETL_IF_CONSTEXPR (etl::is_trivially_destructible<T>::value)
492 {
493 write = 0;
494 read = 0;
495 }
496 else
497 {
498 while (pop())
499 {
500 // Do nothing.
501 }
502 }
503 }
504
505 protected:
506
507 //*************************************************************************
509 //*************************************************************************
510 iqueue_spsc_atomic(T* p_buffer_, size_type reserved_)
511 : base_t(reserved_)
512 , p_buffer(p_buffer_)
513 {
514 }
515
516 private:
517
518 // Disable copy construction and assignment.
519 iqueue_spsc_atomic(const iqueue_spsc_atomic&) ETL_DELETE;
520 iqueue_spsc_atomic& operator=(const iqueue_spsc_atomic&) ETL_DELETE;
521
522 #if ETL_USING_CPP11
523 iqueue_spsc_atomic(iqueue_spsc_atomic&&) = delete;
524 iqueue_spsc_atomic& operator=(iqueue_spsc_atomic&&) = delete;
525 #endif
526
527 T* p_buffer;
528 };
529
530 //***************************************************************************
538 //***************************************************************************
539 template <typename T, size_t Size, const size_t Memory_Model = etl::memory_model::MEMORY_MODEL_LARGE>
540 class queue_spsc_atomic : public iqueue_spsc_atomic<T, Memory_Model>
541 {
542 private:
543
544 typedef typename etl::iqueue_spsc_atomic<T, Memory_Model> base_t;
545
546 public:
547
548 typedef typename base_t::size_type size_type;
549
550 private:
551
552 static ETL_CONSTANT size_type Reserved_Size = size_type(Size + 1);
553
554 public:
555
556 ETL_STATIC_ASSERT((Size <= (etl::integral_limits<size_type>::max - 1)), "Size too large for memory model");
557
558 static ETL_CONSTANT size_type MAX_SIZE = size_type(Size);
559
560 //*************************************************************************
562 //*************************************************************************
563 queue_spsc_atomic()
564 : base_t(reinterpret_cast<T*>(&buffer[0]), Reserved_Size)
565 {
566 }
567
568 //*************************************************************************
570 //*************************************************************************
571 ~queue_spsc_atomic()
572 {
573 base_t::clear();
574 }
575
576 private:
577
579 typename etl::aligned_storage<sizeof(T), etl::alignment_of<T>::value>::type buffer[Reserved_Size];
580 };
581
582 template <typename T, size_t Size, const size_t Memory_Model>
583 ETL_CONSTANT typename queue_spsc_atomic<T, Size, Memory_Model>::size_type queue_spsc_atomic<T, Size, Memory_Model>::MAX_SIZE;
584} // namespace etl
585
586#endif
587
588#endif
bitset_ext
Definition absolute.h:40
etl::optional< T > read(etl::bit_stream_reader &stream)
Read a checked type from a stream.
Definition bit_stream.h:1377
ETL_CONSTEXPR TContainer::size_type size(const TContainer &container)
Definition iterator.h:1192
bool write(etl::bit_stream_writer &stream, bool value)
Definition bit_stream.h:995