join 1.0
lightweight network framework library
Loading...
Searching...
No Matches
queue.hpp
Go to the documentation of this file.
1
25#ifndef JOIN_CORE_QUEUE_HPP
26#define JOIN_CORE_QUEUE_HPP
27
28// libjoin.
29#include <join/backoff.hpp>
30#include <join/memory.hpp>
31#include <join/utils.hpp>
32
33// C++.
34#include <type_traits>
35#include <algorithm>
36#include <atomic>
37
38// C.
39#include <sys/types.h>
40#include <cstring>
41
42namespace join
43{
47 struct QueueSync
48 {
50 static constexpr uint64_t MAGIC = 0x9F7E3B2A8D5C4E1B;
51
53 alignas (64) std::atomic_uint64_t _magic;
54
56 alignas (64) std::atomic_uint64_t _head;
57
59 uint64_t _cachedTail;
60
62 alignas (64) std::atomic_uint64_t _tail;
63
65 uint64_t _cachedHead;
66 };
67
71 template <typename Type>
73 {
75 Type data;
76 };
77
81 template <typename Type>
83 {
85 alignas (64) std::atomic_uint64_t _seq;
86
88 Type data;
89
91 char _padding[(64 - ((sizeof (std::atomic_uint64_t) + sizeof (Type)) % 64)) % 64];
92 };
93
97 template <typename Type, typename Slot>
99 {
101 alignas (64) QueueSync _sync;
102
104 Slot _elements[];
105 };
106
107 // forward declarations.
108 template <typename Type, typename Backend>
109 struct Spsc;
110
115 template <typename SyncPolicy>
116 struct needs_seq : std::true_type
117 {
118 };
119
123 template <typename Type, typename Backend>
124 struct needs_seq<Spsc<Type, Backend>> : std::false_type
125 {
126 };
127
131 template <typename Type, typename Backend, typename SyncPolicy>
133 {
134 static_assert (std::is_trivially_copyable<Type>::value, "type must be trivially copyable");
135 static_assert (std::is_trivially_destructible<Type>::value, "type must be trivially destructible");
136
137 public:
138 using ValueType = Type;
139 using Slot =
140 typename std::conditional<needs_seq<SyncPolicy>::value, QueueSlotFull<Type>, QueueSlotLight<Type>>::type;
142
148 template <typename... Args>
149 explicit BasicQueue (uint64_t capacity, Args&&... args)
150 : _capacity (nextPow2 (capacity))
151 , _mask (_capacity - 1)
152 , _elementSize (sizeof (Slot))
153 , _totalSize (sizeof (QueueSync) + (_capacity * _elementSize))
154 , _backend (_totalSize, std::forward<Args> (args)...)
155 , _segment (static_cast<Segment*> (_backend.get ()))
156 {
157 uint64_t expected = 0;
158
159 if (_segment->_sync._magic.compare_exchange_strong (expected, 0xFFFFFFFFFFFFFFFF,
160 std::memory_order_acq_rel))
161 {
162 _segment->_sync._head.store (0, std::memory_order_relaxed);
163 _segment->_sync._cachedTail = 0;
164 _segment->_sync._tail.store (0, std::memory_order_relaxed);
165 _segment->_sync._cachedHead = 0;
166
167 initSlots<needs_seq<SyncPolicy>::value> ();
168
169 _segment->_sync._magic.store (QueueSync::MAGIC, std::memory_order_release);
170 }
171 else
172 {
173 Backoff backoff;
174 while (_segment->_sync._magic.load (std::memory_order_acquire) != QueueSync::MAGIC)
175 {
176 backoff (); // LCOV_EXCL_LINE
177 }
178 }
179 }
180
185 BasicQueue (const BasicQueue& other) = delete;
186
192 BasicQueue& operator= (const BasicQueue& other) = delete;
193
198 BasicQueue (BasicQueue&& other) = delete;
199
205 BasicQueue& operator= (BasicQueue&& other) = delete;
206
210 ~BasicQueue () noexcept = default;
211
217 int tryPush (const Type& element) noexcept
218 {
219 return SyncPolicy::tryPush (_segment, element, _capacity, _mask);
220 }
221
228 ssize_t tryPush (const Type* elements, size_t size) noexcept
229 {
230 return SyncPolicy::tryPush (_segment, elements, size, _capacity, _mask);
231 }
232
238 int push (const Type& element) noexcept
239 {
240 Backoff backoff;
241
242 while (tryPush (element) == -1)
243 {
244 if (JOIN_UNLIKELY (lastError != Errc::TemporaryError))
245 {
246 return -1; // LCOV_EXCL_LINE
247 }
248
249 backoff ();
250 }
251
252 return 0;
253 }
254
261 int push (const Type* elements, size_t size) noexcept
262 {
263 Backoff backoff;
264 uint64_t pushed = 0;
265
266 while (pushed < size)
267 {
268 ssize_t n = tryPush (elements + pushed, size - pushed);
269 if (n == -1)
270 {
271 if (JOIN_UNLIKELY (lastError != Errc::TemporaryError))
272 {
273 return -1;
274 }
275
276 backoff (); // LCOV_EXCL_LINE
277 }
278 else
279 {
280 pushed += static_cast<uint64_t> (n);
281 }
282 }
283
284 return 0;
285 }
286
292 int tryPop (Type& element) noexcept
293 {
294 return SyncPolicy::tryPop (_segment, element, _capacity, _mask);
295 }
296
303 ssize_t tryPop (Type* elements, size_t size) noexcept
304 {
305 return SyncPolicy::tryPop (_segment, elements, size, _capacity, _mask);
306 }
307
313 int pop (Type& element) noexcept
314 {
315 Backoff backoff;
316
317 while (tryPop (element) == -1)
318 {
319 if (JOIN_UNLIKELY (lastError != Errc::TemporaryError))
320 {
321 return -1; // LCOV_EXCL_LINE
322 }
323
324 backoff ();
325 }
326
327 return 0;
328 }
329
336 int pop (Type* elements, size_t size) noexcept
337 {
338 Backoff backoff;
339 uint64_t popped = 0;
340
341 while (popped < size)
342 {
343 ssize_t n = tryPop (elements + popped, size - popped);
344 if (n == -1)
345 {
346 if (JOIN_UNLIKELY (lastError != Errc::TemporaryError))
347 {
348 return -1;
349 }
350
351 backoff (); // LCOV_EXCL_LINE
352 }
353 else
354 {
355 popped += static_cast<uint64_t> (n);
356 }
357 }
358
359 return 0;
360 }
361
366 uint64_t pending () const noexcept
367 {
368 if (JOIN_UNLIKELY (_segment == nullptr))
369 {
370 return 0; // LCOV_EXCL_LINE
371 }
372 auto head = _segment->_sync._head.load (std::memory_order_acquire);
373 auto tail = _segment->_sync._tail.load (std::memory_order_relaxed);
374 return head - tail;
375 }
376
381 uint64_t available () const noexcept
382 {
383 if (JOIN_UNLIKELY (_segment == nullptr))
384 {
385 return 0; // LCOV_EXCL_LINE
386 }
387 return _capacity - pending ();
388 }
389
394 bool full () const noexcept
395 {
396 if (JOIN_UNLIKELY (_segment == nullptr))
397 {
398 return false; // LCOV_EXCL_LINE
399 }
400 return pending () == _capacity;
401 }
402
407 bool empty () const noexcept
408 {
409 if (JOIN_UNLIKELY (_segment == nullptr))
410 {
411 return true; // LCOV_EXCL_LINE
412 }
413 return pending () == 0;
414 }
415
416#ifdef JOIN_HAS_NUMA
422 int mbind (int numa) const noexcept
423 {
424 return _backend.mbind (numa);
425 }
426#endif
427
432 int mlock () const noexcept
433 {
434 return _backend.mlock ();
435 }
436
437 private:
441 template <bool NeedsSeq, typename std::enable_if<!NeedsSeq>::type* = nullptr>
442 void initSlots () noexcept
443 {
444 // nothing to initialize.
445 }
446
450 template <bool NeedsSeq, typename std::enable_if<NeedsSeq>::type* = nullptr>
451 void initSlots () noexcept
452 {
453 for (uint64_t i = 0; i < _capacity; ++i)
454 {
455 _segment->_elements[i]._seq.store (i, std::memory_order_relaxed);
456 }
457 }
458
460 alignas (64) const uint64_t _capacity = 0;
461
463 const uint64_t _mask = 0;
464
466 const uint64_t _elementSize = 0;
467
469 const uint64_t _totalSize = 0;
470
472 Backend _backend;
473
475 Segment* _segment = nullptr;
476 };
477
481 template <typename Type, typename Backend>
482 struct Spsc
483 {
486
495 static int tryPush (Segment* segment, const Type& element, uint64_t capacity, uint64_t mask) noexcept
496 {
497 if (JOIN_UNLIKELY (segment == nullptr))
498 {
499 // LCOV_EXCL_START
500 lastError = make_error_code (Errc::InvalidParam);
501 return -1;
502 // LCOV_EXCL_STOP
503 }
504
505 auto& sync = segment->_sync;
506 uint64_t head = sync._head.load (std::memory_order_relaxed);
507
508 if (JOIN_UNLIKELY ((head - sync._cachedTail) == capacity))
509 {
510 sync._cachedTail = sync._tail.load (std::memory_order_acquire);
511 if ((head - sync._cachedTail) == capacity)
512 {
513 lastError = make_error_code (Errc::TemporaryError);
514 return -1;
515 }
516 }
517
518 segment->_elements[head & mask].data = element;
519 sync._head.store (head + 1, std::memory_order_release);
520
521 return 0;
522 }
523
533 static ssize_t tryPush (Segment* segment, const Type* elements, size_t size, uint64_t capacity,
534 uint64_t mask) noexcept
535 {
536 if (JOIN_UNLIKELY (segment == nullptr || elements == nullptr || size == 0))
537 {
538 lastError = make_error_code (Errc::InvalidParam);
539 return -1;
540 }
541
542 auto& sync = segment->_sync;
543 uint64_t head = sync._head.load (std::memory_order_relaxed);
544 uint64_t avail = capacity - (head - sync._cachedTail);
545
546 if (JOIN_UNLIKELY (avail == 0))
547 {
548 sync._cachedTail = sync._tail.load (std::memory_order_acquire);
549 avail = capacity - (head - sync._cachedTail);
550 if (avail == 0)
551 {
552 lastError = make_error_code (Errc::TemporaryError);
553 return -1;
554 }
555 }
556
557 uint64_t toWrite = std::min (static_cast<uint64_t> (size), avail);
558 uint64_t offset = head & mask;
559 uint64_t first = std::min (toWrite, capacity - offset);
560
561 std::memcpy (&segment->_elements[offset], elements, first * sizeof (Type));
562
563 if (toWrite > first)
564 {
565 std::memcpy (&segment->_elements[0], elements + first, (toWrite - first) * sizeof (Type));
566 }
567
568 sync._head.store (head + toWrite, std::memory_order_release);
569
570 return static_cast<ssize_t> (toWrite);
571 }
572
581 static int tryPop (Segment* segment, Type& element, uint64_t /*capacity*/, uint64_t mask) noexcept
582 {
583 if (JOIN_UNLIKELY (segment == nullptr))
584 {
585 // LCOV_EXCL_START
586 lastError = make_error_code (Errc::InvalidParam);
587 return -1;
588 // LCOV_EXCL_STOP
589 }
590
591 auto& sync = segment->_sync;
592 uint64_t tail = sync._tail.load (std::memory_order_relaxed);
593
594 if (sync._cachedHead == tail)
595 {
596 sync._cachedHead = sync._head.load (std::memory_order_acquire);
597 if (sync._cachedHead == tail)
598 {
599 lastError = make_error_code (Errc::TemporaryError);
600 return -1;
601 }
602 }
603
604 element = segment->_elements[tail & mask].data;
605 sync._tail.store (tail + 1, std::memory_order_release);
606
607 return 0;
608 }
609
619 static ssize_t tryPop (Segment* segment, Type* elements, size_t size, uint64_t capacity, uint64_t mask) noexcept
620 {
621 if (JOIN_UNLIKELY (segment == nullptr || elements == nullptr || size == 0))
622 {
623 lastError = make_error_code (Errc::InvalidParam);
624 return -1;
625 }
626
627 auto& sync = segment->_sync;
628 uint64_t tail = sync._tail.load (std::memory_order_relaxed);
629 uint64_t pending = sync._cachedHead - tail;
630
631 if (pending == 0)
632 {
633 sync._cachedHead = sync._head.load (std::memory_order_acquire);
634 pending = sync._cachedHead - tail;
635 if (pending == 0)
636 {
637 lastError = make_error_code (Errc::TemporaryError);
638 return -1;
639 }
640 }
641
642 uint64_t toRead = std::min (static_cast<uint64_t> (size), pending);
643 uint64_t offset = tail & mask;
644 uint64_t first = std::min (toRead, capacity - offset);
645
646 std::memcpy (elements, &segment->_elements[offset], first * sizeof (Type));
647
648 if (toRead > first)
649 {
650 std::memcpy (elements + first, &segment->_elements[0], (toRead - first) * sizeof (Type));
651 }
652
653 sync._tail.store (tail + toRead, std::memory_order_release);
654
655 return static_cast<ssize_t> (toRead);
656 }
657 };
658
662 template <typename Type, typename Backend>
663 struct Mpsc
664 {
667
676 static int tryPush (Segment* segment, const Type& element, uint64_t /*capacity*/, uint64_t mask) noexcept
677 {
678 if (JOIN_UNLIKELY (segment == nullptr))
679 {
680 // LCOV_EXCL_START
681 lastError = make_error_code (Errc::InvalidParam);
682 return -1;
683 // LCOV_EXCL_STOP
684 }
685
686 auto& sync = segment->_sync;
687 uint64_t head = sync._head.load (std::memory_order_relaxed);
688 Backoff backoff;
689
690 for (;;)
691 {
692 auto* slot = &segment->_elements[head & mask];
693 uint64_t seq = slot->_seq.load (std::memory_order_acquire);
694
695 if (seq == head)
696 {
697 if (JOIN_LIKELY (sync._head.compare_exchange_weak (head, head + 1, std::memory_order_acquire,
698 std::memory_order_relaxed)))
699 {
700 slot->data = element;
701 slot->_seq.store (head + 1, std::memory_order_release);
702 return 0;
703 }
704 }
705 else if (JOIN_UNLIKELY (seq < head))
706 {
707 lastError = make_error_code (Errc::TemporaryError);
708 return -1;
709 }
710 else
711 {
712 backoff ();
713 head = sync._head.load (std::memory_order_relaxed);
714 }
715 }
716 }
717
727 static ssize_t tryPush (Segment* segment, const Type* elements, size_t size, uint64_t capacity,
728 uint64_t mask) noexcept
729 {
730 if (JOIN_UNLIKELY (segment == nullptr || elements == nullptr || size == 0))
731 {
732 lastError = make_error_code (Errc::InvalidParam);
733 return -1;
734 }
735
736 Backoff backoff;
737 auto& sync = segment->_sync;
738 uint64_t head = sync._head.load (std::memory_order_relaxed);
739
740 for (;;)
741 {
742 uint64_t tail = sync._tail.load (std::memory_order_acquire);
743 uint64_t toWrite = std::min (static_cast<uint64_t> (size), capacity - (head - tail));
744
745 if (JOIN_UNLIKELY (toWrite == 0))
746 {
747 lastError = make_error_code (Errc::TemporaryError);
748 return -1;
749 }
750
751 if (JOIN_LIKELY (sync._head.compare_exchange_weak (head, head + toWrite, std::memory_order_acquire,
752 std::memory_order_relaxed)))
753 {
754 for (uint64_t i = 0; i < toWrite; ++i)
755 {
756 auto* slot = &segment->_elements[(head + i) & mask];
757 slot->data = elements[i];
758 slot->_seq.store (head + i + 1, std::memory_order_release);
759 }
760
761 return static_cast<ssize_t> (toWrite);
762 }
763
764 backoff (); // LCOV_EXCL_LINE
765 }
766 }
767
776 static int tryPop (Segment* segment, Type& element, uint64_t capacity, uint64_t mask) noexcept
777 {
778 if (JOIN_UNLIKELY (segment == nullptr))
779 {
780 // LCOV_EXCL_START
781 lastError = make_error_code (Errc::InvalidParam);
782 return -1;
783 // LCOV_EXCL_STOP
784 }
785
786 auto& sync = segment->_sync;
787 uint64_t tail = sync._tail.load (std::memory_order_relaxed);
788 auto* slot = &segment->_elements[tail & mask];
789 uint64_t seq = slot->_seq.load (std::memory_order_acquire);
790
791 if (JOIN_UNLIKELY (seq != tail + 1))
792 {
793 lastError = make_error_code (Errc::TemporaryError);
794 return -1;
795 }
796
797 element = slot->data;
798 slot->_seq.store (tail + capacity, std::memory_order_release);
799 sync._tail.store (tail + 1, std::memory_order_release);
800
801 return 0;
802 }
803
813 static ssize_t tryPop (Segment* segment, Type* elements, size_t size, uint64_t capacity, uint64_t mask) noexcept
814 {
815 if (JOIN_UNLIKELY (segment == nullptr || elements == nullptr || size == 0))
816 {
817 lastError = make_error_code (Errc::InvalidParam);
818 return -1;
819 }
820
821 auto& sync = segment->_sync;
822 uint64_t tail = sync._tail.load (std::memory_order_relaxed);
823 uint64_t head = sync._head.load (std::memory_order_acquire);
824 uint64_t toRead = std::min (static_cast<uint64_t> (size), head - tail);
825 uint64_t popped = 0;
826
827 for (uint64_t i = 0; i < toRead; ++i)
828 {
829 auto* slot = &segment->_elements[(tail + i) & mask];
830
831 if (JOIN_UNLIKELY (slot->_seq.load (std::memory_order_acquire) != tail + i + 1))
832 {
833 break;
834 }
835
836 elements[i] = slot->data;
837 slot->_seq.store (tail + i + capacity, std::memory_order_release);
838 ++popped;
839 }
840
841 if (JOIN_LIKELY (popped > 0))
842 {
843 sync._tail.store (tail + popped, std::memory_order_release);
844 return static_cast<ssize_t> (popped);
845 }
846
847 lastError = make_error_code (Errc::TemporaryError);
848 return -1;
849 }
850 };
851
855 template <typename Type, typename Backend>
856 struct Mpmc
857 {
860
869 static int tryPush (Segment* segment, const Type& element, uint64_t capacity, uint64_t mask) noexcept
870 {
871 return Mpsc<Type, Backend>::tryPush (segment, element, capacity, mask);
872 }
873
883 static ssize_t tryPush (Segment* segment, const Type* elements, size_t size, uint64_t capacity,
884 uint64_t mask) noexcept
885 {
886 if (JOIN_UNLIKELY (segment == nullptr || elements == nullptr || size == 0))
887 {
888 lastError = make_error_code (Errc::InvalidParam);
889 return -1;
890 }
891
892 Backoff backoff;
893 auto& sync = segment->_sync;
894 uint64_t head = sync._head.load (std::memory_order_relaxed);
895
896 for (;;)
897 {
898 uint64_t tail = sync._tail.load (std::memory_order_acquire);
899 uint64_t toWrite = std::min (static_cast<uint64_t> (size), capacity - (head - tail));
900
901 if (JOIN_UNLIKELY (toWrite == 0))
902 {
903 lastError = make_error_code (Errc::TemporaryError);
904 return -1;
905 }
906
907 if (JOIN_LIKELY (sync._head.compare_exchange_weak (head, head + toWrite, std::memory_order_acquire,
908 std::memory_order_relaxed)))
909 {
910 for (uint64_t i = 0; i < toWrite; ++i)
911 {
912 auto* slot = &segment->_elements[(head + i) & mask];
913 Backoff slotBackoff;
914 while (slot->_seq.load (std::memory_order_acquire) != head + i)
915 {
916 slotBackoff (); // LCOV_EXCL_LINE
917 }
918 slot->data = elements[i];
919 slot->_seq.store (head + i + 1, std::memory_order_release);
920 }
921
922 return static_cast<ssize_t> (toWrite);
923 }
924
925 backoff (); // LCOV_EXCL_LINE
926 }
927 }
928
937 static int tryPop (Segment* segment, Type& element, uint64_t capacity, uint64_t mask) noexcept
938 {
939 if (JOIN_UNLIKELY (segment == nullptr))
940 {
941 // LCOV_EXCL_START
942 lastError = make_error_code (Errc::InvalidParam);
943 return -1;
944 // LCOV_EXCL_STOP
945 }
946
947 auto& sync = segment->_sync;
948 uint64_t tail = sync._tail.load (std::memory_order_relaxed);
949 Backoff backoff;
950
951 for (;;)
952 {
953 auto* slot = &segment->_elements[tail & mask];
954 uint64_t seq = slot->_seq.load (std::memory_order_acquire);
955
956 if (seq == (tail + 1))
957 {
958 Type local = slot->data;
959 if (JOIN_LIKELY (sync._tail.compare_exchange_weak (tail, tail + 1, std::memory_order_acquire,
960 std::memory_order_relaxed)))
961 {
962 element = local;
963 slot->_seq.store (tail + capacity, std::memory_order_release);
964 return 0;
965 }
966 }
967 else if (JOIN_UNLIKELY (seq < (tail + 1)))
968 {
969 lastError = make_error_code (Errc::TemporaryError);
970 return -1;
971 }
972 else
973 {
974 backoff ();
975 tail = sync._tail.load (std::memory_order_relaxed);
976 }
977 }
978 }
979
989 static ssize_t tryPop (Segment* segment, Type* elements, size_t size, uint64_t capacity, uint64_t mask) noexcept
990 {
991 if (JOIN_UNLIKELY (segment == nullptr || elements == nullptr || size == 0))
992 {
993 lastError = make_error_code (Errc::InvalidParam);
994 return -1;
995 }
996
997 Backoff backoff;
998 auto& sync = segment->_sync;
999 uint64_t tail = sync._tail.load (std::memory_order_relaxed);
1000
1001 for (;;)
1002 {
1003 uint64_t head = sync._head.load (std::memory_order_acquire);
1004 uint64_t toRead = std::min (static_cast<uint64_t> (size), head - tail);
1005
1006 if (JOIN_UNLIKELY (toRead == 0))
1007 {
1008 lastError = make_error_code (Errc::TemporaryError);
1009 return -1;
1010 }
1011
1012 uint64_t ready = 0;
1013 for (; ready < toRead; ++ready)
1014 {
1015 auto* slot = &segment->_elements[(tail + ready) & mask];
1016 if (JOIN_UNLIKELY (slot->_seq.load (std::memory_order_acquire) != tail + ready + 1))
1017 {
1018 break;
1019 }
1020 elements[ready] = slot->data;
1021 }
1022
1023 if (JOIN_UNLIKELY (ready == 0))
1024 {
1025 lastError = make_error_code (Errc::TemporaryError);
1026 return -1;
1027 }
1028
1029 if (JOIN_LIKELY (sync._tail.compare_exchange_weak (tail, tail + ready, std::memory_order_acquire,
1030 std::memory_order_relaxed)))
1031 {
1032 for (uint64_t i = 0; i < ready; ++i)
1033 {
1034 segment->_elements[(tail + i) & mask]._seq.store (tail + i + capacity,
1035 std::memory_order_release);
1036 }
1037
1038 return static_cast<ssize_t> (ready);
1039 }
1040
1041 backoff (); // LCOV_EXCL_LINE
1042 }
1043 }
1044 };
1045
1049 template <typename Backend, template <typename, typename> class SyncPolicy>
1051 {
1053 template <typename Type>
1055 };
1056}
1057
1058#endif
adaptive backoff strategy for busy-wait loops.
Definition backoff.hpp:43
queue base class.
Definition queue.hpp:133
int push(const Type *elements, size_t size) noexcept
push multiple elements into the ring buffer.
Definition queue.hpp:261
BasicQueue(uint64_t capacity, Args &&... args)
create instance.
Definition queue.hpp:149
typename std::conditional< needs_seq< SyncPolicy >::value, QueueSlotFull< Type >, QueueSlotLight< Type > >::type Slot
Definition queue.hpp:139
int push(const Type &element) noexcept
push element into the ring buffer.
Definition queue.hpp:238
int mlock() const noexcept
lock memory in RAM.
Definition queue.hpp:432
ssize_t tryPop(Type *elements, size_t size) noexcept
try to pop multiple elements from the ring buffer.
Definition queue.hpp:303
int tryPop(Type &element) noexcept
try to pop element from the ring buffer.
Definition queue.hpp:292
~BasicQueue() noexcept=default
destroy queue instance.
int pop(Type *elements, size_t size) noexcept
pop multiple elements from the ring buffer.
Definition queue.hpp:336
BasicQueue(BasicQueue &&other)=delete
move constructor.
int pop(Type &element) noexcept
pop element from the ring buffer.
Definition queue.hpp:313
bool full() const noexcept
check if the ring buffer is full.
Definition queue.hpp:394
ssize_t tryPush(const Type *elements, size_t size) noexcept
try to push multiple elements into the ring buffer.
Definition queue.hpp:228
bool empty() const noexcept
check if the ring buffer is empty.
Definition queue.hpp:407
uint64_t pending() const noexcept
get the number of pending elements for reading.
Definition queue.hpp:366
uint64_t available() const noexcept
get the number of available slots for writing.
Definition queue.hpp:381
Type ValueType
Definition queue.hpp:138
BasicQueue(const BasicQueue &other)=delete
copy constructor.
Definition acceptor.hpp:32
constexpr uint64_t nextPow2(uint64_t value) noexcept
round up to the next power of two.
Definition utils.hpp:506
std::error_code make_error_code(join::Errc code) noexcept
Create an std::error_code object.
Definition error.cpp:195
Definition error.hpp:144
multiple producer multiple consumer ring buffer.
Definition queue.hpp:857
static ssize_t tryPush(Segment *segment, const Type *elements, size_t size, uint64_t capacity, uint64_t mask) noexcept
try to push multiple elements into the ring buffer.
Definition queue.hpp:883
static ssize_t tryPop(Segment *segment, Type *elements, size_t size, uint64_t capacity, uint64_t mask) noexcept
try to pop multiple elements from the ring buffer.
Definition queue.hpp:989
static int tryPush(Segment *segment, const Type &element, uint64_t capacity, uint64_t mask) noexcept
try to push element into the ring buffer.
Definition queue.hpp:869
static int tryPop(Segment *segment, Type &element, uint64_t capacity, uint64_t mask) noexcept
try to pop element from the ring buffer.
Definition queue.hpp:937
multiple producer single consumer ring buffer.
Definition queue.hpp:664
static ssize_t tryPop(Segment *segment, Type *elements, size_t size, uint64_t capacity, uint64_t mask) noexcept
try to pop multiple elements from the ring buffer.
Definition queue.hpp:813
static int tryPush(Segment *segment, const Type &element, uint64_t, uint64_t mask) noexcept
try to push element into the ring buffer.
Definition queue.hpp:676
static int tryPop(Segment *segment, Type &element, uint64_t capacity, uint64_t mask) noexcept
try to pop element from the ring buffer.
Definition queue.hpp:776
static ssize_t tryPush(Segment *segment, const Type *elements, size_t size, uint64_t capacity, uint64_t mask) noexcept
try to push multiple elements into the ring buffer.
Definition queue.hpp:727
queue memory segment.
Definition queue.hpp:99
Slot _elements[]
flexible array of queue slots.
Definition queue.hpp:104
QueueSync _sync
synchronization primitives.
Definition queue.hpp:101
full queue slot used by MPSC/MPMC.
Definition queue.hpp:83
Type data
stored element data.
Definition queue.hpp:88
std::atomic_uint64_t _seq
sequence number for synchronization.
Definition queue.hpp:85
char _padding[(64 -((sizeof(std::atomic_uint64_t)+sizeof(Type)) % 64)) % 64]
padding to prevent false sharing.
Definition queue.hpp:91
lightweight queue slot used by SPSC.
Definition queue.hpp:73
Type data
stored element data.
Definition queue.hpp:75
queue synchronization primitives.
Definition queue.hpp:48
std::atomic_uint64_t _head
write position.
Definition queue.hpp:56
uint64_t _cachedTail
cached tail index for the producer side.
Definition queue.hpp:59
uint64_t _cachedHead
cached head index for the consumer side.
Definition queue.hpp:65
static constexpr uint64_t MAGIC
magic number for initialization detection.
Definition queue.hpp:50
std::atomic_uint64_t _tail
read position.
Definition queue.hpp:62
std::atomic_uint64_t _magic
initialization state atomic.
Definition queue.hpp:53
single producer single consumer ring buffer.
Definition queue.hpp:483
static ssize_t tryPop(Segment *segment, Type *elements, size_t size, uint64_t capacity, uint64_t mask) noexcept
try to pop multiple elements from the ring buffer.
Definition queue.hpp:619
static ssize_t tryPush(Segment *segment, const Type *elements, size_t size, uint64_t capacity, uint64_t mask) noexcept
try to push multiple elements into the ring buffer.
Definition queue.hpp:533
static int tryPop(Segment *segment, Type &element, uint64_t, uint64_t mask) noexcept
try to pop element from the ring buffer.
Definition queue.hpp:581
static int tryPush(Segment *segment, const Type &element, uint64_t capacity, uint64_t mask) noexcept
try to push element into the ring buffer.
Definition queue.hpp:495
queue forward declarations.
Definition queue.hpp:1051
primary trait: all sync policies need sequence numbers by default.
Definition queue.hpp:117
#define JOIN_LIKELY(x)
Definition utils.hpp:45
#define JOIN_UNLIKELY(x)
Definition utils.hpp:46