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