33: _commands (_queueSize)
34, _wakeup (eventfd (0, EFD_NONBLOCK | EFD_CLOEXEC))
35, _readOps (256, nullptr)
36, _writeOps (256, nullptr)
40 throw std::system_error (errno, std::system_category (),
"eventfd failed");
43 _reactor.
addHandler (_wakeup,
this,
true,
false,
false);
63 _wakeupState.store (WakeupState::Sleeping, std::memory_order_seq_cst);
73 if (isProactorThread ())
75 cancelAllOperations ();
76 _reactor.stop (
false);
85 writeCommand ({CommandType::Stop,
nullptr, sync,
nullptr,
nullptr,
nullptr});
99 _reactor.waitStopped ();
106template <
size_t Count,
size_t Size>
110 std::error_code errc;
112 InvokeHandler fn = [
this, group, &arena, &result, &errc] () {
121 if (
JOIN_UNLIKELY (_bufferRings[group].registerBuffer (group, arena) == -1))
124 _bufferRings.erase (group);
157 std::error_code errc;
160 auto it = _bufferRings.find (group);
186 _bufferRings.erase (it);
214inline int join::BasicProactor::mbind (
int numa)
const noexcept
216 if (_commands.mbind (numa) == -1)
221 return _reactor.mbind (numa);
231 if (_commands.mlock () == -1)
236 return _reactor.mlock ();
245 return _reactor.isRunning ();
254 return _reactor.isReactorThread ();
261inline int join::BasicProactor::writeCommand (
const Command& cmd)
noexcept
270 std::atomic_thread_fence (std::memory_order_seq_cst);
272 WakeupState state = _wakeupState.load (std::memory_order_relaxed);
273 if (state != WakeupState::Sleeping)
278 if (_wakeupState.compare_exchange_strong (state, WakeupState::Waking, std::memory_order_seq_cst))
281 if (
JOIN_UNLIKELY (::write (_wakeup, &value,
sizeof (uint64_t)) == -1))
283 _wakeupState.store (WakeupState::Sleeping, std::memory_order_seq_cst);
294inline void join::BasicProactor::readCommands () noexcept
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);
302 while (_commands.tryPop (cmd) == 0)
304 processCommand (cmd);
312inline void join::BasicProactor::processCommand (
const Command& cmd)
noexcept
318 case CommandType::Submit:
319 err = submitOperation (*cmd.op, cmd.flush);
322 dispatchOperation (cmd.op, -lastError.default_error_condition ().value (),
false);
326 case CommandType::Cancel:
327 err = cancelOperation (*cmd.op, cmd.flush);
330 case CommandType::Invoke:
331 err = invokeFunction (cmd.fn);
334 case CommandType::Stop:
335 cancelAllOperations ();
336 _reactor.stop (
false);
345 if (cmd.errc && (err != 0))
349 cmd.done->store (
true, std::memory_order_release);
357inline int join::BasicProactor::submitOperation (IoOperation& op, [[maybe_unused]]
bool flush)
noexcept
361 lastError = std::make_error_code (std::errc::bad_file_descriptor);
373 std::memory_order_relaxed) &&
379 IoRingBuffer* ring =
nullptr;
384 auto it = _bufferRings.find (op.group);
397 if (
JOIN_UNLIKELY (::connect (op.data.connect.fd, op.data.connect.addr, op.data.connect.addrlen) == -1 &&
398 errno != EINPROGRESS))
400 lastError = std::error_code (errno, std::system_category ());
406 if (
JOIN_UNLIKELY (
static_cast<size_t> (op.fd ()) >= _readOps.size ()))
408 size_t newSize =
static_cast<size_t> (op.fd ()) + 1;
409 _readOps.resize (newSize,
nullptr);
410 _writeOps.resize (newSize,
nullptr);
413 bool isWrite = isWriteOp (op);
415 if (
JOIN_UNLIKELY ((isWrite && (_writeOps[op.fd ()] == &op)) || (!isWrite && (_readOps[op.fd ()] == &op))))
421 if (
JOIN_UNLIKELY ((isWrite && (_writeOps[op.fd ()] !=
nullptr)) || (!isWrite && (_readOps[op.fd ()] !=
nullptr))))
430 _writeOps[op.fd ()] = &op;
434 _readOps[op.fd ()] = &op;
443 int err = _reactor.addHandler (op.fd (),
this, _readOps[op.fd ()] !=
nullptr, _writeOps[op.fd ()] !=
nullptr);
448 _writeOps[op.fd ()] =
nullptr;
452 _readOps[op.fd ()] =
nullptr;
471inline int join::BasicProactor::cancelOperation (IoOperation& op, [[maybe_unused]]
bool flush)
noexcept
475 lastError = std::make_error_code (std::errc::bad_file_descriptor);
485 if (
JOIN_UNLIKELY (
static_cast<size_t> (op.fd ()) >= _readOps.size ()))
487 lastError = std::make_error_code (std::errc::bad_file_descriptor);
491 bool isWrite = isWriteOp (op);
493 if (
JOIN_UNLIKELY ((isWrite && (_writeOps[op.fd ()] != &op)) || (!isWrite && (_readOps[op.fd ()] != &op))))
501 _writeOps[op.fd ()] =
nullptr;
505 _readOps[op.fd ()] =
nullptr;
510 if (_readOps[op.fd ()] ==
nullptr && _writeOps[op.fd ()] ==
nullptr)
512 ret = _reactor.delHandler (op.fd ());
516 ret = _reactor.addHandler (op.fd (),
this, _readOps[op.fd ()] !=
nullptr, _writeOps[op.fd ()] !=
nullptr);
519 dispatchOperation (&op, -ECANCELED,
true);
528inline void join::BasicProactor::cancelAllOperations () noexcept
530 for (
size_t fd = 0; fd < _readOps.size (); ++fd)
532 IoOperation* rOp = std::exchange (_readOps[fd],
nullptr);
533 IoOperation* wOp = std::exchange (_writeOps[fd],
nullptr);
536 _reactor.delHandler (fd);
538 dispatchOperation (rOp, -ECANCELED,
true);
539 dispatchOperation (wOp, -ECANCELED,
true);
547inline void join::BasicProactor::endOperation (IoOperation& op,
int result,
bool cancelled)
noexcept
551 if (
JOIN_UNLIKELY (fd < 0 ||
static_cast<size_t> (fd) >= _readOps.size ()))
558 _writeOps[fd] =
nullptr;
562 _readOps[fd] =
nullptr;
565 if (_readOps[fd] ==
nullptr && _writeOps[fd] ==
nullptr)
567 _reactor.delHandler (fd);
571 _reactor.addHandler (fd,
this, _readOps[fd] !=
nullptr, _writeOps[fd] !=
nullptr);
574 dispatchOperation (&op, result, cancelled);
581inline bool join::BasicProactor::isWriteOp (
const IoOperation& op)
noexcept
586 return (op.data.poll.events & POLLIN) == 0;
602inline int join::BasicProactor::executeOp (IoOperation& op, uint32_t revents)
noexcept
609 return static_cast<int> (revents & (op.data.poll.events | POLLERR | POLLHUP | POLLRDHUP));
613 int fd = ::accept4 (op.data.accept.fd, op.data.accept.addr, op.data.accept.addrlen,
614 op.data.accept.flags);
619 return (fd == -1) ? -errno : fd;
625 socklen_t len =
sizeof (err);
626 if (
JOIN_UNLIKELY (::getsockopt (op.data.connect.fd, SOL_SOCKET, SO_ERROR, &err, &len) == -1))
636 ssize_t n = ::read (op.data.rw.fd, op.data.rw.buf, op.data.rw.len);
641 return (n == -1) ? -errno :
static_cast<int> (n);
647 ssize_t n = ::write (op.data.rw.fd, op.data.rw.buf, op.data.rw.len);
652 return (n == -1) ? -errno :
static_cast<int> (n);
657 ssize_t n = ::recvmsg (op.data.msg.fd, op.data.msg.msg, op.data.msg.flags);
662 return (n == -1) ? -errno :
static_cast<int> (n);
667 ssize_t n = ::sendmsg (op.data.msg.fd, op.data.msg.msg, op.data.msg.flags);
672 return (n == -1) ? -errno :
static_cast<int> (n);
678 ::recv (op.data.stream.fd, op.data.stream.buf, op.data.stream.len, op.data.stream.flags);
683 return (n == -1) ? -errno :
static_cast<int> (n);
689 ::send (op.data.stream.fd, op.data.stream.buf, op.data.stream.len, op.data.stream.flags);
694 return (n == -1) ? -errno :
static_cast<int> (n);
707inline void join::BasicProactor::onEvent (
int fd, uint32_t revents)
noexcept
715 if (
JOIN_UNLIKELY (revents & (EPOLLERR | EPOLLRDHUP | EPOLLHUP)))
717 IoOperation* rOp = std::exchange (_readOps[fd],
nullptr);
718 IoOperation* wOp = std::exchange (_writeOps[fd],
nullptr);
721 _reactor.delHandler (fd);
724 int result = (revents & EPOLLERR) ? -ECONNRESET : 0;
725 int rResult = result;
726 int wResult = result;
730 rResult = executeOp (*rOp, revents);
735 wResult = executeOp (*wOp, revents);
738 dispatchOperation (rOp, rResult,
false);
739 dispatchOperation (wOp, wResult,
false);
744 IoOperation* op = (revents & EPOLLIN) ? _readOps[fd] : _writeOps[fd];
756 current = op->state.load (std::memory_order_acquire);
764 IoRingBuffer* br =
nullptr;
767 if (op->ring !=
nullptr)
769 int selected = op->ring->select ();
772 endOperation (*op, -ENOBUFS,
false);
777 bid =
static_cast<uint16_t
> (selected);
781 uint32_t reserved = op->data.msg.namelen + op->data.msg.controllen;
784 endOperation (*op, -EFAULT,
false);
789 char*
base =
static_cast<char*
> (br->get (bid));
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;
802 op->data.stream.buf = br->get (bid);
803 op->data.stream.len = br->size ();
807 int result = executeOp (*op, revents);
820 op->data.msg.msg->msg_iov->iov_len =
static_cast<size_t> (result);
822 if (op->data.msg.msg->msg_controllen < sizeof (cmsghdr))
824 op->data.msg.msg->msg_control =
nullptr;
832 notifyOperation (*op, result,
false);
836 endOperation (*op, result,
false);
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