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 "parameter_type.h"
37#include "atomic.h"
38#include "memory_model.h"
39#include "integral_limits.h"
40#include "utility.h"
41#include "placement_new.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
131 : write(0),
132 read(0),
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
154 const size_type Reserved;
155
156 private:
157
158 //*************************************************************************
160 //*************************************************************************
161#if defined(ETL_POLYMORPHIC_SPSC_QUEUE_ATOMIC) || defined(ETL_POLYMORPHIC_CONTAINERS)
162 public:
164 {
165 }
166#else
167 protected:
169 {
170 }
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::write;
203 using base_t::read;
204 using base_t::Reserved;
205 using base_t::get_next_index;
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 //*************************************************************************
255 //*************************************************************************
256 template <typename ... Args>
257 bool emplace(Args&&... args)
258 {
259 size_type write_index = write.load(etl::memory_order_relaxed);
260 size_type next_index = get_next_index(write_index, Reserved);
261
262 if (next_index != read.load(etl::memory_order_acquire))
263 {
264 ::new (&p_buffer[write_index]) T(etl::forward<Args>(args)...);
265
266 write.store(next_index, etl::memory_order_release);
267
268 return true;
269 }
270
271 // Queue is full.
272 return false;
273 }
274#else
275 //*************************************************************************
278 //*************************************************************************
279 bool emplace()
280 {
281 size_type write_index = write.load(etl::memory_order_relaxed);
282 size_type next_index = get_next_index(write_index, Reserved);
283
284 if (next_index != read.load(etl::memory_order_acquire))
285 {
286 ::new (&p_buffer[write_index]) T();
287
288 write.store(next_index, etl::memory_order_release);
289
290 return true;
291 }
292
293 // Queue is full.
294 return false;
295 }
296
297 //*************************************************************************
300 //*************************************************************************
301 template <typename T1>
302 bool emplace(const T1& value1)
303 {
304 size_type write_index = write.load(etl::memory_order_relaxed);
305 size_type next_index = get_next_index(write_index, Reserved);
306
307 if (next_index != read.load(etl::memory_order_acquire))
308 {
309 ::new (&p_buffer[write_index]) T(value1);
310
311 write.store(next_index, etl::memory_order_release);
312
313 return true;
314 }
315
316 // Queue is full.
317 return false;
318 }
319
320 //*************************************************************************
323 //*************************************************************************
324 template <typename T1, typename T2>
325 bool emplace(const T1& value1, const T2& value2)
326 {
327 size_type write_index = write.load(etl::memory_order_relaxed);
328 size_type next_index = get_next_index(write_index, Reserved);
329
330 if (next_index != read.load(etl::memory_order_acquire))
331 {
332 ::new (&p_buffer[write_index]) T(value1, value2);
333
334 write.store(next_index, etl::memory_order_release);
335
336 return true;
337 }
338
339 // Queue is full.
340 return false;
341 }
342
343 //*************************************************************************
346 //*************************************************************************
347 template <typename T1, typename T2, typename T3>
348 bool emplace(const T1& value1, const T2& value2, const T3& value3)
349 {
350 size_type write_index = write.load(etl::memory_order_relaxed);
351 size_type next_index = get_next_index(write_index, Reserved);
352
353 if (next_index != read.load(etl::memory_order_acquire))
354 {
355 ::new (&p_buffer[write_index]) T(value1, value2, value3);
356
357 write.store(next_index, etl::memory_order_release);
358
359 return true;
360 }
361
362 // Queue is full.
363 return false;
364 }
365
366 //*************************************************************************
369 //*************************************************************************
370 template <typename T1, typename T2, typename T3, typename T4>
371 bool emplace(const T1& value1, const T2& value2, const T3& value3, const T4& value4)
372 {
373 size_type write_index = write.load(etl::memory_order_relaxed);
374 size_type next_index = get_next_index(write_index, Reserved);
375
376 if (next_index != read.load(etl::memory_order_acquire))
377 {
378 ::new (&p_buffer[write_index]) T(value1, value2, value3, value4);
379
380 write.store(next_index, etl::memory_order_release);
381
382 return true;
383 }
384
385 // Queue is full.
386 return false;
387 }
388#endif
389
390 //*************************************************************************
392 //*************************************************************************
393 bool front(reference value)
394 {
395 size_type read_index = read.load(etl::memory_order_relaxed);
396
397 if (read_index == write.load(etl::memory_order_acquire))
398 {
399 // Queue is empty
400 return false;
401 }
402
403 value = p_buffer[read_index];
404
405 return true;
406 }
407
408 //*************************************************************************
410 //*************************************************************************
411 bool pop(reference value)
412 {
413 size_type read_index = read.load(etl::memory_order_relaxed);
414
415 if (read_index == write.load(etl::memory_order_acquire))
416 {
417 // Queue is empty
418 return false;
419 }
420
421 size_type next_index = get_next_index(read_index, Reserved);
422
423#if ETL_USING_CPP11 && ETL_NOT_USING_STLPORT && !defined(ETL_QUEUE_LOCKABLE_FORCE_CPP03_IMPLEMENTATION)
424 value = etl::move(p_buffer[read_index]);
425#else
426 value = p_buffer[read_index];
427#endif
428
429 p_buffer[read_index].~T();
430
431 read.store(next_index, etl::memory_order_release);
432
433 return true;
434 }
435
436 //*************************************************************************
438 //*************************************************************************
439 bool pop()
440 {
441 size_type read_index = read.load(etl::memory_order_relaxed);
442
443 if (read_index == write.load(etl::memory_order_acquire))
444 {
445 // Queue is empty
446 return false;
447 }
448
449 size_type next_index = get_next_index(read_index, Reserved);
450
451 p_buffer[read_index].~T();
452
453 read.store(next_index, etl::memory_order_release);
454
455 return true;
456 }
457
458 //*************************************************************************
460 //*************************************************************************
461 reference front()
462 {
463 size_type read_index = read.load(etl::memory_order_relaxed);
464
465 return p_buffer[read_index];
466 }
467
468 //*************************************************************************
470 //*************************************************************************
471 const_reference front() const
472 {
473 size_type read_index = read.load(etl::memory_order_relaxed);
474
475 return p_buffer[read_index];
476 }
477
478 //*************************************************************************
482 //*************************************************************************
483 void clear()
484 {
485 while (pop())
486 {
487 // Do nothing.
488 }
489 }
490
491 protected:
492
493 //*************************************************************************
495 //*************************************************************************
497 : base_t(reserved_),
498 p_buffer(p_buffer_)
499 {
500 }
501
502 private:
503
504 // Disable copy construction and assignment.
505 iqueue_spsc_atomic(const iqueue_spsc_atomic&) ETL_DELETE;
506 iqueue_spsc_atomic& operator =(const iqueue_spsc_atomic&) ETL_DELETE;
507
508#if ETL_USING_CPP11
510 iqueue_spsc_atomic& operator =(iqueue_spsc_atomic&&) = delete;
511#endif
512
513 T* p_buffer;
514 };
515
516 //***************************************************************************
523 //***************************************************************************
524 template <typename T, size_t Size, const size_t Memory_Model = etl::memory_model::MEMORY_MODEL_LARGE>
525 class queue_spsc_atomic : public iqueue_spsc_atomic<T, Memory_Model>
526 {
527 private:
528
529 typedef typename etl::iqueue_spsc_atomic<T, Memory_Model> base_t;
530
531 public:
532
533 typedef typename base_t::size_type size_type;
534
535 private:
536
537 static ETL_CONSTANT size_type Reserved_Size = size_type(Size + 1);
538
539 public:
540
541 ETL_STATIC_ASSERT((Size <= (etl::integral_limits<size_type>::max - 1)), "Size too large for memory model");
542
543 static ETL_CONSTANT size_type MAX_SIZE = size_type(Size);
544
545 //*************************************************************************
547 //*************************************************************************
549 : base_t(reinterpret_cast<T*>(&buffer[0]), Reserved_Size)
550 {
551 }
552
553 //*************************************************************************
555 //*************************************************************************
557 {
558 base_t::clear();
559 }
560
561 private:
562
564 typename etl::aligned_storage<sizeof(T), etl::alignment_of<T>::value>::type buffer[Reserved_Size];
565 };
566
567 template <typename T, size_t Size, const size_t Memory_Model>
568 ETL_CONSTANT typename queue_spsc_atomic<T, Size, Memory_Model>::size_type queue_spsc_atomic<T, Size, Memory_Model>::MAX_SIZE;
569}
570
571#endif
572
573#endif
Definition alignment.h:231
Definition integral_limits.h:516
add_rvalue_reference
Definition type_traits_generator.h:1322
bitset_ext
Definition absolute.h:38
size_t max_size() const
Returns the maximum number of items in the variant_pool.
Definition variant_pool_generator.h:395
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:1187
bool write(etl::bit_stream_writer &stream, bool value)
Definition bit_stream.h:995
pair holds two objects of arbitrary type
Definition utility.h:164
ETL_CONSTEXPR pair()
Default constructor.
Definition utility.h:176
Definition memory_model.h:50