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 : unregisterBufferRing
153// =========================================================================
155{
156 int result = 0;
157 std::error_code errc;
158
159 InvokeHandler fn = [this, group, &result, &errc] () {
160 auto it = _bufferRings.find (group);
161 if (JOIN_UNLIKELY (it == _bufferRings.end ()))
162 {
163 lastError = make_error_code (Errc::NotFound);
164 errc = lastError;
165 result = -1;
166 return;
167 }
168
169 if (JOIN_UNLIKELY (it->second.armed ()))
170 {
171 lastError = make_error_code (Errc::InUse);
172 errc = lastError;
173 result = -1;
174 return;
175 }
176
177 if (JOIN_UNLIKELY (it->second.unregisterBuffer () == -1))
178 {
179 // LCOV_EXCL_START
180 errc = lastError;
181 result = -1;
182 return;
183 // LCOV_EXCL_STOP
184 }
185
186 _bufferRings.erase (it);
187 };
188
189 if (JOIN_UNLIKELY (!isRunning ()))
190 {
192 return -1;
193 }
194
195 if (JOIN_UNLIKELY (invoke (&fn) == -1))
196 {
197 return -1; // LCOV_EXCL_LINE
198 }
199
200 if (JOIN_UNLIKELY (result == -1))
201 {
202 lastError = errc;
203 return -1;
204 }
205
206 return 0;
207}
208
209#ifdef JOIN_HAS_NUMA
210// =========================================================================
211// CLASS : BasicProactor
212// METHOD : mbind
213// =========================================================================
214inline int join::BasicProactor::mbind (int numa) const noexcept
215{
216 if (_commands.mbind (numa) == -1)
217 {
218 return -1;
219 }
220
221 return _reactor.mbind (numa);
222}
223#endif
224
225// =========================================================================
226// CLASS : BasicProactor
227// METHOD : mlock
228// =========================================================================
229inline int join::BasicProactor::mlock () const noexcept
230{
231 if (_commands.mlock () == -1)
232 {
233 return -1;
234 }
235
236 return _reactor.mlock ();
237}
238
239// =========================================================================
240// CLASS : BasicProactor
241// METHOD : isRunning
242// =========================================================================
243inline bool join::BasicProactor::isRunning () const noexcept
244{
245 return _reactor.isRunning ();
246}
247
248// =========================================================================
249// CLASS : BasicProactor
250// METHOD : isProactorThread
251// =========================================================================
252inline bool join::BasicProactor::isProactorThread () const noexcept
253{
254 return _reactor.isReactorThread ();
255}
256
257// =========================================================================
258// CLASS : BasicProactor
259// METHOD : writeCommand
260// =========================================================================
261inline int join::BasicProactor::writeCommand (const Command& cmd) noexcept
262{
263 if (JOIN_UNLIKELY (_commands.push (cmd) == -1))
264 {
265 return -1; // LCOV_EXCL_LINE
266 }
267
268 // pairs with the fence in the event loop: an acquire load would let the push above be
269 // reordered after it, the loop would sleep and the wakeup would be lost.
270 std::atomic_thread_fence (std::memory_order_seq_cst);
271
272 WakeupState state = _wakeupState.load (std::memory_order_relaxed);
273 if (state != WakeupState::Sleeping)
274 {
275 return 0;
276 }
277
278 if (_wakeupState.compare_exchange_strong (state, WakeupState::Waking, std::memory_order_seq_cst))
279 {
280 uint64_t value = 1;
281 if (JOIN_UNLIKELY (::write (_wakeup, &value, sizeof (uint64_t)) == -1))
282 {
283 _wakeupState.store (WakeupState::Sleeping, std::memory_order_seq_cst); // LCOV_EXCL_LINE
284 }
285 }
286
287 return 0;
288}
289
290// =========================================================================
291// CLASS : BasicProactor
292// METHOD : readCommands
293// =========================================================================
294inline void join::BasicProactor::readCommands () noexcept
295{
296 uint64_t count;
297 [[maybe_unused]] ssize_t nread = ::read (_wakeup, &count, sizeof (count));
298 _wakeupState.store (WakeupState::Sleeping, std::memory_order_relaxed);
299 std::atomic_thread_fence (std::memory_order_seq_cst);
300
301 Command cmd;
302 while (_commands.tryPop (cmd) == 0)
303 {
304 processCommand (cmd);
305 }
306}
307
308// =========================================================================
309// CLASS : BasicProactor
310// METHOD : processCommand
311// =========================================================================
312inline void join::BasicProactor::processCommand (const Command& cmd) noexcept
313{
314 int err = 0;
315
316 switch (cmd.type)
317 {
318 case CommandType::Submit:
319 err = submitOperation (*cmd.op, cmd.flush);
320 if (JOIN_UNLIKELY ((err != 0) && (cmd.done == nullptr) && (cmd.op->state == IoOperation::State::Idle)))
321 {
322 dispatchOperation (cmd.op, -lastError.default_error_condition ().value (), false);
323 }
324 break;
325
326 case CommandType::Cancel:
327 err = cancelOperation (*cmd.op, cmd.flush);
328 break;
329
330 case CommandType::Invoke:
331 err = invokeFunction (cmd.fn);
332 break;
333
334 case CommandType::Stop:
335 cancelAllOperations ();
336 _reactor.stop (false);
337 break;
338
339 default: // LCOV_EXCL_LINE
340 break; // LCOV_EXCL_LINE
341 }
342
343 if (JOIN_UNLIKELY (cmd.done))
344 {
345 if (cmd.errc && (err != 0))
346 {
347 *cmd.errc = lastError;
348 }
349 cmd.done->store (true, std::memory_order_release);
350 }
351}
352
353// =========================================================================
354// CLASS : BasicProactor
355// METHOD : submitOperation
356// =========================================================================
357inline int join::BasicProactor::submitOperation (IoOperation& op, [[maybe_unused]] bool flush) noexcept
358{
359 if (JOIN_UNLIKELY (op.fd () < 0))
360 {
361 lastError = std::make_error_code (std::errc::bad_file_descriptor);
362 return -1;
363 }
364
365 Backoff backoff;
366 while (JOIN_UNLIKELY (op.state.load (std::memory_order_acquire) == IoOperation::State::Suspended))
367 {
368 backoff ();
369 }
370
372 if (!op.state.compare_exchange_strong (expected, IoOperation::State::Submitted, std::memory_order_acquire,
373 std::memory_order_relaxed) &&
374 (expected == IoOperation::State::Busy))
375 {
376 op.state.store (IoOperation::State::Submitted, std::memory_order_release);
377 }
378
379 IoRingBuffer* ring = nullptr;
380
381 if (JOIN_UNLIKELY (op.multishot && ((op.code == static_cast<uint8_t> (IoOperation::Opcode::RecvMsg)) ||
382 (op.code == static_cast<uint8_t> (IoOperation::Opcode::Recv)))))
383 {
384 auto it = _bufferRings.find (op.group);
385 if (JOIN_UNLIKELY (it == _bufferRings.end ()))
386 {
387 resetOperation (op);
388 lastError = make_error_code (Errc::NotFound);
389 return -1;
390 }
391
392 ring = &it->second;
393 }
394
395 if (JOIN_UNLIKELY (static_cast<IoOperation::Opcode> (op.code) == IoOperation::Opcode::Connect))
396 {
397 if (JOIN_UNLIKELY (::connect (op.data.connect.fd, op.data.connect.addr, op.data.connect.addrlen) == -1 &&
398 errno != EINPROGRESS))
399 {
400 lastError = std::error_code (errno, std::system_category ());
401 resetOperation (op);
402 return -1;
403 }
404 }
405
406 if (JOIN_UNLIKELY (static_cast<size_t> (op.fd ()) >= _readOps.size ()))
407 {
408 size_t newSize = static_cast<size_t> (op.fd ()) + 1;
409 _readOps.resize (newSize, nullptr);
410 _writeOps.resize (newSize, nullptr);
411 }
412
413 bool isWrite = isWriteOp (op);
414
415 if (JOIN_UNLIKELY ((isWrite && (_writeOps[op.fd ()] == &op)) || (!isWrite && (_readOps[op.fd ()] == &op))))
416 {
417 lastError = make_error_code (std::errc::device_or_resource_busy);
418 return -1;
419 }
420
421 if (JOIN_UNLIKELY ((isWrite && (_writeOps[op.fd ()] != nullptr)) || (!isWrite && (_readOps[op.fd ()] != nullptr))))
422 {
423 resetOperation (op);
425 return -1;
426 }
427
428 if (isWrite)
429 {
430 _writeOps[op.fd ()] = &op;
431 }
432 else
433 {
434 _readOps[op.fd ()] = &op;
435 }
436
437 if (JOIN_UNLIKELY (ring != nullptr))
438 {
439 op.ring = ring;
440 ring->bind ();
441 }
442
443 int err = _reactor.addHandler (op.fd (), this, _readOps[op.fd ()] != nullptr, _writeOps[op.fd ()] != nullptr);
444 if (JOIN_UNLIKELY (err == -1))
445 {
446 if (isWrite)
447 {
448 _writeOps[op.fd ()] = nullptr;
449 }
450 else
451 {
452 _readOps[op.fd ()] = nullptr;
453 }
454
455 if (JOIN_UNLIKELY (ring != nullptr))
456 {
457 op.ring->unbind ();
458 op.ring = nullptr;
459 }
460
461 resetOperation (op);
462 }
463
464 return err;
465}
466
467// =========================================================================
468// CLASS : BasicProactor
469// METHOD : cancelOperation
470// =========================================================================
471inline int join::BasicProactor::cancelOperation (IoOperation& op, [[maybe_unused]] bool flush) noexcept
472{
473 if (JOIN_UNLIKELY (op.fd () < 0))
474 {
475 lastError = std::make_error_code (std::errc::bad_file_descriptor);
476 return -1;
477 }
478
480 {
482 return -1;
483 }
484
485 if (JOIN_UNLIKELY (static_cast<size_t> (op.fd ()) >= _readOps.size ()))
486 {
487 lastError = std::make_error_code (std::errc::bad_file_descriptor);
488 return -1;
489 }
490
491 bool isWrite = isWriteOp (op);
492
493 if (JOIN_UNLIKELY ((isWrite && (_writeOps[op.fd ()] != &op)) || (!isWrite && (_readOps[op.fd ()] != &op))))
494 {
496 return -1;
497 }
498
499 if (isWrite)
500 {
501 _writeOps[op.fd ()] = nullptr;
502 }
503 else
504 {
505 _readOps[op.fd ()] = nullptr;
506 }
507
508 int ret = 0;
509
510 if (_readOps[op.fd ()] == nullptr && _writeOps[op.fd ()] == nullptr)
511 {
512 ret = _reactor.delHandler (op.fd ());
513 }
514 else
515 {
516 ret = _reactor.addHandler (op.fd (), this, _readOps[op.fd ()] != nullptr, _writeOps[op.fd ()] != nullptr);
517 }
518
519 dispatchOperation (&op, -ECANCELED, true);
520
521 return ret;
522}
523
524// =========================================================================
525// CLASS : BasicProactor
526// METHOD : cancelAllOperations
527// =========================================================================
528inline void join::BasicProactor::cancelAllOperations () noexcept
529{
530 for (size_t fd = 0; fd < _readOps.size (); ++fd)
531 {
532 IoOperation* rOp = std::exchange (_readOps[fd], nullptr);
533 IoOperation* wOp = std::exchange (_writeOps[fd], nullptr);
534 if (rOp || wOp)
535 {
536 _reactor.delHandler (fd);
537 }
538 dispatchOperation (rOp, -ECANCELED, true);
539 dispatchOperation (wOp, -ECANCELED, true);
540 }
541}
542
543// =========================================================================
544// CLASS : BasicProactor
545// METHOD : endOperation
546// =========================================================================
547inline void join::BasicProactor::endOperation (IoOperation& op, int result, bool cancelled) noexcept
548{
549 int fd = op.fd ();
550
551 if (JOIN_UNLIKELY (fd < 0 || static_cast<size_t> (fd) >= _readOps.size ()))
552 {
553 return; // LCOV_EXCL_LINE
554 }
555
556 if (isWriteOp (op))
557 {
558 _writeOps[fd] = nullptr;
559 }
560 else
561 {
562 _readOps[fd] = nullptr;
563 }
564
565 if (_readOps[fd] == nullptr && _writeOps[fd] == nullptr)
566 {
567 _reactor.delHandler (fd);
568 }
569 else
570 {
571 _reactor.addHandler (fd, this, _readOps[fd] != nullptr, _writeOps[fd] != nullptr);
572 }
573
574 dispatchOperation (&op, result, cancelled);
575}
576
577// =========================================================================
578// CLASS : BasicProactor
579// METHOD : isWriteOp
580// =========================================================================
581inline bool join::BasicProactor::isWriteOp (const IoOperation& op) noexcept
582{
583 switch (static_cast<IoOperation::Opcode> (op.code))
584 {
586 return (op.data.poll.events & POLLIN) == 0;
592 return true;
593 default:
594 return false;
595 }
596}
597
598// =========================================================================
599// CLASS : BasicProactor
600// METHOD : executeOp
601// =========================================================================
602inline int join::BasicProactor::executeOp (IoOperation& op, uint32_t revents) noexcept
603{
604 for (;;)
605 {
606 switch (static_cast<IoOperation::Opcode> (op.code))
607 {
609 return static_cast<int> (revents & (op.data.poll.events | POLLERR | POLLHUP | POLLRDHUP));
610
612 {
613 int fd = ::accept4 (op.data.accept.fd, op.data.accept.addr, op.data.accept.addrlen,
614 op.data.accept.flags);
615 if (JOIN_UNLIKELY ((fd == -1) && (errno == EINTR)))
616 {
617 continue; // LCOV_EXCL_LINE
618 }
619 return (fd == -1) ? -errno : fd;
620 }
621
623 {
624 int err = 0;
625 socklen_t len = sizeof (err);
626 if (JOIN_UNLIKELY (::getsockopt (op.data.connect.fd, SOL_SOCKET, SO_ERROR, &err, &len) == -1))
627 {
628 return -errno;
629 }
630 return JOIN_UNLIKELY (err) ? -err : 0;
631 }
632
635 {
636 ssize_t n = ::read (op.data.rw.fd, op.data.rw.buf, op.data.rw.len);
637 if (JOIN_UNLIKELY ((n == -1) && (errno == EINTR)))
638 {
639 continue; // LCOV_EXCL_LINE
640 }
641 return (n == -1) ? -errno : static_cast<int> (n);
642 }
643
646 {
647 ssize_t n = ::write (op.data.rw.fd, op.data.rw.buf, op.data.rw.len);
648 if (JOIN_UNLIKELY ((n == -1) && (errno == EINTR)))
649 {
650 continue; // LCOV_EXCL_LINE
651 }
652 return (n == -1) ? -errno : static_cast<int> (n);
653 }
654
656 {
657 ssize_t n = ::recvmsg (op.data.msg.fd, op.data.msg.msg, op.data.msg.flags);
658 if (JOIN_UNLIKELY ((n == -1) && (errno == EINTR)))
659 {
660 continue; // LCOV_EXCL_LINE
661 }
662 return (n == -1) ? -errno : static_cast<int> (n);
663 }
664
666 {
667 ssize_t n = ::sendmsg (op.data.msg.fd, op.data.msg.msg, op.data.msg.flags);
668 if (JOIN_UNLIKELY ((n == -1) && (errno == EINTR)))
669 {
670 continue; // LCOV_EXCL_LINE
671 }
672 return (n == -1) ? -errno : static_cast<int> (n);
673 }
674
676 {
677 ssize_t n =
678 ::recv (op.data.stream.fd, op.data.stream.buf, op.data.stream.len, op.data.stream.flags);
679 if (JOIN_UNLIKELY ((n == -1) && (errno == EINTR)))
680 {
681 continue; // LCOV_EXCL_LINE
682 }
683 return (n == -1) ? -errno : static_cast<int> (n);
684 }
685
687 {
688 ssize_t n =
689 ::send (op.data.stream.fd, op.data.stream.buf, op.data.stream.len, op.data.stream.flags);
690 if (JOIN_UNLIKELY ((n == -1) && (errno == EINTR)))
691 {
692 continue; // LCOV_EXCL_LINE
693 }
694 return (n == -1) ? -errno : static_cast<int> (n);
695 }
696
697 default:
698 return -EINVAL;
699 }
700 }
701}
702
703// =========================================================================
704// CLASS : BasicProactor
705// METHOD : onEvent
706// =========================================================================
707inline void join::BasicProactor::onEvent (int fd, uint32_t revents) noexcept
708{
709 if (JOIN_UNLIKELY (fd == _wakeup))
710 {
711 readCommands ();
712 return;
713 }
714
715 if (JOIN_UNLIKELY (revents & (EPOLLERR | EPOLLRDHUP | EPOLLHUP)))
716 {
717 IoOperation* rOp = std::exchange (_readOps[fd], nullptr);
718 IoOperation* wOp = std::exchange (_writeOps[fd], nullptr);
719 if (JOIN_LIKELY (rOp || wOp))
720 {
721 _reactor.delHandler (fd);
722 }
723
724 int result = (revents & EPOLLERR) ? -ECONNRESET : 0;
725 int rResult = result;
726 int wResult = result;
727
728 if ((rOp != nullptr) && (rOp->code == static_cast<uint8_t> (IoOperation::Opcode::Poll)))
729 {
730 rResult = executeOp (*rOp, revents);
731 }
732
733 if ((wOp != nullptr) && (wOp->code == static_cast<uint8_t> (IoOperation::Opcode::Poll)))
734 {
735 wResult = executeOp (*wOp, revents);
736 }
737
738 dispatchOperation (rOp, rResult, false);
739 dispatchOperation (wOp, wResult, false);
740
741 return;
742 }
743
744 IoOperation* op = (revents & EPOLLIN) ? _readOps[fd] : _writeOps[fd];
745 if (JOIN_UNLIKELY (op == nullptr))
746 {
747 return;
748 }
749
750 IoOperation::State current = op->state.load (std::memory_order_acquire);
751 Backoff backoff;
752
754 {
755 backoff ();
756 current = op->state.load (std::memory_order_acquire);
757 }
758
760 {
761 return; // LCOV_EXCL_LINE
762 }
763
764 IoRingBuffer* br = nullptr;
765 uint16_t bid = 0;
766
767 if (op->ring != nullptr)
768 {
769 int selected = op->ring->select ();
770 if (JOIN_UNLIKELY (selected == -1))
771 {
772 endOperation (*op, -ENOBUFS, false);
773 return;
774 }
775
776 br = op->ring;
777 bid = static_cast<uint16_t> (selected);
778
779 if (op->code == static_cast<uint8_t> (IoOperation::Opcode::RecvMsg))
780 {
781 uint32_t reserved = op->data.msg.namelen + op->data.msg.controllen;
782 if (JOIN_UNLIKELY (reserved >= br->size ()))
783 {
784 endOperation (*op, -EFAULT, false);
785 br->recycle (bid);
786 return;
787 }
788
789 char* base = static_cast<char*> (br->get (bid));
790
791 op->data.msg.msg->msg_name = base;
792 op->data.msg.msg->msg_namelen = op->data.msg.namelen;
793 op->data.msg.msg->msg_control = base + op->data.msg.namelen;
794 op->data.msg.msg->msg_controllen = op->data.msg.controllen;
795 op->data.msg.msg->msg_iov->iov_base = base + reserved;
796 op->data.msg.msg->msg_iov->iov_len = br->size () - reserved;
797 op->data.msg.msg->msg_iovlen = 1;
798 op->data.msg.msg->msg_flags = 0;
799 }
800 else
801 {
802 op->data.stream.buf = br->get (bid);
803 op->data.stream.len = br->size ();
804 }
805 }
806
807 int result = executeOp (*op, revents);
808
809 if (JOIN_UNLIKELY ((result == -EAGAIN) && (op->code != static_cast<uint8_t> (IoOperation::Opcode::Connect))))
810 {
811 if (br != nullptr)
812 {
813 br->recycle (bid);
814 }
815 return;
816 }
817
818 if ((br != nullptr) && (result >= 0) && (op->code == static_cast<uint8_t> (IoOperation::Opcode::RecvMsg)))
819 {
820 op->data.msg.msg->msg_iov->iov_len = static_cast<size_t> (result);
821
822 if (op->data.msg.msg->msg_controllen < sizeof (cmsghdr))
823 {
824 op->data.msg.msg->msg_control = nullptr;
825 }
826 }
827
828 if (op->multishot &&
829 ((result > 0) || ((result == 0) && (op->code == static_cast<uint8_t> (IoOperation::Opcode::Accept)))))
830 {
831 op->more = true;
832 notifyOperation (*op, result, false);
833 }
834 else
835 {
836 endOperation (*op, result, false);
837 }
838
839 if (br != nullptr)
840 {
841 br->recycle (bid);
842 }
843}
memory arena owning backend and managing one or more pools.
Definition memory.hpp:53
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
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:100
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:52
Opcode
operation code.
Definition io_operation.hpp:63
#define JOIN_LIKELY(x)
Definition utils.hpp:45
#define JOIN_UNLIKELY(x)
Definition utils.hpp:46