Embedded Template Library  1.0
queue_spsc_atomic.h
Go to the documentation of this file.
1 
3 /******************************************************************************
4 The MIT License(MIT)
5 
6 Embedded Template Library.
7 https://github.com/ETLCPP/etl
8 https://www.etlcpp.com
9 
10 Copyright(c) 2018 jwellbelove
11 
12 Permission is hereby granted, free of charge, to any person obtaining a copy
13 of this software and associated documentation files(the "Software"), to deal
14 in the Software without restriction, including without limitation the rights
15 to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
16 copies of the Software, and to permit persons to whom the Software is
17 furnished to do so, subject to the following conditions :
18 
19 The above copyright notice and this permission notice shall be included in all
20 copies or substantial portions of the Software.
21 
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
25 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 SOFTWARE.
29 ******************************************************************************/
30 
31 #ifndef ETL_SPSC_QUEUE_ATOMIC_INCLUDED
32 #define ETL_SPSC_QUEUE_ATOMIC_INCLUDED
33 
34 #include <stddef.h>
35 #include <stdint.h>
36 
37 #include "platform.h"
38 #include "alignment.h"
39 #include "parameter_type.h"
40 #include "atomic.h"
41 #include "memory_model.h"
42 #include "integral_limits.h"
43 #include "utility.h"
44 #include "placement_new.h"
45 
46 #undef ETL_FILE
47 #define ETL_FILE "47"
48 
49 #if ETL_HAS_ATOMIC
50 
51 namespace etl
52 {
53  template <const size_t MEMORY_MODEL = etl::memory_model::MEMORY_MODEL_LARGE>
54  class queue_spsc_atomic_base
55  {
56  public:
57 
59  typedef typename etl::size_type_lookup<MEMORY_MODEL>::type size_type;
60 
61  //*************************************************************************
65  //*************************************************************************
66  bool empty() const
67  {
68  return read.load(etl::memory_order_acquire) == write.load(etl::memory_order_acquire);
69  }
70 
71  //*************************************************************************
75  //*************************************************************************
76  bool full() const
77  {
78  size_type next_index = get_next_index(write.load(etl::memory_order_acquire), RESERVED);
79 
80  return (next_index == read.load(etl::memory_order_acquire));
81  }
82 
83  //*************************************************************************
86  //*************************************************************************
87  size_type size() const
88  {
89  size_type write_index = write.load(etl::memory_order_acquire);
90  size_type read_index = read.load(etl::memory_order_acquire);
91 
92  size_type n;
93 
94  if (write_index >= read_index)
95  {
96  n = write_index - read_index;
97  }
98  else
99  {
100  n = RESERVED - read_index + write_index - 1;
101  }
102 
103  return n;
104  }
105 
106  //*************************************************************************
109  //*************************************************************************
110  size_type available() const
111  {
112  return RESERVED - size() - 1;
113  }
114 
115  //*************************************************************************
117  //*************************************************************************
118  size_type capacity() const
119  {
120  return RESERVED - 1;
121  }
122 
123  //*************************************************************************
125  //*************************************************************************
126  size_type max_size() const
127  {
128  return RESERVED - 1;
129  }
130 
131  protected:
132 
133  queue_spsc_atomic_base(size_type reserved_)
134  : write(0),
135  read(0),
136  RESERVED(reserved_)
137  {
138  }
139 
140  //*************************************************************************
142  //*************************************************************************
143  static size_type get_next_index(size_type index, size_type maximum)
144  {
145  ++index;
146 
147  if (index == maximum)
148  {
149  index = 0;
150  }
151 
152  return index;
153  }
154 
155  etl::atomic<size_type> write;
157  const size_type RESERVED;
158 
159  private:
160 
161  //*************************************************************************
163  //*************************************************************************
164 #if defined(ETL_POLYMORPHIC_SPSC_QUEUE_ATOMIC) || defined(ETL_POLYMORPHIC_CONTAINERS)
165  public:
166  virtual ~queue_spsc_atomic_base()
167  {
168  }
169 #else
170  protected:
171  ~queue_spsc_atomic_base()
172  {
173  }
174 #endif
175  };
176 
177  //***************************************************************************
187  //***************************************************************************
188  template <typename T, const size_t MEMORY_MODEL = etl::memory_model::MEMORY_MODEL_LARGE>
189  class iqueue_spsc_atomic : public queue_spsc_atomic_base<MEMORY_MODEL>
190  {
191  private:
192 
193  typedef typename etl::queue_spsc_atomic_base<MEMORY_MODEL> base_t;
194 
195  public:
196 
197  typedef T value_type;
198  typedef T& reference;
199  typedef const T& const_reference;
200 #if ETL_CPP11_SUPPORTED
201  typedef T&& rvalue_reference;
202 #endif
203  typedef typename base_t::size_type size_type;
204 
205  using base_t::write;
206  using base_t::read;
207  using base_t::RESERVED;
208  using base_t::get_next_index;
209 
210  //*************************************************************************
212  //*************************************************************************
213  bool push(const_reference value)
214  {
215  size_type write_index = write.load(etl::memory_order_relaxed);
216  size_type next_index = get_next_index(write_index, RESERVED);
217 
218  if (next_index != read.load(etl::memory_order_acquire))
219  {
220  ::new (&p_buffer[write_index]) T(value);
221 
222  write.store(next_index, etl::memory_order_release);
223 
224  return true;
225  }
226 
227  // Queue is full.
228  return false;
229  }
230 
231 #if ETL_CPP11_SUPPORTED
232  //*************************************************************************
234  //*************************************************************************
235  bool push(rvalue_reference value)
236  {
237  size_type write_index = write.load(etl::memory_order_relaxed);
238  size_type next_index = get_next_index(write_index, RESERVED);
239 
240  if (next_index != read.load(etl::memory_order_acquire))
241  {
242  ::new (&p_buffer[write_index]) T(etl::move(value));
243 
244  write.store(next_index, etl::memory_order_release);
245 
246  return true;
247  }
248 
249  // Queue is full.
250  return false;
251  }
252 #endif
253 
254 #if ETL_CPP11_SUPPORTED && ETL_NOT_USING_STLPORT && !defined(ETL_QUEUE_ATOMIC_FORCE_CPP03)
255  //*************************************************************************
258  //*************************************************************************
259  template <typename ... Args>
260  bool emplace(Args&&... args)
261  {
262  size_type write_index = write.load(etl::memory_order_relaxed);
263  size_type next_index = get_next_index(write_index, RESERVED);
264 
265  if (next_index != read.load(etl::memory_order_acquire))
266  {
267  ::new (&p_buffer[write_index]) T(etl::forward<Args>(args)...);
268 
269  write.store(next_index, etl::memory_order_release);
270 
271  return true;
272  }
273 
274  // Queue is full.
275  return false;
276  }
277 #else
278  //*************************************************************************
281  //*************************************************************************
282  template <typename T1>
283  bool emplace(const T1& value1)
284  {
285  size_type write_index = write.load(etl::memory_order_relaxed);
286  size_type next_index = get_next_index(write_index, RESERVED);
287 
288  if (next_index != read.load(etl::memory_order_acquire))
289  {
290  ::new (&p_buffer[write_index]) T(value1);
291 
292  write.store(next_index, etl::memory_order_release);
293 
294  return true;
295  }
296 
297  // Queue is full.
298  return false;
299  }
300 
301  //*************************************************************************
304  //*************************************************************************
305  template <typename T1, typename T2>
306  bool emplace(const T1& value1, const T2& value2)
307  {
308  size_type write_index = write.load(etl::memory_order_relaxed);
309  size_type next_index = get_next_index(write_index, RESERVED);
310 
311  if (next_index != read.load(etl::memory_order_acquire))
312  {
313  ::new (&p_buffer[write_index]) T(value1, value2);
314 
315  write.store(next_index, etl::memory_order_release);
316 
317  return true;
318  }
319 
320  // Queue is full.
321  return false;
322  }
323 
324  //*************************************************************************
327  //*************************************************************************
328  template <typename T1, typename T2, typename T3>
329  bool emplace(const T1& value1, const T2& value2, const T3& value3)
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, value3);
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  //*************************************************************************
350  //*************************************************************************
351  template <typename T1, typename T2, typename T3, typename T4>
352  bool emplace(const T1& value1, const T2& value2, const T3& value3, const T4& value4)
353  {
354  size_type write_index = write.load(etl::memory_order_relaxed);
355  size_type next_index = get_next_index(write_index, RESERVED);
356 
357  if (next_index != read.load(etl::memory_order_acquire))
358  {
359  ::new (&p_buffer[write_index]) T(value1, value2, value3, value4);
360 
361  write.store(next_index, etl::memory_order_release);
362 
363  return true;
364  }
365 
366  // Queue is full.
367  return false;
368  }
369 #endif
370 
371  //*************************************************************************
373  //*************************************************************************
374  bool pop(reference value)
375  {
376  size_type read_index = read.load(etl::memory_order_relaxed);
377 
378  if (read_index == write.load(etl::memory_order_acquire))
379  {
380  // Queue is empty
381  return false;
382  }
383 
384  size_type next_index = get_next_index(read_index, RESERVED);
385 
386  value = p_buffer[read_index];
387  p_buffer[read_index].~T();
388 
389  read.store(next_index, etl::memory_order_release);
390 
391  return true;
392  }
393 
394 #if ETL_CPP11_SUPPORTED
395  //*************************************************************************
397  //*************************************************************************
398  bool pop(rvalue_reference value)
399  {
400  size_type read_index = read.load(etl::memory_order_relaxed);
401 
402  if (read_index == write.load(etl::memory_order_acquire))
403  {
404  // Queue is empty
405  return false;
406  }
407 
408  size_type next_index = get_next_index(read_index, RESERVED);
409 
410  value = etl::move(p_buffer[read_index]);
411  p_buffer[read_index].~T();
412 
413  read.store(next_index, etl::memory_order_release);
414 
415  return true;
416  }
417 #endif
418 
419  //*************************************************************************
421  //*************************************************************************
422  bool pop()
423  {
424  size_type read_index = read.load(etl::memory_order_relaxed);
425 
426  if (read_index == write.load(etl::memory_order_acquire))
427  {
428  // Queue is empty
429  return false;
430  }
431 
432  size_type next_index = get_next_index(read_index, RESERVED);
433 
434  p_buffer[read_index].~T();
435 
436  read.store(next_index, etl::memory_order_release);
437 
438  return true;
439  }
440 
441  //*************************************************************************
445  //*************************************************************************
446  void clear()
447  {
448  while (pop())
449  {
450  // Do nothing.
451  }
452  }
453 
454  protected:
455 
456  //*************************************************************************
458  //*************************************************************************
459  iqueue_spsc_atomic(T* p_buffer_, size_type reserved_)
460  : base_t(reserved_),
461  p_buffer(p_buffer_)
462  {
463  }
464 
465  private:
466 
467  // Disable copy construction and assignment.
468  iqueue_spsc_atomic(const iqueue_spsc_atomic&) ETL_DELETE;
469  iqueue_spsc_atomic& operator =(const iqueue_spsc_atomic&) ETL_DELETE;
470 
471 #if ETL_CPP11_SUPPORTED
472  iqueue_spsc_atomic(iqueue_spsc_atomic&&) = delete;
473  iqueue_spsc_atomic& operator =(iqueue_spsc_atomic&&) = delete;
474 #endif
475 
476  T* p_buffer;
477  };
478 
479  //***************************************************************************
486  //***************************************************************************
487  template <typename T, size_t SIZE, const size_t MEMORY_MODEL = etl::memory_model::MEMORY_MODEL_LARGE>
488  class queue_spsc_atomic : public iqueue_spsc_atomic<T, MEMORY_MODEL>
489  {
490  private:
491 
492  typedef typename etl::iqueue_spsc_atomic<T, MEMORY_MODEL> base_t;
493 
494  public:
495 
496  typedef typename base_t::size_type size_type;
497 
498  private:
499 
500  static const size_type RESERVED_SIZE = size_type(SIZE + 1);
501 
502  public:
503 
504  ETL_STATIC_ASSERT((SIZE <= (etl::integral_limits<size_type>::max - 1)), "Size too large for memory model");
505 
506  static const size_type MAX_SIZE = size_type(SIZE);
507 
508  //*************************************************************************
510  //*************************************************************************
511  queue_spsc_atomic()
512  : base_t(reinterpret_cast<T*>(&buffer[0]), RESERVED_SIZE)
513  {
514  }
515 
516  //*************************************************************************
518  //*************************************************************************
519  ~queue_spsc_atomic()
520  {
521  base_t::clear();
522  }
523 
524  private:
525 
527  typename etl::aligned_storage<sizeof(T), etl::alignment_of<T>::value>::type buffer[RESERVED_SIZE];
528  };
529 }
530 
531 #endif
532 
533 #undef ETL_FILE
534 
535 #endif
For all types except bool and pointers.
Definition: atomic_gcc_sync.h:69
Definition: alignment.h:116
Definition: integral_limits.h:54
add_rvalue_reference
Definition: type_traits_generator.h:1348
Definition: absolute.h:37
Definition: memory_model.h:48