join 1.0
lightweight network framework library
Loading...
Searching...
No Matches
proactor_uring_impl.hpp
Go to the documentation of this file.
1
25// =========================================================================
26// CLASS : BasicProactor
27// METHOD : BasicProactor
28// =========================================================================
29template <typename IoPolicy>
31: _commands (_queueSize)
32, _wakeup (is_default_v<IoPolicy> ? eventfd (0, EFD_NONBLOCK | EFD_CLOEXEC) : -1)
33{
34 static_assert (has_spin_v<IoPolicy> || !has_sqpoll_v<IoPolicy>, "spin required for sq poll policy");
35
36 io_uring_params params{};
37 params.flags = IoPolicy::flags;
38
39 if constexpr (has_cq_entries_v<IoPolicy>)
40 {
41 params.flags |= IORING_SETUP_CQSIZE;
42 params.cq_entries = IoPolicy::cqEntries;
43 }
44
45 if constexpr (has_sq_thread_idle_v<IoPolicy>)
46 {
47 params.sq_thread_idle = IoPolicy::sqThreadIdle;
48 }
49
50 if constexpr (has_sq_thread_cpu_v<IoPolicy>)
51 {
52 params.flags |= IORING_SETUP_SQ_AFF;
53 params.sq_thread_cpu = IoPolicy::sqThreadCpu;
54 }
55
56 if (io_uring_queue_init_params (IoPolicy::sqEntries, &_ring, &params) < 0)
57 {
58 // LCOV_EXCL_START
59 ::close (_wakeup);
60 throw std::system_error (errno, std::system_category (), "io_uring_queue_init failed");
61 // LCOV_EXCL_STOP
62 }
63
64 if constexpr (is_default_v<IoPolicy>)
65 {
66 _wakeupOp = IoOperation::makeRead (_wakeup, &_wakeupBuf, sizeof (_wakeupBuf), nullptr);
67 }
68
69 _pendingOps.reserve (IoPolicy::sqEntries);
70}
71
72// =========================================================================
73// CLASS : BasicProactor
74// METHOD : ~BasicProactor
75// =========================================================================
76template <typename IoPolicy>
78{
79 stop (true);
80
81 io_uring_queue_exit (&_ring);
82
83 if (_wakeup != -1)
84 {
85 ::close (_wakeup);
86 }
87}
88
89// =========================================================================
90// CLASS : BasicProactor
91// METHOD : flush
92// =========================================================================
93template <typename IoPolicy>
94int join::BasicProactor<IoPolicy>::flush (bool sync) noexcept
95{
96 if (isProactorThread ())
97 {
98 if (JOIN_UNLIKELY (io_uring_submit (&_ring) < 0))
99 {
100 // LCOV_EXCL_START
101 lastError = std::error_code (errno, std::system_category ());
102 return -1;
103 // LCOV_EXCL_STOP
104 }
105 return 0;
106 }
107
108 std::atomic<bool> done{false}, *pdone = nullptr;
109 std::error_code errc, *perrc = nullptr;
110
111 if (JOIN_UNLIKELY (sync))
112 {
113 pdone = &done;
114 perrc = &errc;
115 }
116
117 if (JOIN_UNLIKELY (writeCommand ({CommandType::Flush, nullptr, false, pdone, perrc, nullptr}) == -1))
118 {
119 return -1; // LCOV_EXCL_LINE
120 }
121
122 if (JOIN_UNLIKELY (sync))
123 {
124 Backoff backoff;
125 while (!done.load (std::memory_order_acquire))
126 {
127 backoff ();
128 }
129
130 if (JOIN_UNLIKELY (errc))
131 {
132 lastError = errc;
133 return -1;
134 }
135 }
136
137 return 0;
138}
139
140// =========================================================================
141// CLASS : BasicProactor
142// METHOD : run
143// =========================================================================
144template <typename IoPolicy>
146{
147 _threadId.store (Thread::currentId (), std::memory_order_release);
148
149 const int slack = prctl (PR_GET_TIMERSLACK, 0, 0, 0, 0);
150 prctl (PR_SET_TIMERSLACK, 1UL, 0, 0, 0);
151 _running.store (true, std::memory_order_release);
152 eventLoop ();
153 prctl (PR_SET_TIMERSLACK, static_cast<unsigned long> (slack), 0, 0, 0);
154
155 _threadId.store (Thread::invalidId, std::memory_order_release);
156}
157
158// =========================================================================
159// CLASS : BasicProactor
160// METHOD : stop
161// =========================================================================
162template <typename IoPolicy>
163void join::BasicProactor<IoPolicy>::stop (bool sync) noexcept
164{
165 if (isProactorThread ())
166 {
167 _running.store (false, std::memory_order_release);
168 cancelAllOperations ();
169 return;
170 }
171
172 if (JOIN_UNLIKELY (!isRunning ()))
173 {
174 return;
175 }
176
177 writeCommand ({CommandType::Stop, nullptr, sync, nullptr, nullptr, nullptr});
178
179 if (JOIN_LIKELY (sync))
180 {
181 waitStopped ();
182 }
183}
184
185// =========================================================================
186// CLASS : BasicProactor
187// METHOD : waitStopped
188// =========================================================================
189template <typename IoPolicy>
191{
192 Backoff backoff;
193
194 while (_threadId.load (std::memory_order_acquire) != Thread::invalidId)
195 {
196 backoff ();
197 }
198}
199
200// =========================================================================
201// CLASS : BasicProactor
202// METHOD : registerFixedBuffers
203// =========================================================================
204template <typename IoPolicy>
205template <size_t Count, size_t... Sizes>
206int join::BasicProactor<IoPolicy>::registerFixedBuffers (LocalMem::SlabAllocator<Count, Sizes...>& arena) noexcept
207{
208 return registerFixedBuffers (arena, std::make_index_sequence<sizeof...(Sizes)>{});
209}
210
211// =========================================================================
212// CLASS : BasicProactor
213// METHOD : unregisterFixedBuffers
214// =========================================================================
215template <typename IoPolicy>
217{
218 int ret = io_uring_unregister_buffers (&_ring);
219 if (JOIN_UNLIKELY (ret < 0))
220 {
221 lastError = std::error_code (-ret, std::system_category ());
222 return -1;
223 }
224
225 return 0;
226}
227
228// =========================================================================
229// CLASS : BasicProactor
230// METHOD : registerBufferRing
231// =========================================================================
232template <typename IoPolicy>
233template <size_t Count, size_t Size>
234int join::BasicProactor<IoPolicy>::registerBufferRing (uint16_t group, LocalMem::SlabAllocator<Count, Size>& arena)
235{
236 int result = 0;
237 std::error_code errc;
238
239 InvokeHandler fn = [this, group, &arena, &result, &errc] () {
240 if (JOIN_UNLIKELY (_bufferRings.count (group)))
241 {
242 lastError = make_error_code (Errc::InUse);
243 errc = lastError;
244 result = -1;
245 return;
246 }
247
248 auto it = _bufferRings.emplace (group, &_ring).first;
249 if (JOIN_UNLIKELY (it->second.registerBuffer (group, arena) == -1))
250 {
251 errc = lastError;
252 _bufferRings.erase (it);
253 result = -1;
254 return;
255 }
256 };
257
258 if (JOIN_UNLIKELY (!isRunning ()))
259 {
260 lastError = make_error_code (Errc::OperationFailed);
261 return -1;
262 }
263
264 if (JOIN_UNLIKELY (invoke (&fn) == -1))
265 {
266 return -1; // LCOV_EXCL_LINE
267 }
268
269 if (JOIN_UNLIKELY (result == -1))
270 {
271 lastError = errc;
272 return -1;
273 }
274
275 return 0;
276}
277
278// =========================================================================
279// CLASS : BasicProactor
280// METHOD : registerBufferRing
281// =========================================================================
282template <typename IoPolicy>
283template <size_t Count, size_t Size>
284int join::BasicProactor<IoPolicy>::registerBufferRing (LocalMem::SlabAllocator<Count, Size>& arena)
285{
286 int result = 0;
287 std::error_code errc;
288
289 InvokeHandler fn = [this, &arena, &result, &errc] () {
290 uint32_t group = 0;
291
292 while ((group <= UINT16_MAX) && _bufferRings.count (static_cast<uint16_t> (group)))
293 {
294 ++group;
295 }
296
297 if (JOIN_UNLIKELY (group > UINT16_MAX))
298 {
299 // LCOV_EXCL_START
300 lastError = make_error_code (Errc::OutOfMemory);
301 errc = lastError;
302 result = -1;
303 return;
304 // LCOV_EXCL_STOP
305 }
306
307 auto it = _bufferRings.emplace (static_cast<uint16_t> (group), &_ring).first;
308 if (JOIN_UNLIKELY (it->second.registerBuffer (static_cast<uint16_t> (group), arena) == -1))
309 {
310 errc = lastError;
311 _bufferRings.erase (it);
312 result = -1;
313 return;
314 }
315
316 result = static_cast<int> (group);
317 };
318
319 if (JOIN_UNLIKELY (!isRunning ()))
320 {
321 lastError = make_error_code (Errc::OperationFailed);
322 return -1;
323 }
324
325 if (JOIN_UNLIKELY (invoke (&fn) == -1))
326 {
327 return -1; // LCOV_EXCL_LINE
328 }
329
330 if (JOIN_UNLIKELY (result == -1))
331 {
332 lastError = errc;
333 }
334
335 return result;
336}
337
338// =========================================================================
339// CLASS : BasicProactor
340// METHOD : unregisterBufferRing
341// =========================================================================
342template <typename IoPolicy>
344{
345 int result = 0;
346 std::error_code errc;
347
348 InvokeHandler fn = [this, group, &result, &errc] () {
349 auto it = _bufferRings.find (group);
350 if (JOIN_UNLIKELY (it == _bufferRings.end ()))
351 {
352 lastError = make_error_code (Errc::NotFound);
353 errc = lastError;
354 result = -1;
355 return;
356 }
357
358 if (JOIN_UNLIKELY (it->second.armed ()))
359 {
360 lastError = make_error_code (Errc::InUse);
361 errc = lastError;
362 result = -1;
363 return;
364 }
365
366 if (JOIN_UNLIKELY (it->second.unregisterBuffer () == -1))
367 {
368 // LCOV_EXCL_START
369 errc = lastError;
370 result = -1;
371 return;
372 // LCOV_EXCL_STOP
373 }
374
375 _bufferRings.erase (it);
376 };
377
378 if (JOIN_UNLIKELY (!isRunning ()))
379 {
380 lastError = make_error_code (Errc::OperationFailed);
381 return -1;
382 }
383
384 if (JOIN_UNLIKELY (invoke (&fn) == -1))
385 {
386 return -1; // LCOV_EXCL_LINE
387 }
388
389 if (JOIN_UNLIKELY (result == -1))
390 {
391 lastError = errc;
392 return -1;
393 }
394
395 return 0;
396}
397
398#ifdef JOIN_HAS_NUMA
399// =========================================================================
400// CLASS : BasicProactor
401// METHOD : mbind
402// =========================================================================
403template <typename IoPolicy>
404int join::BasicProactor<IoPolicy>::mbind (int numa) const noexcept
405{
406 if (_wheel.mbind (numa) == -1)
407 {
408 return -1; // LCOV_EXCL_LINE
409 }
410
411 return _commands.mbind (numa);
412}
413#endif
414
415// =========================================================================
416// CLASS : BasicProactor
417// METHOD : mlock
418// =========================================================================
419template <typename IoPolicy>
420int join::BasicProactor<IoPolicy>::mlock () const noexcept
421{
422 if (_wheel.mlock () == -1)
423 {
424 return -1; // LCOV_EXCL_LINE
425 }
426
427 return _commands.mlock ();
428}
429
430// =========================================================================
431// CLASS : BasicProactor
432// METHOD : isRunning
433// =========================================================================
434template <typename IoPolicy>
435bool join::BasicProactor<IoPolicy>::isRunning () const noexcept
436{
437 return _threadId.load (std::memory_order_acquire) != Thread::invalidId;
438}
439
440// =========================================================================
441// CLASS : BasicProactor
442// METHOD : isProactorThread
443// =========================================================================
444template <typename IoPolicy>
446{
447 return _threadId.load (std::memory_order_acquire) == Thread::currentId ();
448}
449
450// =========================================================================
451// CLASS : BasicProactor
452// METHOD : wheel
453// =========================================================================
454template <typename IoPolicy>
456{
457 return _wheel;
458}
459
460// =========================================================================
461// CLASS : BasicProactor
462// METHOD : writeCommand
463// =========================================================================
464template <typename IoPolicy>
465int join::BasicProactor<IoPolicy>::writeCommand (const Command& cmd) noexcept
466{
467 if (JOIN_UNLIKELY (_commands.push (cmd) == -1))
468 {
469 return -1; // LCOV_EXCL_LINE
470 }
471
472 return wakeup ();
473}
474
475// =========================================================================
476// CLASS : BasicProactor
477// METHOD : wakeup
478// =========================================================================
479template <typename IoPolicy>
481{
482 if constexpr (is_default_v<IoPolicy>)
483 {
484 // pairs with the fence in the event loop: an acquire load would let the push above be
485 // reordered after it, the loop would sleep and the wakeup would be lost.
486 std::atomic_thread_fence (std::memory_order_seq_cst);
487
488 WakeupState state = _wakeupState.load (std::memory_order_relaxed);
489 if (state != WakeupState::Sleeping)
490 {
491 return 0;
492 }
493
494 if (_wakeupState.compare_exchange_strong (state, WakeupState::Waking, std::memory_order_seq_cst))
495 {
496 uint64_t value = 1;
497 if (JOIN_UNLIKELY (::write (_wakeup, &value, sizeof (uint64_t)) == -1))
498 {
499 _wakeupState.store (WakeupState::Sleeping, std::memory_order_seq_cst); // LCOV_EXCL_LINE
500 }
501 }
502 }
503
504 return 0;
505}
506
507// =========================================================================
508// CLASS : BasicProactor
509// METHOD : readCommands
510// =========================================================================
511template <typename IoPolicy>
513{
514 Command cmd;
515 while (_commands.tryPop (cmd) == 0)
516 {
517 processCommand (cmd);
518 }
519}
520
521// =========================================================================
522// CLASS : BasicProactor
523// METHOD : processCommand
524// =========================================================================
525template <typename IoPolicy>
526void join::BasicProactor<IoPolicy>::processCommand (const Command& cmd) noexcept
527{
528 int err = 0;
529
530 switch (cmd.type)
531 {
532 case CommandType::Submit:
533 err = submitOperation (*cmd.op, cmd.flush);
534 if (JOIN_UNLIKELY ((err == -1) && (cmd.done == nullptr) &&
535 (cmd.op->state.load (std::memory_order_relaxed) == IoOperation::State::Idle)))
536 {
537 dispatchOperation (*cmd.op, -lastError.default_error_condition ().value (), false);
538 }
539 break;
540
541 case CommandType::Cancel:
542 err = cancelOperation (*cmd.op, cmd.flush);
543 break;
544
545 case CommandType::Invoke:
546 err = invokeFunction (cmd.fn);
547 break;
548
549 case CommandType::Stop:
550 _running.store (false, std::memory_order_release);
551 cancelAllOperations ();
552 if (JOIN_UNLIKELY (cmd.flush))
553 {
554 io_uring_submit (&_ring);
555 }
556 break;
557
558 case CommandType::Flush:
559 if (JOIN_UNLIKELY (io_uring_submit (&_ring) < 0))
560 {
561 // LCOV_EXCL_START
562 lastError = std::error_code (errno, std::system_category ());
563 err = -1;
564 // LCOV_EXCL_STOP
565 }
566 break;
567
568 default: // LCOV_EXCL_LINE
569 break; // LCOV_EXCL_LINE
570 }
571
572 if (JOIN_UNLIKELY (cmd.done))
573 {
574 if (cmd.errc && (err != 0))
575 {
576 *cmd.errc = lastError;
577 }
578 cmd.done->store (true, std::memory_order_release);
579 }
580}
581
582// =========================================================================
583// CLASS : BasicProactor
584// METHOD : submitOperation
585// =========================================================================
586template <typename IoPolicy>
587int join::BasicProactor<IoPolicy>::submitOperation (IoOperation& op, bool flush) noexcept
588{
589 if (JOIN_UNLIKELY (!_running.load (std::memory_order_acquire)))
590 {
591 lastError = std::make_error_code (std::errc::operation_canceled);
592 return -1;
593 }
594
595 Backoff backoff;
596 while (JOIN_UNLIKELY (op.state.load (std::memory_order_acquire) == IoOperation::State::Suspended))
597 {
598 backoff ();
599 }
600
601 IoOperation::State expected = IoOperation::State::Idle;
602 if (!op.state.compare_exchange_strong (expected, IoOperation::State::Submitted, std::memory_order_acquire,
603 std::memory_order_relaxed) &&
604 (expected == IoOperation::State::Busy))
605 {
606 op.state.store (IoOperation::State::Submitted, std::memory_order_release);
607 }
608
609 if (JOIN_UNLIKELY (op.fd () < 0))
610 {
611 resetOperation (op);
612 lastError = std::make_error_code (std::errc::bad_file_descriptor);
613 return -1;
614 }
615
616 if (JOIN_UNLIKELY ((op.index < _pendingOps.size ()) && (_pendingOps[op.index] == &op)))
617 {
618 lastError = make_error_code (std::errc::device_or_resource_busy);
619 return -1;
620 }
621
622 IoRingBuffer* ring = nullptr;
623
624 if (JOIN_UNLIKELY (op.multishot && ((op.code == static_cast<uint8_t> (IoOperation::Opcode::RecvMsg)) ||
625 (op.code == static_cast<uint8_t> (IoOperation::Opcode::Recv)))))
626 {
627 auto it = _bufferRings.find (op.group);
628 if (JOIN_UNLIKELY (it == _bufferRings.end ()))
629 {
630 resetOperation (op);
631 lastError = make_error_code (Errc::NotFound);
632 return -1;
633 }
634
635 ring = &it->second;
636 }
637
638 io_uring_sqe* sqe = getSqe ();
639 if (JOIN_UNLIKELY (sqe == nullptr))
640 {
641 // LCOV_EXCL_START
642 resetOperation (op);
643 lastError = make_error_code (std::errc::no_buffer_space);
644 return -1;
645 // LCOV_EXCL_STOP
646 }
647
648 prepareSqe (sqe, op);
649 op.index = static_cast<uint32_t> (_pendingOps.size ());
650 _pendingOps.push_back (&op);
651
652 if (JOIN_UNLIKELY (ring != nullptr))
653 {
654 op.ring = ring;
655 ring->bind ();
656 }
657
658 if (JOIN_UNLIKELY (flush))
659 {
660 io_uring_submit (&_ring);
661 }
662
663 return 0;
664}
665
666// =========================================================================
667// CLASS : BasicProactor
668// METHOD : cancelOperation
669// =========================================================================
670template <typename IoPolicy>
671int join::BasicProactor<IoPolicy>::cancelOperation (IoOperation& op, bool flush) noexcept
672{
673 if (JOIN_UNLIKELY (op.fd () < 0))
674 {
675 lastError = std::make_error_code (std::errc::bad_file_descriptor);
676 return -1;
677 }
678
679 if (JOIN_UNLIKELY (op.state.load (std::memory_order_acquire) != IoOperation::State::Submitted))
680 {
681 lastError = make_error_code (Errc::OperationFailed);
682 return -1;
683 }
684
685 if (JOIN_UNLIKELY (op.index >= _pendingOps.size () || _pendingOps[op.index] != &op))
686 {
687 lastError = make_error_code (Errc::InvalidParam);
688 return -1;
689 }
690
691 io_uring_sqe* sqe = getSqe ();
692 if (JOIN_UNLIKELY (sqe == nullptr))
693 {
694 // LCOV_EXCL_START
695 lastError = make_error_code (Errc::OperationFailed);
696 return -1;
697 // LCOV_EXCL_STOP
698 }
699
700 io_uring_prep_cancel (sqe, &op, 0);
701 io_uring_sqe_set_data (sqe, nullptr);
702
703 if (JOIN_UNLIKELY (flush))
704 {
705 io_uring_submit (&_ring);
706 }
707
708 return 0;
709}
710
711// =========================================================================
712// CLASS : BasicProactor
713// METHOD : cancelAllOperations
714// =========================================================================
715template <typename IoPolicy>
717{
718 for (IoOperation* op : _pendingOps)
719 {
720 Backoff backoff;
721 while (op->state.load (std::memory_order_acquire) == IoOperation::State::Suspended)
722 {
723 backoff (); // LCOV_EXCL_LINE
724 }
725 cancelOperation (*op, false);
726 }
727}
728
729// =========================================================================
730// CLASS : BasicProactor
731// METHOD : endOperation
732// =========================================================================
733template <typename IoPolicy>
734void join::BasicProactor<IoPolicy>::endOperation (IoOperation& op, int result, bool cancelled) noexcept
735{
736 if (JOIN_LIKELY (op.index < _pendingOps.size () && _pendingOps[op.index] == &op))
737 {
738 IoOperation* last = _pendingOps.back ();
739 _pendingOps[op.index] = last;
740 last->index = op.index;
741 _pendingOps.pop_back ();
742 }
743
744 dispatchOperation (op, result, cancelled);
745}
746
747// =========================================================================
748// CLASS : BasicProactor
749// METHOD : registerFixedBuffers
750// =========================================================================
751template <typename IoPolicy>
752template <size_t Count, size_t... Sizes, size_t... Is>
753int join::BasicProactor<IoPolicy>::registerFixedBuffers (LocalMem::SlabAllocator<Count, Sizes...>& arena,
754 std::index_sequence<Is...>) noexcept
755{
756 iovec iovecs[] = {iovec{arena.template getPtr<Is> (0), Count * Sizes}...};
757
758 int ret = io_uring_register_buffers (&_ring, iovecs, sizeof...(Sizes));
759 if (JOIN_UNLIKELY (ret < 0))
760 {
761 lastError = std::error_code (-ret, std::system_category ());
762 return -1;
763 }
764
765 return 0;
766}
767
768// =========================================================================
769// CLASS : BasicProactor
770// METHOD : getSqe
771// =========================================================================
772template <typename IoPolicy>
773io_uring_sqe* join::BasicProactor<IoPolicy>::getSqe () noexcept
774{
775 io_uring_sqe* sqe = io_uring_get_sqe (&_ring);
776 if (JOIN_UNLIKELY (sqe == nullptr))
777 {
778 io_uring_submit (&_ring);
779 sqe = io_uring_get_sqe (&_ring);
780 }
781
782 return sqe;
783}
784
785// =========================================================================
786// CLASS : BasicProactor
787// METHOD : prepareSqe
788// =========================================================================
789template <typename IoPolicy>
790void join::BasicProactor<IoPolicy>::prepareSqe (io_uring_sqe* sqe, IoOperation& op) noexcept
791{
792 switch (static_cast<IoOperation::Opcode> (op.code))
793 {
794 case IoOperation::Opcode::Poll:
795 if (op.multishot)
796 {
797 io_uring_prep_poll_multishot (sqe, op.data.poll.fd, op.data.poll.events);
798 }
799 else
800 {
801 io_uring_prep_poll_add (sqe, op.data.poll.fd, op.data.poll.events);
802 }
803 break;
804
805 case IoOperation::Opcode::Accept:
806 if (op.multishot)
807 {
808 io_uring_prep_multishot_accept (sqe, op.data.accept.fd, op.data.accept.addr, op.data.accept.addrlen,
809 op.data.accept.flags);
810 }
811 else
812 {
813 io_uring_prep_accept (sqe, op.data.accept.fd, op.data.accept.addr, op.data.accept.addrlen,
814 op.data.accept.flags);
815 }
816 break;
817
818 case IoOperation::Opcode::Connect:
819 io_uring_prep_connect (sqe, op.data.connect.fd, op.data.connect.addr, op.data.connect.addrlen);
820 break;
821
822 case IoOperation::Opcode::Read:
823 io_uring_prep_read (sqe, op.data.rw.fd, op.data.rw.buf, op.data.rw.len, 0);
824 break;
825
826 case IoOperation::Opcode::Write:
827 io_uring_prep_write (sqe, op.data.rw.fd, op.data.rw.buf, op.data.rw.len, 0);
828 break;
829
830 case IoOperation::Opcode::ReadFixed:
831 io_uring_prep_read_fixed (sqe, op.data.rw.fd, op.data.rw.buf, op.data.rw.len, 0, op.data.rw.index);
832 break;
833
834 case IoOperation::Opcode::WriteFixed:
835 io_uring_prep_write_fixed (sqe, op.data.rw.fd, op.data.rw.buf, op.data.rw.len, 0, op.data.rw.index);
836 break;
837
838 case IoOperation::Opcode::RecvMsg:
839 if (op.multishot)
840 {
841 op.data.msg.msg->msg_namelen = op.data.msg.namelen;
842 op.data.msg.msg->msg_controllen = op.data.msg.controllen;
843 op.data.msg.msg->msg_iovlen = 0;
844 io_uring_prep_recvmsg_multishot (sqe, op.data.msg.fd, op.data.msg.msg, op.data.msg.flags);
845 sqe->flags |= IOSQE_BUFFER_SELECT;
846 sqe->buf_group = op.group;
847 }
848 else
849 {
850 io_uring_prep_recvmsg (sqe, op.data.msg.fd, op.data.msg.msg, op.data.msg.flags);
851 }
852 break;
853
854 case IoOperation::Opcode::SendMsg:
855 io_uring_prep_sendmsg (sqe, op.data.msg.fd, op.data.msg.msg, op.data.msg.flags);
856 break;
857
858 case IoOperation::Opcode::Recv:
859 if (op.multishot)
860 {
861 io_uring_prep_recv_multishot (sqe, op.data.stream.fd, nullptr, 0, op.data.stream.flags);
862 sqe->flags |= IOSQE_BUFFER_SELECT;
863 sqe->buf_group = op.group;
864 }
865 else
866 {
867 io_uring_prep_recv (sqe, op.data.stream.fd, op.data.stream.buf, op.data.stream.len,
868 op.data.stream.flags);
869 }
870 break;
871
872 case IoOperation::Opcode::Send:
873 io_uring_prep_send (sqe, op.data.stream.fd, op.data.stream.buf, op.data.stream.len, op.data.stream.flags);
874 break;
875
876 default: // LCOV_EXCL_LINE
877 io_uring_prep_nop (sqe); // LCOV_EXCL_LINE
878 }
879
880 io_uring_sqe_set_data (sqe, &op);
881
882 if (JOIN_UNLIKELY (op.linked))
883 {
884 sqe->flags |= IOSQE_IO_LINK;
885 }
886}
887
888// =========================================================================
889// CLASS : BasicProactor
890// METHOD : dispatchCqe
891// =========================================================================
892template <typename IoPolicy>
893inline void join::BasicProactor<IoPolicy>::dispatchCqe (io_uring_cqe* cqe) noexcept
894{
895 IoOperation* op = static_cast<IoOperation*> (io_uring_cqe_get_data (cqe));
896
897 if constexpr (is_default_v<IoPolicy>)
898 {
899 if (JOIN_UNLIKELY (op == &_wakeupOp))
900 {
901 endOperation (*op, cqe->res, false);
902 if (JOIN_LIKELY (_running.load (std::memory_order_acquire)))
903 {
904 submitOperation (_wakeupOp, true);
905 }
906 return;
907 }
908 }
909
910 if (JOIN_UNLIKELY (op == nullptr))
911 {
912 return;
913 }
914
915 if (JOIN_UNLIKELY (op->state == IoOperation::State::Idle))
916 {
917 return; // LCOV_EXCL_LINE
918 }
919
920 IoOperation::State current = op->state.load (std::memory_order_acquire);
921 Backoff backoff;
922
923 while (JOIN_UNLIKELY (current == IoOperation::State::Suspended))
924 {
925 backoff ();
926 current = op->state.load (std::memory_order_acquire);
927 }
928
929 if (JOIN_UNLIKELY (current == IoOperation::State::Idle))
930 {
931 return; // LCOV_EXCL_LINE
932 }
933
934 int result = cqe->res;
935 IoRingBuffer* br = nullptr;
936 uint16_t bid = 0;
937
938 if ((cqe->flags & IORING_CQE_F_BUFFER) != 0)
939 {
940 bid = static_cast<uint16_t> (cqe->flags >> IORING_CQE_BUFFER_SHIFT);
941 br = op->ring;
942
943 if (op->code == static_cast<uint8_t> (IoOperation::Opcode::RecvMsg))
944 {
945 msghdr reserved = {};
946 reserved.msg_namelen = op->data.msg.namelen;
947 reserved.msg_controllen = op->data.msg.controllen;
948
949 io_uring_recvmsg_out* out = io_uring_recvmsg_validate (br->get (bid), result, &reserved);
950 if (JOIN_UNLIKELY (out == nullptr))
951 {
952 result = -EFAULT; // LCOV_EXCL_LINE
953 }
954 else
955 {
956 uint32_t payloadlen = io_uring_recvmsg_payload_length (out, result, &reserved);
957
958 op->data.msg.msg->msg_name = io_uring_recvmsg_name (out);
959 op->data.msg.msg->msg_control = io_uring_recvmsg_cmsg_firsthdr (out, &reserved);
960 op->data.msg.msg->msg_iov->iov_base = io_uring_recvmsg_payload (out, &reserved);
961 op->data.msg.msg->msg_iov->iov_len = payloadlen;
962 op->data.msg.msg->msg_iovlen = 1;
963 op->data.msg.msg->msg_namelen = out->namelen;
964 op->data.msg.msg->msg_controllen = out->controllen;
965 op->data.msg.msg->msg_flags = static_cast<int> (out->flags);
966
967 result = static_cast<int> (payloadlen);
968 }
969 }
970 else
971 {
972 op->data.stream.buf = br->get (bid);
973 }
974 }
975
976 if ((cqe->flags & IORING_CQE_F_MORE) != 0)
977 {
978 op->more = true;
979 notifyOperation (*op, result, false);
980 }
981 else
982 {
983 endOperation (*op, result, (result == -ECANCELED));
984 }
985
986 if (br != nullptr)
987 {
988 br->recycle (bid);
989 }
990}
991
992// =========================================================================
993// CLASS : BasicProactor
994// METHOD : eventLoop
995// =========================================================================
996template <typename IoPolicy>
998{
999 eventLoop (std::bool_constant<has_spin_v<IoPolicy>>{}, std::bool_constant<has_sqpoll_v<IoPolicy>>{});
1000}
1001
1002// =========================================================================
1003// CLASS : BasicProactor
1004// METHOD : eventLoop
1005// =========================================================================
1006template <typename IoPolicy>
1007void join::BasicProactor<IoPolicy>::eventLoop (std::false_type, std::false_type) noexcept
1008{
1009 if (JOIN_LIKELY (_running.load (std::memory_order_acquire)))
1010 {
1011 submitOperation (_wakeupOp, true);
1012 }
1013
1014 Backoff backoff;
1015 bool running;
1016
1017 while ((running = _running.load (std::memory_order_acquire)) || !_pendingOps.empty ())
1018 {
1019 WheelPolicy::advance (_wheel);
1020 readCommands ();
1021 io_uring_submit (&_ring);
1022
1023 io_uring_cqe* cqe = nullptr;
1024 if (io_uring_peek_cqe (&_ring, &cqe) == 0)
1025 {
1026 backoff.reset ();
1027
1028 do
1029 {
1030 dispatchCqe (cqe);
1031 io_uring_cqe_seen (&_ring, cqe);
1032 }
1033 while (io_uring_peek_cqe (&_ring, &cqe) == 0);
1034
1035 continue;
1036 }
1037
1038 if (!running || !backoff.spinExhausted ())
1039 {
1040 backoff ();
1041 continue;
1042 }
1043
1044 _wakeupState.store (WakeupState::Sleeping, std::memory_order_relaxed);
1045 std::atomic_thread_fence (std::memory_order_seq_cst);
1046 readCommands ();
1047 WheelPolicy::advance (_wheel);
1048
1049 io_uring_submit (&_ring);
1050 if (JOIN_LIKELY (_running.load (std::memory_order_acquire)))
1051 {
1052 if (io_uring_peek_cqe (&_ring, &cqe) != 0)
1053 {
1054 __kernel_timespec ts;
1055 io_uring_wait_cqe_timeout (&_ring, &cqe, WheelPolicy::timeout (_wheel, ts));
1056 }
1057 }
1058
1059 _wakeupState.store (WakeupState::Spinning, std::memory_order_seq_cst);
1060 }
1061}
1062
1063// =========================================================================
1064// CLASS : BasicProactor
1065// METHOD : eventLoop
1066// =========================================================================
1067template <typename IoPolicy>
1068void join::BasicProactor<IoPolicy>::eventLoop (std::true_type, std::false_type) noexcept
1069{
1070 Backoff backoff{std::chrono::nanoseconds (spin_ns_v<IoPolicy>), std::chrono::nanoseconds (yield_ns_v<IoPolicy>),
1071 std::chrono::nanoseconds (sleep_ns_v<IoPolicy>)};
1072 bool running;
1073
1074 while ((running = _running.load (std::memory_order_acquire)) || !_pendingOps.empty ())
1075 {
1076 WheelPolicy::advance (_wheel);
1077
1078 if (JOIN_LIKELY (running))
1079 {
1080 readCommands ();
1081 }
1082 else
1083 {
1084 io_uring_submit (&_ring); // LCOV_EXCL_LINE
1085 }
1086
1087 io_uring_cqe* cqe = nullptr;
1088 if (JOIN_UNLIKELY (io_uring_peek_cqe (&_ring, &cqe) != 0))
1089 {
1090 backoff ();
1091 continue;
1092 }
1093
1094 do
1095 {
1096 dispatchCqe (cqe);
1097 io_uring_cqe_seen (&_ring, cqe);
1098 }
1099 while (io_uring_peek_cqe (&_ring, &cqe) == 0);
1100
1101 backoff.reset ();
1102 }
1103}
1104
1105// =========================================================================
1106// CLASS : BasicProactor
1107// METHOD : eventLoop
1108// =========================================================================
1109template <typename IoPolicy>
1110void join::BasicProactor<IoPolicy>::eventLoop (std::true_type, std::true_type) noexcept
1111{
1112 Backoff backoff{std::chrono::nanoseconds (spin_ns_v<IoPolicy>), std::chrono::nanoseconds (yield_ns_v<IoPolicy>),
1113 std::chrono::nanoseconds (sleep_ns_v<IoPolicy>)};
1114 bool running;
1115
1116 while ((running = _running.load (std::memory_order_acquire)) || !_pendingOps.empty ())
1117 {
1118 WheelPolicy::advance (_wheel);
1119
1120 if (JOIN_LIKELY (running))
1121 {
1122 readCommands ();
1123 // publishes the SQ tail and, in SQPOLL mode, only enters the kernel
1124 // if the poll thread went to sleep (IORING_SQ_NEED_WAKEUP).
1125 io_uring_submit (&_ring);
1126 }
1127 else
1128 {
1129 io_uring_submit (&_ring); // LCOV_EXCL_LINE
1130 }
1131
1132 io_uring_cqe* cqe = nullptr;
1133 if (JOIN_UNLIKELY (io_uring_peek_cqe (&_ring, &cqe) != 0))
1134 {
1135 backoff ();
1136 continue;
1137 }
1138
1139 do
1140 {
1141 dispatchCqe (cqe);
1142 io_uring_cqe_seen (&_ring, cqe);
1143 }
1144 while (io_uring_peek_cqe (&_ring, &cqe) == 0);
1145
1146 backoff.reset ();
1147 }
1148}
basic proactor class.
Definition proactor.hpp:158
int mlock() const noexcept
lock proactor command queue and timer wheel memory in RAM.
Definition proactor_epoll_impl.hpp:288
int unregisterBufferRing(uint16_t group)
unregister a provided buffer ring.
Definition proactor_epoll_impl.hpp:213
bool isRunning() const noexcept
check if the event loop is running.
Definition proactor_epoll_impl.hpp:302
void run()
run the event loop (blocking).
Definition proactor_epoll_impl.hpp:61
~BasicProactor() noexcept
destroy instance.
Definition proactor_epoll_impl.hpp:50
int registerBufferRing(uint16_t group, LocalMem::SlabAllocator< Count, Size > &arena)
register a provided buffer ring.
Definition proactor_epoll_impl.hpp:107
Wheel & wheel() noexcept
get the timer wheel advanced by the event loop.
Definition proactor_epoll_impl.hpp:320
void stop(bool sync=true) noexcept
stop the event loop, ignored if no event loop is running.
Definition proactor_epoll_impl.hpp:71
bool isProactorThread() const noexcept
check if the calling thread is the proactor thread.
Definition proactor_epoll_impl.hpp:311
void waitStopped() const noexcept
wait for the event loop thread to terminate.
Definition proactor_epoll_impl.hpp:97
BasicProactor()
initialize the proactor and its I/O backend.
Definition proactor_epoll_impl.hpp:32
timing wheel.
Definition wheel.hpp:58
constexpr bool is_default_v
Definition io_policy.hpp:77
std::error_code make_error_code(join::Errc code) noexcept
Create an std::error_code object.
Definition error.cpp:195
#define JOIN_LIKELY(x)
Definition utils.hpp:50
#define JOIN_UNLIKELY(x)
Definition utils.hpp:51