join 1.0
lightweight network framework library
Loading...
Searching...
No Matches
wheel.hpp
Go to the documentation of this file.
1
25#ifndef JOIN_CORE_WHEEL_HPP
26#define JOIN_CORE_WHEEL_HPP
27
28// libjoin.
30#include <join/function.hpp>
31#include <join/backoff.hpp>
32#include <join/memory.hpp>
33#include <join/thread.hpp>
34#include <join/queue.hpp>
35#include <join/error.hpp>
36#include <join/clock.hpp>
37#include <join/utils.hpp>
38
39// C++.
40#include <functional>
41#include <utility>
42#include <chrono>
43#include <atomic>
44#include <limits>
45#include <new>
46
47// C.
48#include <pthread.h>
49#include <cstdint>
50
51namespace join
52{
56 template <class ClockPolicy, class RunPolicy, size_t Capacity, uint64_t TickNs>
58 {
59 static_assert (Capacity > 0, "capacity must be greater than zero");
60 static_assert (TickNs > 0, "tick duration must be greater than zero");
61
63 friend RunPolicy;
64
65 public:
72 template <typename... Args>
73 explicit BasicWheel (Args&&... args)
74 : _commands (_queueSize)
75 , _generationsMem (Capacity * sizeof (std::atomic_uint32_t))
76 , _generations (static_cast<std::atomic_uint32_t*> (_generationsMem.get ()))
77 , _runner (std::forward<Args> (args)...)
78 {
79 _allocator.reserveAll ();
80
81 for (uint32_t index = 0; index < Capacity; ++index)
82 {
83 new (&_generations[index]) std::atomic_uint32_t (0);
84 new (_allocator.getPtr (index)) Node ();
85 }
86
87 _allocator.releaseAll ();
88
89 _tick = toTick (ClockPolicy::now ());
90 _runner.start (*this, resolution ());
91 }
92
97 BasicWheel (const BasicWheel& other) = delete;
98
104 BasicWheel& operator= (const BasicWheel& other) = delete;
105
110 BasicWheel (BasicWheel&& other) = delete;
111
117 BasicWheel& operator= (BasicWheel&& other) = delete;
118
122 ~BasicWheel () noexcept
123 {
124 _runner.stop ();
125 readCommands ();
126
127 for (uint32_t index = 0; index < Capacity; ++index)
128 {
129 static_cast<Node*> (_allocator.getPtr (index))->~Node ();
130 _generations[index].~atomic ();
131 }
132 }
133
141 template <class Rep, class Period, typename Func, typename... Args>
142 ssize_t setOneShot (std::chrono::duration<Rep, Period> duration, Func&& callback, Args&&... args) noexcept
143 {
144 return createTimer (ticksFor (duration), 0, std::forward<Func> (callback), std::forward<Args> (args)...);
145 }
146
154 template <class Rep, class Period, typename Func, typename... Args>
155 ssize_t setInterval (std::chrono::duration<Rep, Period> duration, Func&& callback, Args&&... args) noexcept
156 {
157 const uint64_t ticks = ticksFor (duration);
158 return createTimer (ticks, ticks, std::forward<Func> (callback), std::forward<Args> (args)...);
159 }
160
167 int cancel (ssize_t id, bool sync = false) noexcept
168 {
169 Node* node = resolve (id);
170
171 if (JOIN_UNLIKELY (node == nullptr))
172 {
173 lastError = make_error_code (Errc::NotFound);
174 return -1;
175 }
176
177 if (JOIN_LIKELY (_runner.isRunnerThread ()))
178 {
179 disarmTimer (node);
180 return 0;
181 }
182
183 if (JOIN_UNLIKELY (writeCommand ({CommandType::Disarm, placeOf (id), occupantOf (id)}) == -1))
184 {
185 return -1; // LCOV_EXCL_LINE
186 }
187
188 _runner.flush (*this);
189
190 if (JOIN_UNLIKELY (sync))
191 {
192 Backoff backoff;
193 while (resolve (id) != nullptr)
194 {
195 if (JOIN_UNLIKELY (_runner.flush (*this) == -1))
196 {
198 return -1;
199 }
200
201 backoff ();
202 }
203 }
204
205 return 0;
206 }
207
213 bool active (ssize_t id) const noexcept
214 {
215 return resolve (id) != nullptr;
216 }
217
223 std::chrono::nanoseconds remaining (ssize_t id) const noexcept
224 {
225 const Node* node = resolve (id);
226
227 if (JOIN_UNLIKELY (node == nullptr))
228 {
229 return std::chrono::nanoseconds::zero ();
230 }
231
232 const uint64_t tick = toTick (ClockPolicy::now ());
233 const uint64_t deadline = node->deadline.load (std::memory_order_acquire);
234
235 if (JOIN_UNLIKELY (resolve (id) == nullptr))
236 {
237 return std::chrono::nanoseconds::zero (); // LCOV_EXCL_LINE
238 }
239
240 return std::chrono::nanoseconds ((deadline > tick + 1) ? ((deadline - tick - 1) * TickNs) : 0);
241 }
242
248 std::chrono::nanoseconds interval (ssize_t id) const noexcept
249 {
250 const Node* node = resolve (id);
251
252 if (JOIN_UNLIKELY (node == nullptr))
253 {
254 return std::chrono::nanoseconds::zero ();
255 }
256
257 const uint64_t ticks = node->interval.load (std::memory_order_acquire);
258
259 if (JOIN_UNLIKELY (resolve (id) == nullptr))
260 {
261 return std::chrono::nanoseconds::zero (); // LCOV_EXCL_LINE
262 }
263
264 return std::chrono::nanoseconds (ticks * TickNs);
265 }
266
272 bool oneShot (ssize_t id) const noexcept
273 {
274 const Node* node = resolve (id);
275
276 if (JOIN_UNLIKELY (node == nullptr))
277 {
278 return false;
279 }
280
281 const uint64_t ticks = node->interval.load (std::memory_order_acquire);
282
283 return (resolve (id) != nullptr) && (ticks == 0);
284 }
285
286#ifdef JOIN_HAS_NUMA
292 int mbind (int numa) const noexcept
293 {
294 if ((_allocator.mbind (numa) == -1) || (_commands.mbind (numa) == -1))
295 {
296 return -1;
297 }
298
299 return _generationsMem.mbind (numa);
300 }
301#endif
302
307 int mlock () const noexcept
308 {
309 if ((_allocator.mlock () == -1) || (_commands.mlock () == -1))
310 {
311 return -1; // LCOV_EXCL_LINE
312 }
313
314 return _generationsMem.mlock ();
315 }
316
321 const RunPolicy& runner () const noexcept
322 {
323 return _runner;
324 }
325
330 static constexpr std::chrono::nanoseconds resolution () noexcept
331 {
332 return std::chrono::nanoseconds (TickNs);
333 }
334
339 static constexpr size_t capacity () noexcept
340 {
341 return Capacity;
342 }
343
344 private:
348 enum class CommandType : uint8_t
349 {
350 Arm,
351 Disarm,
352 };
353
357 enum class State : uint8_t
358 {
359 Idle,
360 Linked,
361 Firing,
362 Cancelled,
363 };
364
368 struct Node
369 {
371 Node* prev = nullptr;
372
374 Node* next = nullptr;
375
377 std::atomic_uint64_t deadline{0};
378
380 std::atomic_uint64_t interval{0};
381
383 uint16_t slot = 0;
384
386 uint8_t level = 0;
387
389 State state = State::Idle;
390
392 Function<void (), 48> callback;
393 };
394
395 static_assert (sizeof (Node) == 128, "node must fill exactly two cache lines");
396
400 struct Command
401 {
402 CommandType type;
403 uint32_t place;
404 uint32_t occupant;
405 };
406
411 uint64_t advance () noexcept
412 {
413 readCommands ();
414
415 if (_armed == 0)
416 {
417 return 0;
418 }
419
420 const uint64_t target = toTick (ClockPolicy::now ());
421 const uint64_t start = _tick;
422
423 if (JOIN_UNLIKELY (target < start))
424 {
425 return 0; // LCOV_EXCL_LINE
426 }
427
428 if (target >= _due)
429 {
430 uint64_t tick = start;
431 uint64_t next;
432
433 while ((next = nextTick (tick)) <= target)
434 {
435 tick = next;
436 _tick = tick;
437
438 if (JOIN_UNLIKELY ((tick & _slotMask) == 0))
439 {
440 cascade (tick);
441 }
442
443 fire (tick & _slotMask);
444 }
445
446 _due = next;
447 }
448
449 _tick = target;
450
451 return target - start;
452 }
453
458 std::chrono::nanoseconds next () const noexcept
459 {
460 if (_armed == 0)
461 {
462 return std::chrono::nanoseconds::max ();
463 }
464
465 const uint64_t now = static_cast<uint64_t> (ClockPolicy::now ().time_since_epoch ().count ());
466 const uint64_t tick = now / TickNs;
467 const uint64_t ticks = (_due > tick) ? (_due - tick) : 0;
468 const uint64_t limit = static_cast<uint64_t> (std::numeric_limits<int64_t>::max () / 2) / TickNs;
469 const uint64_t elapsed = (ticks != 0) ? (now % TickNs) : 0;
470
471 return std::chrono::nanoseconds (((ticks < limit) ? ticks : limit) * TickNs - elapsed);
472 }
473
482 template <typename Func, typename... Args>
483 ssize_t createTimer (uint64_t ticks, uint64_t interval, Func&& callback, Args&&... args) noexcept
484 {
485 void* ptr = _allocator.tryAllocate (sizeof (Node));
486
487 if (JOIN_UNLIKELY (ptr == nullptr))
488 {
490 return -1;
491 }
492
493 Node* node = static_cast<Node*> (ptr);
494 node->callback = std::bind (std::forward<Func> (callback), std::forward<Args> (args)...);
495 node->interval.store (interval, std::memory_order_release);
496 node->state = State::Idle;
497 const uint64_t now = toTick (ClockPolicy::now ());
498 node->deadline.store (now + ticks + 1, std::memory_order_release);
499
500 const uint32_t place = _allocator.getIndex (node);
501 const uint32_t occupant = _generations[place].load (std::memory_order_relaxed) & _occupantMask;
502
503 if (JOIN_LIKELY (_runner.isRunnerThread ()))
504 {
505 if (_armed++ == 0)
506 {
507 _tick = now;
508 _due = std::numeric_limits<uint64_t>::max ();
509 }
510
511 armTimer (node);
512 return makeId (occupant, place);
513 }
514
515 if (JOIN_UNLIKELY (writeCommand ({CommandType::Arm, place, occupant}) == -1))
516 {
517 // LCOV_EXCL_START
518 releaseTimer (node);
519 return -1;
520 // LCOV_EXCL_STOP
521 }
522
523 _runner.flush (*this);
524
525 return makeId (occupant, place);
526 }
527
533 void armTimer (Node* node, bool sameTick = false) noexcept
534 {
535 const uint64_t tick = _tick;
536 const uint64_t deadline = node->deadline.load (std::memory_order_relaxed);
537 const uint64_t horizon = tick + ((uint64_t (1) << (_levels * _slotBits)) - 1);
538 const uint64_t due = (deadline > tick) ? deadline : (sameTick ? tick : (tick + 1));
539 const uint64_t expires = (due < horizon) ? due : horizon;
540 const uint64_t delta = (expires > tick) ? (expires - tick) : 1;
541
542 size_t level = static_cast<size_t> (63 - __builtin_clzll (delta)) / _slotBits;
543 level = (level < _levels) ? level : (_levels - 1);
544
545 const size_t slot = (expires >> (level * _slotBits)) & _slotMask;
546
547 node->level = static_cast<uint8_t> (level);
548 node->slot = static_cast<uint16_t> (slot);
549 node->state = State::Linked;
550 node->prev = nullptr;
551 node->next = _wheel[level][slot];
552
553 if (node->next != nullptr)
554 {
555 node->next->prev = node;
556 }
557
558 _wheel[level][slot] = node;
559 _occupied[level][slot >> 6] |= uint64_t (1) << (slot & 63);
560
561 const uint64_t event = (expires >> (level * _slotBits)) << (level * _slotBits);
562
563 if (event < _due)
564 {
565 _due = event;
566 }
567 }
568
573 void disarmTimer (Node* node) noexcept
574 {
575 if (JOIN_UNLIKELY (node->state == State::Firing))
576 {
577 node->state = State::Cancelled;
578 return;
579 }
580
581 if (JOIN_UNLIKELY (node->state == State::Cancelled))
582 {
583 return;
584 }
585
586 if (JOIN_LIKELY (node->state == State::Linked))
587 {
588 unlink (node);
589 --_armed;
590 }
591
592 releaseTimer (node);
593 }
594
599 void releaseTimer (Node* node) noexcept
600 {
601 const uint32_t place = _allocator.getIndex (node);
602
603 node->callback.reset ();
604 _generations[place].fetch_add (1, std::memory_order_release);
605 _allocator.deallocate (node);
606 }
607
613 uint64_t nextTick (uint64_t tick) const noexcept
614 {
615 uint64_t next = std::numeric_limits<uint64_t>::max ();
616
617 for (size_t level = 0; level < _levels; ++level)
618 {
619 const size_t shift = level * _slotBits;
620 const uint64_t base = (tick >> shift) + 1;
621 const size_t start = base & _slotMask;
622 const size_t word = start >> 6;
623 uint64_t bits = _occupied[level][word] & (~uint64_t (0) << (start & 63));
624
625 for (size_t i = 0; i <= _slots / 64; ++i)
626 {
627 if (bits != 0)
628 {
629 const size_t slot = ((word + i) % (_slots / 64)) * 64 + __builtin_ctzll (bits);
630 const uint64_t candidate = (base + ((slot - start) & _slotMask)) << shift;
631 next = (candidate < next) ? candidate : next;
632 break;
633 }
634
635 bits = _occupied[level][(word + i + 1) % (_slots / 64)];
636 }
637
638 if (next < (((tick >> (shift + _slotBits)) + 1) << (shift + _slotBits)))
639 {
640 break;
641 }
642 }
643
644 return next;
645 }
646
651 void cascade (uint64_t tick) noexcept
652 {
653 for (size_t level = 1; level < _levels; ++level)
654 {
655 const size_t slot = (tick >> (level * _slotBits)) & _slotMask;
656 Node* node;
657
658 while ((node = _wheel[level][slot]) != nullptr)
659 {
660 unlink (node);
661 armTimer (node, true);
662 }
663
664 if (slot != 0)
665 {
666 break;
667 }
668 }
669 }
670
675 void fire (size_t slot) noexcept
676 {
677 Node* node = _wheel[0][slot];
678
679 while (node != nullptr)
680 {
681 unlink (node);
682
683 node->state = State::Firing;
684 node->callback ();
685
686 const bool cancelled = (node->state == State::Cancelled);
687 node->state = State::Idle;
688
689 if (JOIN_LIKELY (!cancelled && (node->interval.load (std::memory_order_relaxed) != 0)))
690 {
691 node->deadline.fetch_add (node->interval.load (std::memory_order_relaxed),
692 std::memory_order_release);
693 armTimer (node);
694 }
695 else
696 {
697 releaseTimer (node);
698 --_armed;
699 }
700
701 node = _wheel[0][slot];
702 }
703 }
704
709 void unlink (Node* node) noexcept
710 {
711 if (node->prev != nullptr)
712 {
713 node->prev->next = node->next;
714 }
715 else
716 {
717 _wheel[node->level][node->slot] = node->next;
718
719 if (node->next == nullptr)
720 {
721 _occupied[node->level][node->slot >> 6] &= ~(uint64_t (1) << (node->slot & 63));
722 }
723 }
724
725 if (node->next != nullptr)
726 {
727 node->next->prev = node->prev;
728 }
729
730 node->prev = nullptr;
731 node->next = nullptr;
732 node->state = State::Idle;
733 }
734
740 int writeCommand (const Command& cmd) noexcept
741 {
742 if (JOIN_UNLIKELY (_commands.tryPush (cmd) == -1))
743 {
744 // LCOV_EXCL_START
746 return -1;
747 // LCOV_EXCL_STOP
748 }
749
750 return 0;
751 }
752
756 void readCommands () noexcept
757 {
758 Command cmd;
759
760 while (_commands.tryPop (cmd) == 0)
761 {
762 processCommand (cmd);
763 }
764 }
765
770 void processCommand (const Command& cmd) noexcept
771 {
772 if (JOIN_LIKELY ((_generations[cmd.place].load (std::memory_order_acquire) & _occupantMask) ==
773 cmd.occupant))
774 {
775 Node* node = static_cast<Node*> (_allocator.getPtr (cmd.place));
776
777 if (cmd.type == CommandType::Arm)
778 {
779 if (_armed++ == 0)
780 {
781 _tick = toTick (ClockPolicy::now ());
782 _due = std::numeric_limits<uint64_t>::max ();
783 }
784
785 armTimer (node);
786 }
787 else
788 {
789 disarmTimer (node);
790 }
791 }
792 }
793
799 Node* resolve (ssize_t id) const noexcept
800 {
801 if (JOIN_UNLIKELY (id <= 0))
802 {
803 return nullptr;
804 }
805
806 const uint32_t place = placeOf (id);
807
808 if (JOIN_UNLIKELY (place >= Capacity))
809 {
810 return nullptr;
811 }
812
813 if (JOIN_UNLIKELY ((_generations[place].load (std::memory_order_acquire) & _occupantMask) !=
814 occupantOf (id)))
815 {
816 return nullptr;
817 }
818
819 return static_cast<Node*> (_allocator.getPtr (place));
820 }
821
828 static constexpr ssize_t makeId (uint32_t occupant, uint32_t place) noexcept
829 {
830 return (static_cast<ssize_t> (occupant & _occupantMask) << 32) | (static_cast<ssize_t> (place) + 1);
831 }
832
838 static constexpr uint32_t placeOf (ssize_t id) noexcept
839 {
840 return static_cast<uint32_t> ((id & 0xFFFFFFFF) - 1);
841 }
842
848 static constexpr uint32_t occupantOf (ssize_t id) noexcept
849 {
850 return static_cast<uint32_t> (id >> 32) & _occupantMask;
851 }
852
858 static uint64_t toTick (typename ClockPolicy::TimePoint timePoint) noexcept
859 {
860 return static_cast<uint64_t> (timePoint.time_since_epoch ().count ()) / TickNs;
861 }
862
868 template <class Rep, class Period>
869 static uint64_t ticksFor (std::chrono::duration<Rep, Period> duration) noexcept
870 {
871 auto ns = std::chrono::duration_cast<std::chrono::nanoseconds> (duration);
872
873 if (ns.count () < 1)
874 {
875 return 1;
876 }
877
878 return (static_cast<uint64_t> (ns.count ()) + TickNs - 1) / TickNs;
879 }
880
882 static constexpr size_t _slots = 256;
883
885 static constexpr size_t _slotBits = 8;
886
888 static constexpr size_t _slotMask = _slots - 1;
889
891 static constexpr size_t _levels = 6;
892
894 static constexpr uint32_t _occupantMask = 0x7FFFFFFF;
895
897 static constexpr size_t _queueSize = 2 * Capacity;
898
900 LocalMem::SlabAllocator<Capacity, sizeof (Node)> _allocator;
901
904
906 LocalMem _generationsMem;
907
909 std::atomic_uint32_t* const _generations;
910
912 size_t _armed = 0;
913
915 uint64_t _occupied[_levels][_slots / 64] = {};
916
918 uint64_t _due = std::numeric_limits<uint64_t>::max ();
919
921 alignas (64) Node* _wheel[_levels][_slots] = {};
922
924 alignas (64) uint64_t _tick = 0;
925
927 ClockPolicy _clock;
928
930 RunPolicy _runner;
931 };
932
936 template <class ClockPolicy>
938 {
939 public:
945 explicit SpinPolicy (int core = -1, int prio = 0) noexcept
946 : _core (core)
947 , _priority (prio)
948 {
949 }
950
955 SpinPolicy (const SpinPolicy& other) = delete;
956
962 SpinPolicy& operator= (const SpinPolicy& other) = delete;
963
968 SpinPolicy (SpinPolicy&& other) = delete;
969
975 SpinPolicy& operator= (SpinPolicy&& other) = delete;
976
980 ~SpinPolicy () noexcept
981 {
982 stop ();
983 }
984
992 template <class Wheel>
993 int start (Wheel& wheel, std::chrono::nanoseconds)
994 {
995 _running.store (true, std::memory_order_release);
996 _dispatcher = Thread (_core, _priority, &SpinPolicy::loop<Wheel>, this, &wheel);
997
998 Backoff backoff;
999 while (_threadId.load (std::memory_order_acquire) == Thread::invalidId)
1000 {
1001 backoff ();
1002 }
1003
1004 return 0;
1005 }
1006
1010 void stop () noexcept
1011 {
1012 _running.store (false, std::memory_order_release);
1013
1015 {
1016 return; // LCOV_EXCL_LINE
1017 }
1018
1019 Backoff backoff;
1020 while (_threadId.load (std::memory_order_acquire) != Thread::invalidId)
1021 {
1022 backoff ();
1023 }
1024 }
1025
1030 bool isRunnerThread () const noexcept
1031 {
1032 return _threadId.load (std::memory_order_acquire) == Thread::currentId ();
1033 }
1034
1040 template <class Wheel>
1041 int flush (Wheel&) noexcept
1042 {
1043 return 0;
1044 }
1045
1050 int affinity () const noexcept
1051 {
1052 return _dispatcher.affinity ();
1053 }
1054
1059 int priority () const noexcept
1060 {
1061 return _dispatcher.priority ();
1062 }
1063
1068 pthread_t handle () const noexcept
1069 {
1070 return _dispatcher.handle ();
1071 }
1072
1073 private:
1078 template <class Wheel>
1079 void loop (Wheel* wheel) noexcept
1080 {
1081 _threadId.store (Thread::currentId (), std::memory_order_release);
1082
1083 while (JOIN_LIKELY (_running.load (std::memory_order_relaxed)))
1084 {
1085 wheel->advance ();
1086
1087 if (wheel->_armed == 0)
1088 {
1089#if defined(__x86_64__) || defined(__i386__)
1090 _mm_pause ();
1091#elif defined(__aarch64__) || defined(__arm__)
1092 __asm__ __volatile__ ("isb" ::: "memory");
1093#endif
1094 }
1095 }
1096
1097 _threadId.store (Thread::invalidId, std::memory_order_release);
1098 }
1099
1101 alignas (64) std::atomic<pid_t> _threadId{Thread::invalidId};
1102
1104 alignas (64) std::atomic_bool _running{false};
1105
1107 int _core = -1;
1108
1110 int _priority = 0;
1111
1113 Thread _dispatcher;
1114 };
1115}
1116
1117#endif
adaptive backoff for busy-wait loops, requires Rdtsc::calibrate ().
Definition backoff.hpp:47
timing wheel.
Definition wheel.hpp:58
static constexpr std::chrono::nanoseconds resolution() noexcept
get the tick duration.
Definition wheel.hpp:330
std::chrono::nanoseconds interval(ssize_t id) const noexcept
get the interval.
Definition wheel.hpp:248
int mlock() const noexcept
lock wheel memory in RAM.
Definition wheel.hpp:307
static constexpr size_t capacity() noexcept
get the capacity.
Definition wheel.hpp:339
ssize_t setInterval(std::chrono::duration< Rep, Period > duration, Func &&callback, Args &&... args) noexcept
arm a periodic timer.
Definition wheel.hpp:155
int cancel(ssize_t id, bool sync=false) noexcept
cancel a timer.
Definition wheel.hpp:167
~BasicWheel() noexcept
destroy instance.
Definition wheel.hpp:122
BasicWheel(const BasicWheel &other)=delete
copy constructor.
BasicWheel(BasicWheel &&other)=delete
move constructor.
std::chrono::nanoseconds remaining(ssize_t id) const noexcept
get the time left.
Definition wheel.hpp:223
BasicWheel & operator=(const BasicWheel &other)=delete
copy assignment operator.
const RunPolicy & runner() const noexcept
get the run policy.
Definition wheel.hpp:321
bool oneShot(ssize_t id) const noexcept
check if a timer is one-shot.
Definition wheel.hpp:272
bool active(ssize_t id) const noexcept
check if a timer is armed.
Definition wheel.hpp:213
ssize_t setOneShot(std::chrono::duration< Rep, Period > duration, Func &&callback, Args &&... args) noexcept
arm a one-shot timer.
Definition wheel.hpp:142
BasicWheel(Args &&... args)
create instance.
Definition wheel.hpp:73
int mlock() const noexcept
lock memory in RAM.
Definition memory.hpp:470
BasicSlabAllocator< LocalMem, Count, Sizes... > SlabAllocator
Definition memory.hpp:327
wheel run policy spinning on a dedicated thread.
Definition wheel.hpp:938
void stop() noexcept
stop spinning.
Definition wheel.hpp:1010
int start(Wheel &wheel, std::chrono::nanoseconds)
start spinning.
Definition wheel.hpp:993
bool isRunnerThread() const noexcept
check if called from the spinning thread.
Definition wheel.hpp:1030
SpinPolicy(int core=-1, int prio=0) noexcept
create instance.
Definition wheel.hpp:945
SpinPolicy(SpinPolicy &&other)=delete
move constructor.
pthread_t handle() const noexcept
get the native handle of the spinning thread.
Definition wheel.hpp:1068
int affinity() const noexcept
get spinning thread affinity.
Definition wheel.hpp:1050
~SpinPolicy() noexcept
destroy instance.
Definition wheel.hpp:980
SpinPolicy & operator=(const SpinPolicy &other)=delete
copy assignment operator.
int flush(Wheel &) noexcept
no-op.
Definition wheel.hpp:1041
int priority() const noexcept
get spinning thread scheduling priority.
Definition wheel.hpp:1059
SpinPolicy(const SpinPolicy &other)=delete
copy constructor.
thread class.
Definition thread.hpp:180
static constexpr pid_t invalidId
invalid thread id.
Definition thread.hpp:183
static pid_t currentId() noexcept
get calling thread id.
Definition thread.hpp:346
int affinity(int core)
set thread affinity.
Definition thread.cpp:148
int priority(int prio)
set thread priority.
Definition thread.cpp:211
pthread_t handle() const noexcept
get the handle of the thread of execution.
Definition thread.cpp:350
Monotonic::Wheel< 64 > Wheel
Definition monotonic_wheel_test.cpp:41
Definition acceptor.hpp:32
std::string base(const std::string &filepath)
get base path of the specified file.
Definition filesystem.hpp:41
std::error_code make_error_code(join::Errc code) noexcept
Create an std::error_code object.
Definition error.cpp:195
Definition error.hpp:144
BasicQueue< Type, Backend, SyncPolicy< Type, Backend > > Queue
queue type alias combining backend and policy.
Definition queue.hpp:1040
#define JOIN_LIKELY(x)
Definition utils.hpp:50
#define JOIN_UNLIKELY(x)
Definition utils.hpp:51