join 1.0
lightweight network framework library
Loading...
Searching...
No Matches
proactor_epoll_impl.hpp
Go to the documentation of this file.
1
25// C.
26#include <poll.h>
27
28// =========================================================================
29// CLASS : BasicProactor
30// METHOD : BasicProactor
31// =========================================================================
33: _commands (_queueSize)
34, _wakeup (eventfd (0, EFD_NONBLOCK | EFD_CLOEXEC))
35, _readOps (256, nullptr)
36, _writeOps (256, nullptr)
37{
38 if (_wakeup == -1)
39 {
40 throw std::system_error (errno, std::system_category (), "eventfd failed"); // LCOV_EXCL_LINE
41 }
42
43 _reactor.addHandler (_wakeup, this, true, false, false);
44}
45
46// =========================================================================
47// CLASS : BasicProactor
48// METHOD : ~BasicProactor
49// =========================================================================
51{
52 stop (true);
53
54 ::close (_wakeup);
55}
56
57// =========================================================================
58// CLASS : BasicProactor
59// METHOD : run
60// =========================================================================
62{
63 _wakeupState.store (WakeupState::Sleeping, std::memory_order_seq_cst);
64 _reactor.run ();
65}
66
67// =========================================================================
68// CLASS : BasicProactor
69// METHOD : stop
70// =========================================================================
71inline void join::BasicProactor::stop (bool sync) noexcept
72{
73 if (isProactorThread ())
74 {
75 cancelAllOperations ();
76 _reactor.stop (false);
77 return;
78 }
79
80 if (JOIN_UNLIKELY (!isRunning ()))
81 {
82 return;
83 }
84
85 writeCommand ({CommandType::Stop, nullptr, sync, nullptr, nullptr, nullptr});
86
87 if (JOIN_LIKELY (sync))
88 {
89 waitStopped ();
90 }
91}
92
93// =========================================================================
94// CLASS : BasicProactor
95// METHOD : waitStopped
96// =========================================================================
97inline void join::BasicProactor::waitStopped () const noexcept
98{
99 _reactor.waitStopped ();
100}
101
102// =========================================================================
103// CLASS : BasicProactor
104// METHOD : registerBufferRing
105// =========================================================================
106template <size_t Count, size_t Size>
108{
109 int result = 0;
110 std::error_code errc;
111
112 InvokeHandler fn = [this, group, &arena, &result, &errc] () {
113 if (JOIN_UNLIKELY (_bufferRings.count (group)))
114 {
115 lastError = make_error_code (Errc::InUse);
116 errc = lastError;
117 result = -1;
118 return;
119 }
120
121 if (JOIN_UNLIKELY (_bufferRings[group].registerBuffer (group, arena) == -1))
122 {
123 errc = lastError;
124 _bufferRings.erase (group);
125 result = -1;
126 return;
127 }
128 };
129
130 if (JOIN_UNLIKELY (!isRunning ()))
131 {
133 return -1;
134 }
135
136 if (JOIN_UNLIKELY (invoke (&fn) == -1))
137 {
138 return -1; // LCOV_EXCL_LINE
139 }
140
141 if (JOIN_UNLIKELY (result == -1))
142 {
143 lastError = errc;
144 return -1;
145 }
146
147 return 0;
148}
149
150// =========================================================================
151// CLASS : BasicProactor
152// METHOD : registerBufferRing
153// =========================================================================
154template <size_t Count, size_t Size>
156{
157 int result = 0;
158 std::error_code errc;
159
160 InvokeHandler fn = [this, &arena, &result, &errc] () {
161 uint32_t group = 0;
162
163 while ((group <= UINT16_MAX) && _bufferRings.count (static_cast<uint16_t> (group)))
164 {
165 ++group;
166 }
167
168 if (JOIN_UNLIKELY (group > UINT16_MAX))
169 {
170 // LCOV_EXCL_START
172 errc = lastError;
173 result = -1;
174 return;
175 // LCOV_EXCL_STOP
176 }
177
178 if (JOIN_UNLIKELY (_bufferRings[static_cast<uint16_t> (group)].registerBuffer (static_cast<uint16_t> (group),
179 arena) == -1))
180 {
181 errc = lastError;
182 _bufferRings.erase (static_cast<uint16_t> (group));
183 result = -1;
184 return;
185 }
186
187 result = static_cast<int> (group);
188 };
189
190 if (JOIN_UNLIKELY (!isRunning ()))
191 {
193 return -1;
194 }
195
196 if (JOIN_UNLIKELY (invoke (&fn) == -1))
197 {
198 return -1; // LCOV_EXCL_LINE
199 }
200
201 if (JOIN_UNLIKELY (result == -1))
202 {
203 lastError = errc;
204 }
205
206 return result;
207}
208
209// =========================================================================
210// CLASS : BasicProactor
211// METHOD : unregisterBufferRing
212// =========================================================================
214{
215 int result = 0;
216 std::error_code errc;
217
218 InvokeHandler fn = [this, group, &result, &errc] () {
219 auto it = _bufferRings.find (group);
220 if (JOIN_UNLIKELY (it == _bufferRings.end ()))
221 {
222 lastError = make_error_code (Errc::NotFound);
223 errc = lastError;
224 result = -1;
225 return;
226 }
227
228 if (JOIN_UNLIKELY (it->second.armed ()))
229 {
230 lastError = make_error_code (Errc::InUse);
231 errc = lastError;
232 result = -1;
233 return;
234 }
235
236 if (JOIN_UNLIKELY (it->second.unregisterBuffer () == -1))
237 {
238 // LCOV_EXCL_START
239 errc = lastError;
240 result = -1;
241 return;
242 // LCOV_EXCL_STOP
243 }
244
245 _bufferRings.erase (it);
246 };
247
248 if (JOIN_UNLIKELY (!isRunning ()))
249 {
251 return -1;
252 }
253
254 if (JOIN_UNLIKELY (invoke (&fn) == -1))
255 {
256 return -1; // LCOV_EXCL_LINE
257 }
258
259 if (JOIN_UNLIKELY (result == -1))
260 {
261 lastError = errc;
262 return -1;
263 }
264
265 return 0;
266}
267
268#ifdef JOIN_HAS_NUMA
269// =========================================================================
270// CLASS : BasicProactor
271// METHOD : mbind
272// =========================================================================
273inline int join::BasicProactor::mbind (int numa) const noexcept
274{
275 if (_commands.mbind (numa) == -1)
276 {
277 return -1;
278 }
279
280 return _reactor.mbind (numa);
281}
282#endif
283
284// =========================================================================
285// CLASS : BasicProactor
286// METHOD : mlock
287// =========================================================================
288inline int join::BasicProactor::mlock () const noexcept
289{
290 if (_commands.mlock () == -1)
291 {
292 return -1;
293 }
294
295 return _reactor.mlock ();
296}
297
298// =========================================================================
299// CLASS : BasicProactor
300// METHOD : isRunning
301// =========================================================================
302inline bool join::BasicProactor::isRunning () const noexcept
303{
304 return _reactor.isRunning ();
305}
306
307// =========================================================================
308// CLASS : BasicProactor
309// METHOD : isProactorThread
310// =========================================================================
311inline bool join::BasicProactor::isProactorThread () const noexcept
312{
313 return _reactor.isReactorThread ();
314}
315
316// =========================================================================
317// CLASS : BasicProactor
318// METHOD : wheel
319// =========================================================================
321{
322 return _reactor.wheel ();
323}
324
325// =========================================================================
326// CLASS : BasicProactor
327// METHOD : writeCommand
328// =========================================================================
329inline int join::BasicProactor::writeCommand (const Command& cmd) noexcept
330{
331 if (JOIN_UNLIKELY (_commands.push (cmd) == -1))
332 {
333 return -1; // LCOV_EXCL_LINE
334 }
335
336 // pairs with the fence in the event loop: an acquire load would let the push above be
337 // reordered after it, the loop would sleep and the wakeup would be lost.
338 std::atomic_thread_fence (std::memory_order_seq_cst);
339
340 WakeupState state = _wakeupState.load (std::memory_order_relaxed);
341 if (state != WakeupState::Sleeping)
342 {
343 return 0;
344 }
345
346 if (_wakeupState.compare_exchange_strong (state, WakeupState::Waking, std::memory_order_seq_cst))
347 {
348 uint64_t value = 1;
349 if (JOIN_UNLIKELY (::write (_wakeup, &value, sizeof (uint64_t)) == -1))
350 {
351 _wakeupState.store (WakeupState::Sleeping, std::memory_order_seq_cst); // LCOV_EXCL_LINE
352 }
353 }
354
355 return 0;
356}
357
358// =========================================================================
359// CLASS : BasicProactor
360// METHOD : readCommands
361// =========================================================================
362inline void join::BasicProactor::readCommands () noexcept
363{
364 uint64_t count;
365 [[maybe_unused]] ssize_t nread = ::read (_wakeup, &count, sizeof (count));
366 _wakeupState.store (WakeupState::Sleeping, std::memory_order_relaxed);
367 std::atomic_thread_fence (std::memory_order_seq_cst);
368
369 Command cmd;
370 while (_commands.tryPop (cmd) == 0)
371 {
372 processCommand (cmd);
373 }
374}
375
376// =========================================================================
377// CLASS : BasicProactor
378// METHOD : processCommand
379// =========================================================================
380inline void join::BasicProactor::processCommand (const Command& cmd) noexcept
381{
382 int err = 0;
383
384 switch (cmd.type)
385 {
386 case CommandType::Submit:
387 err = submitOperation (*cmd.op, cmd.flush);
388 if (JOIN_UNLIKELY ((err != 0) && (cmd.done == nullptr) && (cmd.op->state == IoOperation::State::Idle)))
389 {
390 dispatchOperation (*cmd.op, -lastError.default_error_condition ().value (), false);
391 }
392 break;
393
394 case CommandType::Cancel:
395 err = cancelOperation (*cmd.op, cmd.flush);
396 break;
397
398 case CommandType::Invoke:
399 err = invokeFunction (cmd.fn);
400 break;
401
402 case CommandType::Stop:
403 cancelAllOperations ();
404 _reactor.stop (false);
405 break;
406
407 default: // LCOV_EXCL_LINE
408 break; // LCOV_EXCL_LINE
409 }
410
411 if (JOIN_UNLIKELY (cmd.done))
412 {
413 if (cmd.errc && (err != 0))
414 {
415 *cmd.errc = lastError;
416 }
417 cmd.done->store (true, std::memory_order_release);
418 }
419}
420
421// =========================================================================
422// CLASS : BasicProactor
423// METHOD : submitOperation
424// =========================================================================
425inline int join::BasicProactor::submitOperation (IoOperation& op, [[maybe_unused]] bool flush) noexcept
426{
427 Backoff backoff;
428 while (JOIN_UNLIKELY (op.state.load (std::memory_order_acquire) == IoOperation::State::Suspended))
429 {
430 backoff ();
431 }
432
434 if (!op.state.compare_exchange_strong (expected, IoOperation::State::Submitted, std::memory_order_acquire,
435 std::memory_order_relaxed) &&
436 (expected == IoOperation::State::Busy))
437 {
438 op.state.store (IoOperation::State::Submitted, std::memory_order_release);
439 }
440
441 if (JOIN_UNLIKELY (op.fd () < 0))
442 {
443 resetOperation (op);
444 lastError = std::make_error_code (std::errc::bad_file_descriptor);
445 return -1;
446 }
447
448 IoRingBuffer* ring = nullptr;
449
450 if (JOIN_UNLIKELY (op.multishot && ((op.code == static_cast<uint8_t> (IoOperation::Opcode::RecvMsg)) ||
451 (op.code == static_cast<uint8_t> (IoOperation::Opcode::Recv)))))
452 {
453 auto it = _bufferRings.find (op.group);
454 if (JOIN_UNLIKELY (it == _bufferRings.end ()))
455 {
456 resetOperation (op);
457 lastError = make_error_code (Errc::NotFound);
458 return -1;
459 }
460
461 ring = &it->second;
462 }
463
464 if (JOIN_UNLIKELY (static_cast<IoOperation::Opcode> (op.code) == IoOperation::Opcode::Connect))
465 {
466 if (JOIN_UNLIKELY (::connect (op.data.connect.fd, op.data.connect.addr, op.data.connect.addrlen) == -1 &&
467 errno != EINPROGRESS))
468 {
469 lastError = std::error_code (errno, std::system_category ());
470 resetOperation (op);
471 return -1;
472 }
473 }
474
475 if (JOIN_UNLIKELY (static_cast<size_t> (op.fd ()) >= _readOps.size ()))
476 {
477 size_t newSize = static_cast<size_t> (op.fd ()) + 1;
478 _readOps.resize (newSize, nullptr);
479 _writeOps.resize (newSize, nullptr);
480 }
481
482 if (JOIN_UNLIKELY (op.next != nullptr))
483 {
484 lastError = make_error_code (std::errc::device_or_resource_busy);
485 return -1;
486 }
487
488 IoOperation*& last = isWriteOperation (op) ? _writeOps[op.fd ()] : _readOps[op.fd ()];
489
490 if (JOIN_UNLIKELY (ring != nullptr))
491 {
492 op.ring = ring;
493 ring->bind ();
494 }
495
496 if (last != nullptr)
497 {
498 op.next = last->next;
499 last->next = &op;
500 last = &op;
501 return 0;
502 }
503
504 op.next = &op;
505 last = &op;
506
507 int err = _reactor.addHandler (op.fd (), this, _readOps[op.fd ()] != nullptr, _writeOps[op.fd ()] != nullptr);
508 if (JOIN_UNLIKELY (err == -1))
509 {
510 op.next = nullptr;
511 last = nullptr;
512
513 if (JOIN_UNLIKELY (ring != nullptr))
514 {
515 op.ring->unbind ();
516 op.ring = nullptr;
517 }
518
519 resetOperation (op);
520 }
521
522 return err;
523}
524
525// =========================================================================
526// CLASS : BasicProactor
527// METHOD : cancelOperation
528// =========================================================================
529inline int join::BasicProactor::cancelOperation (IoOperation& op, [[maybe_unused]] bool flush) noexcept
530{
531 if (JOIN_UNLIKELY (op.fd () < 0))
532 {
533 lastError = std::make_error_code (std::errc::bad_file_descriptor);
534 return -1;
535 }
536
538 {
540 return -1;
541 }
542
543 if (JOIN_UNLIKELY (static_cast<size_t> (op.fd ()) >= _readOps.size ()))
544 {
545 lastError = std::make_error_code (std::errc::bad_file_descriptor);
546 return -1;
547 }
548
549 if (JOIN_UNLIKELY (!unqueueOperation (op)))
550 {
552 return -1;
553 }
554
555 int ret = 0;
556
557 if (_readOps[op.fd ()] == nullptr && _writeOps[op.fd ()] == nullptr)
558 {
559 ret = _reactor.delHandler (op.fd ());
560 }
561 else if ((isWriteOperation (op) ? _writeOps[op.fd ()] : _readOps[op.fd ()]) == nullptr)
562 {
563 ret = _reactor.addHandler (op.fd (), this, _readOps[op.fd ()] != nullptr, _writeOps[op.fd ()] != nullptr);
564 }
565
566 dispatchOperation (op, -ECANCELED, true);
567
568 return ret;
569}
570
571// =========================================================================
572// CLASS : BasicProactor
573// METHOD : cancelAllOperations
574// =========================================================================
575inline void join::BasicProactor::cancelAllOperations () noexcept
576{
577 for (size_t fd = 0; fd < _readOps.size (); ++fd)
578 {
579 IoOperation* rOp = std::exchange (_readOps[fd], nullptr);
580 IoOperation* wOp = std::exchange (_writeOps[fd], nullptr);
581 if (rOp || wOp)
582 {
583 _reactor.delHandler (fd);
584 }
585 if (rOp != nullptr)
586 {
587 drainQueue (*rOp, -ECANCELED, 0, true);
588 }
589 if (wOp != nullptr)
590 {
591 drainQueue (*wOp, -ECANCELED, 0, true);
592 }
593 }
594}
595
596// =========================================================================
597// CLASS : BasicProactor
598// METHOD : endOperation
599// =========================================================================
600inline void join::BasicProactor::endOperation (IoOperation& op, int result, bool cancelled) noexcept
601{
602 int fd = op.fd ();
603
604 if (JOIN_UNLIKELY (fd < 0 || static_cast<size_t> (fd) >= _readOps.size ()))
605 {
606 return; // LCOV_EXCL_LINE
607 }
608
609 unqueueOperation (op);
610
611 if (_readOps[fd] == nullptr && _writeOps[fd] == nullptr)
612 {
613 _reactor.delHandler (fd);
614 }
615 else if ((isWriteOperation (op) ? _writeOps[fd] : _readOps[fd]) == nullptr)
616 {
617 _reactor.addHandler (fd, this, _readOps[fd] != nullptr, _writeOps[fd] != nullptr);
618 }
619
620 dispatchOperation (op, result, cancelled);
621}
622
623// =========================================================================
624// CLASS : BasicProactor
625// METHOD : onEvent
626// =========================================================================
627inline void join::BasicProactor::onEvent (int fd, uint32_t revents) noexcept
628{
629 if (JOIN_UNLIKELY (fd == _wakeup))
630 {
631 readCommands ();
632 return;
633 }
634
635 if (JOIN_UNLIKELY (revents & (EPOLLERR | EPOLLRDHUP | EPOLLHUP)))
636 {
637 IoOperation* rOp = std::exchange (_readOps[fd], nullptr);
638 IoOperation* wOp = std::exchange (_writeOps[fd], nullptr);
639 if (JOIN_LIKELY (rOp || wOp))
640 {
641 _reactor.delHandler (fd);
642 }
643
644 int result = (revents & EPOLLERR) ? -ECONNRESET : 0;
645
646 if (rOp != nullptr)
647 {
648 drainQueue (*rOp, result, revents, false);
649 }
650 if (wOp != nullptr)
651 {
652 drainQueue (*wOp, result, revents, false);
653 }
654
655 return;
656 }
657
658 if (revents & EPOLLIN)
659 {
660 processQueue (fd, false, revents);
661 }
662
663 if (revents & EPOLLOUT)
664 {
665 processQueue (fd, true, revents);
666 }
667}
668
669// =========================================================================
670// CLASS : BasicProactor
671// METHOD : drainQueue
672// =========================================================================
673inline void join::BasicProactor::drainQueue (IoOperation& tail, int result, uint32_t revents, bool cancelled) noexcept
674{
675 IoOperation* op = tail.next;
676 tail.next = nullptr;
677
678 while (op != nullptr)
679 {
680 IoOperation* next = op->next;
681 op->next = nullptr;
682
683 int res = result;
684
685 if ((revents != 0) && (op->code == static_cast<uint8_t> (IoOperation::Opcode::Poll)))
686 {
687 res = executeOperation (*op, revents);
688 }
689
690 dispatchOperation (*op, res, cancelled);
691
692 op = next;
693 }
694}
695
696// =========================================================================
697// CLASS : BasicProactor
698// METHOD : processQueue
699// =========================================================================
700inline void join::BasicProactor::processQueue (int fd, bool write, uint32_t revents) noexcept
701{
702 IoOperation* last = write ? _writeOps[fd] : _readOps[fd];
703 if (last == nullptr)
704 {
705 return;
706 }
707
708 size_t count = 1;
709 for (IoOperation* it = last->next; it != last; it = it->next)
710 {
711 ++count;
712 }
713
714 IoOperation* op = last->next;
715 bool consumed = false;
716
717 for (size_t i = 0; (op != nullptr) && (i < count); ++i)
718 {
719 IoOperation* following = (op == last) ? nullptr : op->next;
720 bool poll = (op->code == static_cast<uint8_t> (IoOperation::Opcode::Poll));
721
722 if (poll || !consumed)
723 {
724 consumed = consumed || !poll;
725 processOperation (*op, revents);
726 }
727
728 last = write ? _writeOps[fd] : _readOps[fd];
729 op = nullptr;
730
731 if ((following != nullptr) && (last != nullptr))
732 {
733 for (IoOperation* it = last->next;; it = it->next)
734 {
735 if (it == following)
736 {
737 op = following;
738 break;
739 }
740
741 if (it == last)
742 {
743 break;
744 }
745 }
746 }
747 }
748}
749
750// =========================================================================
751// CLASS : BasicProactor
752// METHOD : processOperation
753// =========================================================================
754inline void join::BasicProactor::processOperation (IoOperation& op, uint32_t revents) noexcept
755{
756 IoOperation::State current = op.state.load (std::memory_order_acquire);
757 Backoff backoff;
758
760 {
761 backoff ();
762 current = op.state.load (std::memory_order_acquire);
763 }
764
766 {
767 return; // LCOV_EXCL_LINE
768 }
769
770 IoRingBuffer* br = nullptr;
771 uint16_t bid = 0;
772
773 if (op.ring != nullptr)
774 {
775 br = op.ring;
776 bid = static_cast<uint16_t> (br->select ());
777
778 if (op.code == static_cast<uint8_t> (IoOperation::Opcode::RecvMsg))
779 {
780 uint32_t reserved = op.data.msg.namelen + op.data.msg.controllen;
781 if (JOIN_UNLIKELY (reserved >= br->size ()))
782 {
783 endOperation (op, -EFAULT, false);
784 br->recycle (bid);
785 return;
786 }
787
788 char* base = static_cast<char*> (br->get (bid));
789
790 op.data.msg.msg->msg_name = base;
791 op.data.msg.msg->msg_namelen = op.data.msg.namelen;
792 op.data.msg.msg->msg_control = base + op.data.msg.namelen;
793 op.data.msg.msg->msg_controllen = op.data.msg.controllen;
794 op.data.msg.msg->msg_iov->iov_base = base + reserved;
795 op.data.msg.msg->msg_iov->iov_len = br->size () - reserved;
796 op.data.msg.msg->msg_iovlen = 1;
797 op.data.msg.msg->msg_flags = 0;
798 }
799 else
800 {
801 op.data.stream.buf = br->get (bid);
802 op.data.stream.len = br->size ();
803 }
804 }
805
806 int result = executeOperation (op, revents);
807
808 if (JOIN_UNLIKELY ((result == -EAGAIN) && (op.code != static_cast<uint8_t> (IoOperation::Opcode::Connect))))
809 {
810 if (br != nullptr)
811 {
812 br->recycle (bid);
813 }
814 return;
815 }
816
817 if ((br != nullptr) && (result >= 0) && (op.code == static_cast<uint8_t> (IoOperation::Opcode::RecvMsg)))
818 {
819 op.data.msg.msg->msg_iov->iov_len = static_cast<size_t> (result);
820
821 if (op.data.msg.msg->msg_controllen < sizeof (cmsghdr))
822 {
823 op.data.msg.msg->msg_control = nullptr;
824 }
825 }
826
827 if (op.multishot &&
828 ((result > 0) || ((result == 0) && (op.code == static_cast<uint8_t> (IoOperation::Opcode::Accept)))))
829 {
830 op.more = true;
831 notifyOperation (op, result, false);
832 }
833 else
834 {
835 endOperation (op, result, false);
836 }
837
838 if (br != nullptr)
839 {
840 br->recycle (bid);
841 }
842}
843
844// =========================================================================
845// CLASS : BasicProactor
846// METHOD : executeOperation
847// =========================================================================
848inline int join::BasicProactor::executeOperation (IoOperation& op, uint32_t revents) noexcept
849{
850 for (;;)
851 {
852 switch (static_cast<IoOperation::Opcode> (op.code))
853 {
855 return static_cast<int> (revents & (op.data.poll.events | POLLERR | POLLHUP | POLLRDHUP));
856
858 {
859 int fd = ::accept4 (op.data.accept.fd, op.data.accept.addr, op.data.accept.addrlen,
860 op.data.accept.flags);
861 if (JOIN_UNLIKELY ((fd == -1) && (errno == EINTR)))
862 {
863 continue; // LCOV_EXCL_LINE
864 }
865 return (fd == -1) ? -errno : fd;
866 }
867
869 {
870 int err = 0;
871 socklen_t len = sizeof (err);
872 if (JOIN_UNLIKELY (::getsockopt (op.data.connect.fd, SOL_SOCKET, SO_ERROR, &err, &len) == -1))
873 {
874 return -errno;
875 }
876 return JOIN_UNLIKELY (err) ? -err : 0;
877 }
878
881 {
882 ssize_t n = ::read (op.data.rw.fd, op.data.rw.buf, op.data.rw.len);
883 if (JOIN_UNLIKELY ((n == -1) && (errno == EINTR)))
884 {
885 continue; // LCOV_EXCL_LINE
886 }
887 return (n == -1) ? -errno : static_cast<int> (n);
888 }
889
892 {
893 ssize_t n = ::write (op.data.rw.fd, op.data.rw.buf, op.data.rw.len);
894 if (JOIN_UNLIKELY ((n == -1) && (errno == EINTR)))
895 {
896 continue; // LCOV_EXCL_LINE
897 }
898 return (n == -1) ? -errno : static_cast<int> (n);
899 }
900
902 {
903 ssize_t n = ::recvmsg (op.data.msg.fd, op.data.msg.msg, op.data.msg.flags);
904 if (JOIN_UNLIKELY ((n == -1) && (errno == EINTR)))
905 {
906 continue; // LCOV_EXCL_LINE
907 }
908 return (n == -1) ? -errno : static_cast<int> (n);
909 }
910
912 {
913 ssize_t n = ::sendmsg (op.data.msg.fd, op.data.msg.msg, op.data.msg.flags);
914 if (JOIN_UNLIKELY ((n == -1) && (errno == EINTR)))
915 {
916 continue; // LCOV_EXCL_LINE
917 }
918 return (n == -1) ? -errno : static_cast<int> (n);
919 }
920
922 {
923 ssize_t n =
924 ::recv (op.data.stream.fd, op.data.stream.buf, op.data.stream.len, op.data.stream.flags);
925 if (JOIN_UNLIKELY ((n == -1) && (errno == EINTR)))
926 {
927 continue; // LCOV_EXCL_LINE
928 }
929 return (n == -1) ? -errno : static_cast<int> (n);
930 }
931
933 {
934 ssize_t n =
935 ::send (op.data.stream.fd, op.data.stream.buf, op.data.stream.len, op.data.stream.flags);
936 if (JOIN_UNLIKELY ((n == -1) && (errno == EINTR)))
937 {
938 continue; // LCOV_EXCL_LINE
939 }
940 return (n == -1) ? -errno : static_cast<int> (n);
941 }
942
943 default:
944 return -EINVAL;
945 }
946 }
947}
948
949// =========================================================================
950// CLASS : BasicProactor
951// METHOD : unqueueOperation
952// =========================================================================
953inline bool join::BasicProactor::unqueueOperation (IoOperation& op) noexcept
954{
955 if (op.next == nullptr)
956 {
957 return false;
958 }
959
960 IoOperation*& last = isWriteOperation (op) ? _writeOps[op.fd ()] : _readOps[op.fd ()];
961 IoOperation* prev = last;
962
963 while (prev->next != &op)
964 {
965 prev = prev->next;
966 }
967
968 if (prev == &op)
969 {
970 last = nullptr;
971 }
972 else
973 {
974 prev->next = op.next;
975
976 if (last == &op)
977 {
978 last = prev;
979 }
980 }
981
982 op.next = nullptr;
983
984 return true;
985}
986
987// =========================================================================
988// CLASS : BasicProactor
989// METHOD : isWriteOperation
990// =========================================================================
991inline bool join::BasicProactor::isWriteOperation (const IoOperation& op) noexcept
992{
993 switch (static_cast<IoOperation::Opcode> (op.code))
994 {
996 return (op.data.poll.events & POLLIN) == 0;
1002 return true;
1003 default:
1004 return false;
1005 }
1006}
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
slab allocator owning a backend and managing one or more pools.
Definition slab_allocator.hpp:339
timing wheel.
Definition wheel.hpp:58
fixed-capacity move-only allocation-free alternative to std::function.
Definition function.hpp:44
int addHandler(int fd, EventHandler *handler, bool wantRead=true, bool wantWrite=false, bool sync=true) noexcept
add handler to reactor.
Definition reactor.cpp:101
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
thread_local std::error_code lastError
last error.
Definition error.cpp:32
State
operation lifecycle state.
Definition io_operation.hpp:53
Opcode
operation code.
Definition io_operation.hpp:64
#define JOIN_LIKELY(x)
Definition utils.hpp:50
#define JOIN_UNLIKELY(x)
Definition utils.hpp:51