25#ifndef JOIN_CORE_PROACTOR_HPP
26#define JOIN_CORE_PROACTOR_HPP
32#ifdef JOIN_HAS_IO_URING
43#include <unordered_map>
48#include <sys/eventfd.h>
53 class CompletionHandler;
54#ifdef JOIN_HAS_IO_URING
55 template <
typename Policy>
57 template <
typename Policy>
58 class BasicProactorThread;
60 using Proactor = BasicProactor<IoDefaultPolicy>;
61 using HybridProactor = BasicProactor<IoHybridPolicy>;
62 using SqpollProactor = BasicProactor<IoSqpollPolicy>;
64 using HybridProactorThread = BasicProactorThread<IoHybridPolicy>;
65 using SqpollProactorThread = BasicProactorThread<IoSqpollPolicy>;
68 class BasicProactorThread;
81#ifdef JOIN_HAS_IO_URING
82 template <
typename Policy>
150#ifdef JOIN_HAS_IO_URING
151template <
typename Policy = join::IoDefaultPolicy>
223#ifdef JOIN_HAS_IO_URING
229 int flush (
bool sync =
false) noexcept;
254 void stop (
bool sync =
true) noexcept;
261#ifdef JOIN_HAS_IO_URING
267 template <
size_t Count,
size_t... Sizes>
274 int unregisterFixedBuffers () noexcept;
284 template <
size_t Count,
size_t Size>
300 int mbind (
int numa)
const noexcept;
307 int mlock () const noexcept;
322#ifdef JOIN_HAS_IO_URING
327 int initWakeup (std::true_type)
noexcept;
333 int initWakeup (std::false_type)
noexcept;
338 void initWakeupOp (std::true_type)
noexcept;
343 void initWakeupOp (std::false_type)
noexcept;
349 void initCqEntries (io_uring_params&, std::false_type)
noexcept;
355 void initCqEntries (io_uring_params& params, std::true_type)
noexcept;
361 void initSqThreadIdle (io_uring_params&, std::false_type)
noexcept;
367 void initSqThreadIdle (io_uring_params& params, std::true_type)
noexcept;
373 void initSqThreadCpu (io_uring_params&, std::false_type)
noexcept;
379 void initSqThreadCpu (io_uring_params& params, std::true_type)
noexcept;
385 enum class WakeupState
395 enum class CommandType
401#ifdef JOIN_HAS_IO_URING
409 struct alignas (64) Command
414 std::atomic<bool>* done;
415 std::error_code* errc;
424 int writeCommand (
const Command& cmd)
noexcept;
426#ifdef JOIN_HAS_IO_URING
432 int writeCommand (
const Command& cmd, std::true_type)
noexcept;
439 int writeCommand (
const Command& cmd, std::false_type)
noexcept;
445 void readCommands () noexcept;
451 void processCommand (const Command& cmd) noexcept;
459 int submitOperation (IoOperation& op,
bool flush) noexcept;
467 int cancelOperation (IoOperation& op,
bool flush) noexcept;
472 void cancelAllOperations () noexcept;
487 void notifyOperation (IoOperation& op,
int result,
bool cancelled) noexcept;
495 void dispatchOperation (IoOperation* op,
int result,
bool cancelled) noexcept;
503 void endOperation (IoOperation& op,
int result,
bool cancelled = false) noexcept;
509 void resetOperation (IoOperation& op) noexcept;
511#ifdef JOIN_HAS_IO_URING
518 template <
size_t Count,
size_t... Sizes,
size_t... Is>
525 io_uring_sqe* getSqe () noexcept;
532 void prepareSqe (io_uring_sqe* sqe, IoOperation& op) noexcept;
538 void dispatchCqe (io_uring_cqe* cqe) noexcept;
544 void dispatchCqe (io_uring_cqe* cqe,
std::true_type) noexcept;
550 void dispatchCqe (io_uring_cqe* cqe,
std::false_type) noexcept;
555 void eventLoop () noexcept;
560 void eventLoop (
std::false_type,
std::false_type) noexcept;
565 void eventLoop (
std::true_type,
std::false_type) noexcept;
570 void eventLoop (
std::true_type,
std::true_type) noexcept;
577 static bool isWriteOp (
const IoOperation& op)
noexcept;
585 static int executeOp (IoOperation& op, uint32_t revents)
noexcept;
592 void onEvent (
int fd, uint32_t revents)
noexcept override;
596 static constexpr size_t _queueSize = 1024;
602#ifdef JOIN_HAS_IO_URING
603 alignas (64) std::atomic<WakeupState> _wakeupState{WakeupState::Spinning};
605 alignas (64) std::atomic<WakeupState> _wakeupState{WakeupState::Sleeping};
611#ifdef JOIN_HAS_IO_URING
613 alignas (64) uint64_t _wakeupBuf = 0;
616 IoOperation _wakeupOp = {};
622 std::vector<IoOperation*> _pendingOps;
625 static constexpr pthread_t _invalidThreadId =
static_cast<pthread_t
> (-1);
628 std::atomic<pthread_t> _threadId{_invalidThreadId};
631 std::atomic<bool> _running{
false};
634 std::vector<IoOperation*> _readOps;
637 std::vector<IoOperation*> _writeOps;
644 std::unordered_map<uint16_t, IoRingBuffer> _bufferRings;
651#ifdef JOIN_HAS_IO_URING
652template <
typename Policy>
658 if (isProactorThread ())
660 return submitOperation (op, flush);
663 std::atomic<bool> done{
false}, *pdone =
nullptr;
664 std::error_code errc, *perrc =
nullptr;
672 if (
JOIN_UNLIKELY (writeCommand ({CommandType::Submit, &op, flush, pdone, perrc,
nullptr}) == -1))
680 while (!done.load (std::memory_order_acquire))
699#ifdef JOIN_HAS_IO_URING
700template <
typename Policy>
706 if (isProactorThread ())
708 return cancelOperation (op, flush);
711 std::atomic<bool> done{
false}, *pdone =
nullptr;
712 std::error_code errc, *perrc =
nullptr;
720 if (
JOIN_UNLIKELY (writeCommand ({CommandType::Cancel, &op, flush, pdone, perrc,
nullptr}) == -1))
728 while (!done.load (std::memory_order_acquire))
747#ifdef JOIN_HAS_IO_URING
748template <
typename Policy>
754 if (isProactorThread ())
756 return invokeFunction (fn);
759 std::atomic<bool> done{
false}, *pdone =
nullptr;
760 std::error_code errc, *perrc =
nullptr;
768 if (
JOIN_UNLIKELY (writeCommand ({CommandType::Invoke,
nullptr,
false, pdone, perrc, fn}) == -1))
776 while (!done.load (std::memory_order_acquire))
795#ifdef JOIN_HAS_IO_URING
796template <
typename Policy>
811 if ((expected == IoOperation::State::Idle) || (expected == IoOperation::State::Submitted))
813 if (op->state.compare_exchange_strong (expected, IoOperation::State::Suspended, std::memory_order_acquire,
814 std::memory_order_relaxed))
816 op->resume = expected;
820 else if ((expected == IoOperation::State::Busy) && isProactorThread ())
833#ifdef JOIN_HAS_IO_URING
834template <
typename Policy>
845 op->handler = handler;
847 op->state.compare_exchange_strong (expected, op->resume, std::memory_order_release, std::memory_order_relaxed);
854#ifdef JOIN_HAS_IO_URING
855template <
typename Policy>
858inline int join::BasicProactor::invokeFunction (InvokeHandler* fn)
noexcept
876#ifdef JOIN_HAS_IO_URING
877template <
typename Policy>
880inline void join::BasicProactor::notifyOperation (IoOperation& op,
int result,
bool cancelled)
noexcept
887 op.handler->onCancel (op, result);
891 op.handler->onComplete (op, result);
900#ifdef JOIN_HAS_IO_URING
901template <
typename Policy>
904inline void join::BasicProactor::dispatchOperation (IoOperation* op,
int result,
bool cancelled)
noexcept
915 IoOperation::State expected = op->state.load (std::memory_order_relaxed);
916 if (expected != IoOperation::State::Suspended)
918 if (op->state.compare_exchange_strong (expected, IoOperation::State::Busy, std::memory_order_acquire,
919 std::memory_order_relaxed))
927 if (op->ring !=
nullptr)
935 notifyOperation (*op, result, cancelled);
942#ifdef JOIN_HAS_IO_URING
943template <
typename Policy>
946inline void join::BasicProactor::resetOperation (IoOperation& op)
noexcept
949 IoOperation::State expected = IoOperation::State::Submitted;
950 if (!op.state.compare_exchange_strong (expected, IoOperation::State::Idle, std::memory_order_release,
951 std::memory_order_relaxed) &&
952 (expected != IoOperation::State::Busy))
954 op.resume = IoOperation::State::Idle;
958#ifdef JOIN_HAS_IO_URING
967#ifdef JOIN_HAS_IO_URING
968template <
typename Policy = join::IoDefaultPolicy>
979#ifdef JOIN_HAS_IO_URING
985 return instance ()._proactor;
995 return instance ()._dispatcher.
affinity (core);
1004 return instance ()._dispatcher.
affinity ();
1014 return instance ()._dispatcher.
priority (prio);
1023 return instance ()._dispatcher.
priority ();
1032 return instance ()._dispatcher.
handle ();
1041 static int mbind (
int numa)
noexcept
1043 return instance ()._proactor.mbind (numa);
1053 return instance ()._proactor.
mlock ();
1064 return proactorThread;
1070 BasicProactorThread ()
1072 _dispatcher = Thread ([
this] () {
1087 BasicProactorThread (
const BasicProactorThread&) =
delete;
1094 BasicProactorThread& operator= (
const BasicProactorThread&) =
delete;
1100 BasicProactorThread (BasicProactorThread&&) =
delete;
1107 BasicProactorThread& operator= (BasicProactorThread&&) =
delete;
1112 ~BasicProactorThread ()
1115 _dispatcher.
join ();
1118#ifdef JOIN_HAS_IO_URING
1120 BasicProactor<Policy> _proactor;
1123 BasicProactor _proactor;
adaptive backoff strategy for busy-wait loops.
Definition backoff.hpp:43
memory arena owning backend and managing one or more pools.
Definition memory.hpp:53
Convenience class that owns a Proactor running on a dedicated background thread.
Definition proactor.hpp:973
static int affinity() noexcept
get proactor thread affinity.
Definition proactor.hpp:1002
static int priority(int prio)
set proactor thread scheduling priority.
Definition proactor.hpp:1012
static int mlock() noexcept
lock proactor command queue memory in RAM.
Definition proactor.hpp:1051
static int affinity(int core)
set proactor thread affinity.
Definition proactor.hpp:993
static pthread_t handle() noexcept
get the native handle of the proactor thread.
Definition proactor.hpp:1030
static BasicProactor & proactor()
get the Proactor instance owned by the singleton ProactorThread.
Definition proactor.hpp:982
static int priority() noexcept
get proactor thread scheduling priority.
Definition proactor.hpp:1021
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(const BasicProactor &other)=delete
copy constructor.
BasicProactor(BasicProactor &&other)=delete
move constructor.
~BasicProactor() noexcept
destroy instance.
Definition proactor_epoll_impl.hpp:50
int invoke(InvokeHandler *fn, bool sync=true) noexcept
invoke a function from the proactor thread.
Definition proactor.hpp:751
void suspend(IoOperation *op) noexcept
suspend an I/O operation.
Definition proactor.hpp:799
BasicProactor & operator=(const BasicProactor &other)=delete
copy assignment operator.
Function< void(), 64 > InvokeHandler
function invoked on the proactor thread.
Definition proactor.hpp:159
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
int submit(IoOperation &op, bool flush=false, bool sync=false) noexcept
submit an asynchronous operation to the proactor.
Definition proactor.hpp:655
bool isProactorThread() const noexcept
check if the calling thread is the proactor thread.
Definition proactor_epoll_impl.hpp:252
void resume(IoOperation *op, CompletionHandler *handler) noexcept
resume an I/O operation.
Definition proactor.hpp:837
void waitStopped() const noexcept
wait for the event loop thread to terminate.
Definition proactor_epoll_impl.hpp:97
int cancel(IoOperation &op, bool flush=false, bool sync=false) noexcept
cancel an in-flight operation.
Definition proactor.hpp:703
BasicProactor()
initialize the proactor and its I/O backend.
Definition proactor_epoll_impl.hpp:32
completion handler interface class.
Definition proactor.hpp:79
virtual void onComplete(IoOperation &op, int result)
method called when an operation completes successfully.
Definition proactor.hpp:131
CompletionHandler()=default
create instance.
virtual ~CompletionHandler()=default
destroy instance.
CompletionHandler(CompletionHandler &&other)=default
move constructor.
CompletionHandler & operator=(const CompletionHandler &other)=default
copy assignment operator.
CompletionHandler(const CompletionHandler &other)=default
copy constructor.
virtual void onCancel(IoOperation &op, int result)
method called when an operation is cancelled.
Definition proactor.hpp:141
Event handler interface class.
Definition reactor.hpp:48
friend class Reactor
friendship with reactor.
Definition reactor.hpp:149
fixed-capacity move-only allocation-free alternative to std::function.
Definition function.hpp:44
BasicArena< LocalMem, Count, Sizes... > Allocator
Definition memory.hpp:125
void join() noexcept
block the current thread until the running thread finishes its execution.
Definition thread.cpp:252
int affinity(int core)
set thread affinity.
Definition thread.cpp:106
int priority(int prio)
set thread priority.
Definition thread.cpp:169
pthread_t handle() const noexcept
get the handle of the thread of execution. @retunr thread of execution handle.
Definition thread.cpp:301
Definition acceptor.hpp:32
BasicProactorThread ProactorThread
Definition proactor.hpp:71
std::error_code make_error_code(join::Errc code) noexcept
Create an std::error_code object.
Definition error.cpp:195
BasicProactor Proactor
Definition proactor.hpp:70
Describes a single asynchronous operation submitted to the Proactor.
Definition io_operation.hpp:47
State
operation lifecycle state.
Definition io_operation.hpp:52
BasicQueue< Type, Backend, SyncPolicy< Type, Backend > > Queue
queue type alias combining backend and policy.
Definition queue.hpp:1054
#define JOIN_LIKELY(x)
Definition utils.hpp:45
#define JOIN_UNLIKELY(x)
Definition utils.hpp:46