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
114 template <typename SyncPolicy>
115 inline constexpr bool needs_seq_v = true;
116
120 template <typename Type, typename Backend>
121 inline constexpr bool needs_seq_v<Spsc<Type, Backend>> = false;
122
126 template <typename Type, typename Backend, typename SyncPolicy>
128 {
129 static_assert (std::is_trivially_copyable_v<Type>, "type must be trivially copyable");
130 static_assert (std::is_trivially_destructible_v<Type>, "type must be trivially destructible");
131
132 public:
133 using ValueType = Type;
134 using Slot = std::conditional_t<needs_seq_v<SyncPolicy>, QueueSlotFull<Type>, QueueSlotLight<Type>>;
136
142 template <typename... Args>
143 explicit BasicQueue (uint64_t capacity, Args&&... args)
144 : _capacity (nextPow2 (capacity))
145 , _mask (_capacity - 1)
146 , _elementSize (sizeof (Slot))
147 , _totalSize (sizeof (QueueSync) + (_capacity * _elementSize))
148 , _backend (_totalSize, std::forward<Args> (args)...)
149 , _segment (static_cast<Segment*> (_backend.get ()))
150 {
151 uint64_t expected = 0;
152
153 if (_segment->_sync._magic.compare_exchange_strong (expected, 0xFFFFFFFFFFFFFFFF,
154 std::memory_order_acq_rel))
155 {
156 _segment->_sync._head.store (0, std::memory_order_relaxed);
157 _segment->_sync._cachedTail = 0;
158 _segment->_sync._tail.store (0, std::memory_order_relaxed);
159 _segment->_sync._cachedHead = 0;
160
161 initSlots ();
162
163 _segment->_sync._magic.store (QueueSync::MAGIC, std::memory_order_release);
164 }
165 else
166 {
167 Backoff backoff;
168 while (_segment->_sync._magic.load (std::memory_order_acquire) != QueueSync::MAGIC)
169 {
170 backoff (); // LCOV_EXCL_LINE
171 }
172 }
173 }
174
179 BasicQueue (const BasicQueue& other) = delete;
180
186 BasicQueue& operator= (const BasicQueue& other) = delete;
187
192 BasicQueue (BasicQueue&& other) = delete;
193
199 BasicQueue& operator= (BasicQueue&& other) = delete;
200
204 ~BasicQueue () noexcept = default;
205
211 int tryPush (const Type& element) noexcept
212 {
213 return SyncPolicy::tryPush (_segment, element, _capacity, _mask);
214 }
215
222 ssize_t tryPush (const Type* elements, size_t size) noexcept
223 {
224 return SyncPolicy::tryPush (_segment, elements, size, _capacity, _mask);
225 }
226
232 int push (const Type& element) noexcept
233 {
234 Backoff backoff;
235
236 while (tryPush (element) == -1)
237 {
238 if (JOIN_UNLIKELY (lastError != Errc::TemporaryError))
239 {
240 return -1; // LCOV_EXCL_LINE
241 }
242
243 backoff ();
244 }
245
246 return 0;
247 }
248
255 int push (const Type* elements, size_t size) noexcept
256 {
257 Backoff backoff;
258 uint64_t pushed = 0;
259
260 while (pushed < size)
261 {
262 ssize_t n = tryPush (elements + pushed, size - pushed);
263 if (n == -1)
264 {
265 if (JOIN_UNLIKELY (lastError != Errc::TemporaryError))
266 {
267 return -1;
268 }
269
270 backoff (); // LCOV_EXCL_LINE
271 }
272 else
273 {
274 pushed += static_cast<uint64_t> (n);
275 }
276 }
277
278 return 0;
279 }
280
286 int tryPop (Type& element) noexcept
287 {
288 return SyncPolicy::tryPop (_segment, element, _capacity, _mask);
289 }
290
297 ssize_t tryPop (Type* elements, size_t size) noexcept
298 {
299 return SyncPolicy::tryPop (_segment, elements, size, _capacity, _mask);
300 }
301
307 int pop (Type& element) noexcept
308 {
309 Backoff backoff;
310
311 while (tryPop (element) == -1)
312 {
313 if (JOIN_UNLIKELY (lastError != Errc::TemporaryError))
314 {
315 return -1; // LCOV_EXCL_LINE
316 }
317
318 backoff ();
319 }
320
321 return 0;
322 }
323
330 int pop (Type* elements, size_t size) noexcept
331 {
332 Backoff backoff;
333 uint64_t popped = 0;
334
335 while (popped < size)
336 {
337 ssize_t n = tryPop (elements + popped, size - popped);
338 if (n == -1)
339 {
340 if (JOIN_UNLIKELY (lastError != Errc::TemporaryError))
341 {
342 return -1;
343 }
344
345 backoff (); // LCOV_EXCL_LINE
346 }
347 else
348 {
349 popped += static_cast<uint64_t> (n);
350 }
351 }
352
353 return 0;
354 }
355
360 uint64_t pending () const noexcept
361 {
362 if (JOIN_UNLIKELY (_segment == nullptr))
363 {
364 return 0; // LCOV_EXCL_LINE
365 }
366 auto head = _segment->_sync._head.load (std::memory_order_acquire);
367 auto tail = _segment->_sync._tail.load (std::memory_order_relaxed);
368 return head - tail;
369 }
370
375 uint64_t available () const noexcept
376 {
377 if (JOIN_UNLIKELY (_segment == nullptr))
378 {
379 return 0; // LCOV_EXCL_LINE
380 }
381 return _capacity - pending ();
382 }
383
388 bool full () const noexcept
389 {
390 if (JOIN_UNLIKELY (_segment == nullptr))
391 {
392 return false; // LCOV_EXCL_LINE
393 }
394 return pending () == _capacity;
395 }
396
401 bool empty () const noexcept
402 {
403 if (JOIN_UNLIKELY (_segment == nullptr))
404 {
405 return true; // LCOV_EXCL_LINE
406 }
407 return pending () == 0;
408 }
409
410#ifdef JOIN_HAS_NUMA
416 int mbind (int numa) const noexcept
417 {
418 return _backend.mbind (numa);
419 }
420#endif
421
426 int mlock () const noexcept
427 {
428 return _backend.mlock ();
429 }
430
431 private:
435 void initSlots () noexcept
436 {
437 if constexpr (needs_seq_v<SyncPolicy>)
438 {
439 for (uint64_t i = 0; i < _capacity; ++i)
440 {
441 _segment->_elements[i]._seq.store (i, std::memory_order_relaxed);
442 }
443 }
444 }
445
447 alignas (64) const uint64_t _capacity = 0;
448
450 const uint64_t _mask = 0;
451
453 const uint64_t _elementSize = 0;
454
456 const uint64_t _totalSize = 0;
457
459 Backend _backend;
460
462 Segment* _segment = nullptr;
463 };
464
468 template <typename Type, typename Backend>
469 struct Spsc
470 {
473
482 static int tryPush (Segment* segment, const Type& element, uint64_t capacity, uint64_t mask) noexcept
483 {
484 if (JOIN_UNLIKELY (segment == nullptr))
485 {
486 // LCOV_EXCL_START
487 lastError = make_error_code (Errc::InvalidParam);
488 return -1;
489 // LCOV_EXCL_STOP
490 }
491
492 auto& sync = segment->_sync;
493 uint64_t head = sync._head.load (std::memory_order_relaxed);
494
495 if (JOIN_UNLIKELY ((head - sync._cachedTail) == capacity))
496 {
497 sync._cachedTail = sync._tail.load (std::memory_order_acquire);
498 if ((head - sync._cachedTail) == capacity)
499 {
500 lastError = make_error_code (Errc::TemporaryError);
501 return -1;
502 }
503 }
504
505 segment->_elements[head & mask].data = element;
506 sync._head.store (head + 1, std::memory_order_release);
507
508 return 0;
509 }
510
520 static ssize_t tryPush (Segment* segment, const Type* elements, size_t size, uint64_t capacity,
521 uint64_t mask) noexcept
522 {
523 if (JOIN_UNLIKELY (segment == nullptr || elements == nullptr || size == 0))
524 {
525 lastError = make_error_code (Errc::InvalidParam);
526 return -1;
527 }
528
529 auto& sync = segment->_sync;
530 uint64_t head = sync._head.load (std::memory_order_relaxed);
531 uint64_t avail = capacity - (head - sync._cachedTail);
532
533 if (JOIN_UNLIKELY (avail == 0))
534 {
535 sync._cachedTail = sync._tail.load (std::memory_order_acquire);
536 avail = capacity - (head - sync._cachedTail);
537 if (avail == 0)
538 {
539 lastError = make_error_code (Errc::TemporaryError);
540 return -1;
541 }
542 }
543
544 uint64_t toWrite = std::min (static_cast<uint64_t> (size), avail);
545 uint64_t offset = head & mask;
546 uint64_t first = std::min (toWrite, capacity - offset);
547
548 std::memcpy (&segment->_elements[offset], elements, first * sizeof (Type));
549
550 if (toWrite > first)
551 {
552 std::memcpy (&segment->_elements[0], elements + first, (toWrite - first) * sizeof (Type));
553 }
554
555 sync._head.store (head + toWrite, std::memory_order_release);
556
557 return static_cast<ssize_t> (toWrite);
558 }
559
568 static int tryPop (Segment* segment, Type& element, uint64_t /*capacity*/, uint64_t mask) noexcept
569 {
570 if (JOIN_UNLIKELY (segment == nullptr))
571 {
572 // LCOV_EXCL_START
573 lastError = make_error_code (Errc::InvalidParam);
574 return -1;
575 // LCOV_EXCL_STOP
576 }
577
578 auto& sync = segment->_sync;
579 uint64_t tail = sync._tail.load (std::memory_order_relaxed);
580
581 if (sync._cachedHead == tail)
582 {
583 sync._cachedHead = sync._head.load (std::memory_order_acquire);
584 if (sync._cachedHead == tail)
585 {
586 lastError = make_error_code (Errc::TemporaryError);
587 return -1;
588 }
589 }
590
591 element = segment->_elements[tail & mask].data;
592 sync._tail.store (tail + 1, std::memory_order_release);
593
594 return 0;
595 }
596
606 static ssize_t tryPop (Segment* segment, Type* elements, size_t size, uint64_t capacity, uint64_t mask) noexcept
607 {
608 if (JOIN_UNLIKELY (segment == nullptr || elements == nullptr || size == 0))
609 {
610 lastError = make_error_code (Errc::InvalidParam);
611 return -1;
612 }
613
614 auto& sync = segment->_sync;
615 uint64_t tail = sync._tail.load (std::memory_order_relaxed);
616 uint64_t pending = sync._cachedHead - tail;
617
618 if (pending == 0)
619 {
620 sync._cachedHead = sync._head.load (std::memory_order_acquire);
621 pending = sync._cachedHead - tail;
622 if (pending == 0)
623 {
624 lastError = make_error_code (Errc::TemporaryError);
625 return -1;
626 }
627 }
628
629 uint64_t toRead = std::min (static_cast<uint64_t> (size), pending);
630 uint64_t offset = tail & mask;
631 uint64_t first = std::min (toRead, capacity - offset);
632
633 std::memcpy (elements, &segment->_elements[offset], first * sizeof (Type));
634
635 if (toRead > first)
636 {
637 std::memcpy (elements + first, &segment->_elements[0], (toRead - first) * sizeof (Type));
638 }
639
640 sync._tail.store (tail + toRead, std::memory_order_release);
641
642 return static_cast<ssize_t> (toRead);
643 }
644 };
645
649 template <typename Type, typename Backend>
650 struct Mpsc
651 {
654
663 static int tryPush (Segment* segment, const Type& element, uint64_t /*capacity*/, uint64_t mask) noexcept
664 {
665 if (JOIN_UNLIKELY (segment == nullptr))
666 {
667 // LCOV_EXCL_START
668 lastError = make_error_code (Errc::InvalidParam);
669 return -1;
670 // LCOV_EXCL_STOP
671 }
672
673 auto& sync = segment->_sync;
674 uint64_t head = sync._head.load (std::memory_order_relaxed);
675 Backoff backoff;
676
677 for (;;)
678 {
679 auto* slot = &segment->_elements[head & mask];
680 uint64_t seq = slot->_seq.load (std::memory_order_acquire);
681
682 if (seq == head)
683 {
684 if (JOIN_LIKELY (sync._head.compare_exchange_weak (head, head + 1, std::memory_order_acquire,
685 std::memory_order_relaxed)))
686 {
687 slot->data = element;
688 slot->_seq.store (head + 1, std::memory_order_release);
689 return 0;
690 }
691 }
692 else if (JOIN_UNLIKELY (seq < head))
693 {
694 lastError = make_error_code (Errc::TemporaryError);
695 return -1;
696 }
697 else
698 {
699 backoff ();
700 head = sync._head.load (std::memory_order_relaxed);
701 }
702 }
703 }
704
714 static ssize_t tryPush (Segment* segment, const Type* elements, size_t size, uint64_t capacity,
715 uint64_t mask) noexcept
716 {
717 if (JOIN_UNLIKELY (segment == nullptr || elements == nullptr || size == 0))
718 {
719 lastError = make_error_code (Errc::InvalidParam);
720 return -1;
721 }
722
723 Backoff backoff;
724 auto& sync = segment->_sync;
725 uint64_t head = sync._head.load (std::memory_order_relaxed);
726
727 for (;;)
728 {
729 uint64_t tail = sync._tail.load (std::memory_order_acquire);
730 uint64_t toWrite = std::min (static_cast<uint64_t> (size), capacity - (head - tail));
731
732 if (JOIN_UNLIKELY (toWrite == 0))
733 {
734 lastError = make_error_code (Errc::TemporaryError);
735 return -1;
736 }
737
738 if (JOIN_LIKELY (sync._head.compare_exchange_weak (head, head + toWrite, std::memory_order_acquire,
739 std::memory_order_relaxed)))
740 {
741 for (uint64_t i = 0; i < toWrite; ++i)
742 {
743 auto* slot = &segment->_elements[(head + i) & mask];
744 slot->data = elements[i];
745 slot->_seq.store (head + i + 1, std::memory_order_release);
746 }
747
748 return static_cast<ssize_t> (toWrite);
749 }
750
751 backoff (); // LCOV_EXCL_LINE
752 }
753 }
754
763 static int tryPop (Segment* segment, Type& element, uint64_t capacity, uint64_t mask) noexcept
764 {
765 if (JOIN_UNLIKELY (segment == nullptr))
766 {
767 // LCOV_EXCL_START
768 lastError = make_error_code (Errc::InvalidParam);
769 return -1;
770 // LCOV_EXCL_STOP
771 }
772
773 auto& sync = segment->_sync;
774 uint64_t tail = sync._tail.load (std::memory_order_relaxed);
775 auto* slot = &segment->_elements[tail & mask];
776 uint64_t seq = slot->_seq.load (std::memory_order_acquire);
777
778 if (JOIN_UNLIKELY (seq != tail + 1))
779 {
780 lastError = make_error_code (Errc::TemporaryError);
781 return -1;
782 }
783
784 element = slot->data;
785 slot->_seq.store (tail + capacity, std::memory_order_release);
786 sync._tail.store (tail + 1, std::memory_order_release);
787
788 return 0;
789 }
790
800 static ssize_t tryPop (Segment* segment, Type* elements, size_t size, uint64_t capacity, uint64_t mask) noexcept
801 {
802 if (JOIN_UNLIKELY (segment == nullptr || elements == nullptr || size == 0))
803 {
804 lastError = make_error_code (Errc::InvalidParam);
805 return -1;
806 }
807
808 auto& sync = segment->_sync;
809 uint64_t tail = sync._tail.load (std::memory_order_relaxed);
810 uint64_t head = sync._head.load (std::memory_order_acquire);
811 uint64_t toRead = std::min (static_cast<uint64_t> (size), head - tail);
812 uint64_t popped = 0;
813
814 for (uint64_t i = 0; i < toRead; ++i)
815 {
816 auto* slot = &segment->_elements[(tail + i) & mask];
817
818 if (JOIN_UNLIKELY (slot->_seq.load (std::memory_order_acquire) != tail + i + 1))
819 {
820 break;
821 }
822
823 elements[i] = slot->data;
824 slot->_seq.store (tail + i + capacity, std::memory_order_release);
825 ++popped;
826 }
827
828 if (JOIN_LIKELY (popped > 0))
829 {
830 sync._tail.store (tail + popped, std::memory_order_release);
831 return static_cast<ssize_t> (popped);
832 }
833
834 lastError = make_error_code (Errc::TemporaryError);
835 return -1;
836 }
837 };
838
842 template <typename Type, typename Backend>
843 struct Mpmc
844 {
847
856 static int tryPush (Segment* segment, const Type& element, uint64_t capacity, uint64_t mask) noexcept
857 {
858 return Mpsc<Type, Backend>::tryPush (segment, element, capacity, mask);
859 }
860
870 static ssize_t tryPush (Segment* segment, const Type* elements, size_t size, uint64_t capacity,
871 uint64_t mask) noexcept
872 {
873 if (JOIN_UNLIKELY (segment == nullptr || elements == nullptr || size == 0))
874 {
875 lastError = make_error_code (Errc::InvalidParam);
876 return -1;
877 }
878
879 Backoff backoff;
880 auto& sync = segment->_sync;
881 uint64_t head = sync._head.load (std::memory_order_relaxed);
882
883 for (;;)
884 {
885 uint64_t tail = sync._tail.load (std::memory_order_acquire);
886 uint64_t toWrite = std::min (static_cast<uint64_t> (size), capacity - (head - tail));
887
888 if (JOIN_UNLIKELY (toWrite == 0))
889 {
890 lastError = make_error_code (Errc::TemporaryError);
891 return -1;
892 }
893
894 if (JOIN_LIKELY (sync._head.compare_exchange_weak (head, head + toWrite, std::memory_order_acquire,
895 std::memory_order_relaxed)))
896 {
897 for (uint64_t i = 0; i < toWrite; ++i)
898 {
899 auto* slot = &segment->_elements[(head + i) & mask];
900 Backoff slotBackoff;
901 while (slot->_seq.load (std::memory_order_acquire) != head + i)
902 {
903 slotBackoff (); // LCOV_EXCL_LINE
904 }
905 slot->data = elements[i];
906 slot->_seq.store (head + i + 1, std::memory_order_release);
907 }
908
909 return static_cast<ssize_t> (toWrite);
910 }
911
912 backoff (); // LCOV_EXCL_LINE
913 }
914 }
915
924 static int tryPop (Segment* segment, Type& element, uint64_t capacity, uint64_t mask) noexcept
925 {
926 if (JOIN_UNLIKELY (segment == nullptr))
927 {
928 // LCOV_EXCL_START
929 lastError = make_error_code (Errc::InvalidParam);
930 return -1;
931 // LCOV_EXCL_STOP
932 }
933
934 auto& sync = segment->_sync;
935 uint64_t tail = sync._tail.load (std::memory_order_relaxed);
936 Backoff backoff;
937
938 for (;;)
939 {
940 auto* slot = &segment->_elements[tail & mask];
941 uint64_t seq = slot->_seq.load (std::memory_order_acquire);
942
943 if (seq == (tail + 1))
944 {
945 if (JOIN_LIKELY (sync._tail.compare_exchange_weak (tail, tail + 1, std::memory_order_acquire,
946 std::memory_order_relaxed)))
947 {
948 element = slot->data;
949 slot->_seq.store (tail + capacity, std::memory_order_release);
950 return 0;
951 }
952 }
953 else if (JOIN_UNLIKELY (seq < (tail + 1)))
954 {
955 lastError = make_error_code (Errc::TemporaryError);
956 return -1;
957 }
958 else
959 {
960 backoff ();
961 tail = sync._tail.load (std::memory_order_relaxed);
962 }
963 }
964 }
965
975 static ssize_t tryPop (Segment* segment, Type* elements, size_t size, uint64_t capacity, uint64_t mask) noexcept
976 {
977 if (JOIN_UNLIKELY (segment == nullptr || elements == nullptr || size == 0))
978 {
979 lastError = make_error_code (Errc::InvalidParam);
980 return -1;
981 }
982
983 Backoff backoff;
984 auto& sync = segment->_sync;
985 uint64_t tail = sync._tail.load (std::memory_order_relaxed);
986
987 for (;;)
988 {
989 uint64_t head = sync._head.load (std::memory_order_acquire);
990 uint64_t toRead = std::min (static_cast<uint64_t> (size), head - tail);
991
992 if (JOIN_UNLIKELY (toRead == 0))
993 {
994 lastError = make_error_code (Errc::TemporaryError);
995 return -1;
996 }
997
998 uint64_t ready = 0;
999 for (; ready < toRead; ++ready)
1000 {
1001 auto* slot = &segment->_elements[(tail + ready) & mask];
1002 if (JOIN_UNLIKELY (slot->_seq.load (std::memory_order_acquire) != tail + ready + 1))
1003 {
1004 break;
1005 }
1006 elements[ready] = slot->data;
1007 }
1008
1009 if (JOIN_UNLIKELY (ready == 0))
1010 {
1011 lastError = make_error_code (Errc::TemporaryError);
1012 return -1;
1013 }
1014
1015 if (JOIN_LIKELY (sync._tail.compare_exchange_weak (tail, tail + ready, std::memory_order_acquire,
1016 std::memory_order_relaxed)))
1017 {
1018 for (uint64_t i = 0; i < ready; ++i)
1019 {
1020 segment->_elements[(tail + i) & mask]._seq.store (tail + i + capacity,
1021 std::memory_order_release);
1022 }
1023
1024 return static_cast<ssize_t> (ready);
1025 }
1026
1027 backoff (); // LCOV_EXCL_LINE
1028 }
1029 }
1030 };
1031
1035 template <typename Backend, template <typename, typename> class SyncPolicy>
1037 {
1039 template <typename Type>
1041 };
1042}
1043
1044#endif
adaptive backoff for busy-wait loops, requires Rdtsc::calibrate ().
Definition backoff.hpp:47
queue base class.
Definition queue.hpp:128
int push(const Type *elements, size_t size) noexcept
push multiple elements into the ring buffer.
Definition queue.hpp:255
BasicQueue(uint64_t capacity, Args &&... args)
create instance.
Definition queue.hpp:143
std::conditional_t< needs_seq_v< SyncPolicy >, QueueSlotFull< Type >, QueueSlotLight< Type > > Slot
Definition queue.hpp:134
int push(const Type &element) noexcept
push element into the ring buffer.
Definition queue.hpp:232
int mlock() const noexcept
lock memory in RAM.
Definition queue.hpp:426
ssize_t tryPop(Type *elements, size_t size) noexcept
try to pop multiple elements from the ring buffer.
Definition queue.hpp:297
int tryPop(Type &element) noexcept
try to pop element from the ring buffer.
Definition queue.hpp:286
~BasicQueue() noexcept=default
destroy queue instance.
int pop(Type *elements, size_t size) noexcept
pop multiple elements from the ring buffer.
Definition queue.hpp:330
BasicQueue(BasicQueue &&other)=delete
move constructor.
int pop(Type &element) noexcept
pop element from the ring buffer.
Definition queue.hpp:307
bool full() const noexcept
check if the ring buffer is full.
Definition queue.hpp:388
ssize_t tryPush(const Type *elements, size_t size) noexcept
try to push multiple elements into the ring buffer.
Definition queue.hpp:222
bool empty() const noexcept
check if the ring buffer is empty.
Definition queue.hpp:401
uint64_t pending() const noexcept
get the number of pending elements for reading.
Definition queue.hpp:360
uint64_t available() const noexcept
get the number of available slots for writing.
Definition queue.hpp:375
Type ValueType
Definition queue.hpp:133
BasicQueue(const BasicQueue &other)=delete
copy constructor.
Definition acceptor.hpp:32
constexpr bool needs_seq_v
primary trait: all sync policies need sequence numbers by default.
Definition queue.hpp:115
constexpr uint64_t nextPow2(uint64_t value) noexcept
round up to the next power of two.
Definition utils.hpp:475
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:844
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:870
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:975
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:856
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:924
multiple producer single consumer ring buffer.
Definition queue.hpp:651
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:800
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:663
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:763
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:714
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:470
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:606
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:520
static int tryPop(Segment *segment, Type &element, uint64_t, uint64_t mask) noexcept
try to pop element from the ring buffer.
Definition queue.hpp:568
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:482
queue forward declarations.
Definition queue.hpp:1037
#define JOIN_LIKELY(x)
Definition utils.hpp:50
#define JOIN_UNLIKELY(x)
Definition utils.hpp:51