25#ifndef JOIN_CORE_PROACTOR_HPP
26#define JOIN_CORE_PROACTOR_HPP
32#ifdef JOIN_HAS_IO_URING
44#include <unordered_map>
49#include <sys/eventfd.h>
55 class CompletionHandler;
56#ifdef JOIN_HAS_IO_URING
57 template <
typename IoPolicy>
59 template <
typename IoPolicy>
60 class BasicProactorThread;
62 using Proactor = BasicProactor<IoDefaultPolicy>;
63 using HybridProactor = BasicProactor<IoHybridPolicy>;
64 using SqpollProactor = BasicProactor<IoSqpollPolicy>;
66 using HybridProactorThread = BasicProactorThread<IoHybridPolicy>;
67 using SqpollProactorThread = BasicProactorThread<IoSqpollPolicy>;
70 class BasicProactorThread;
83#ifdef JOIN_HAS_IO_URING
84 template <
typename IoPolicy>
152#ifdef JOIN_HAS_IO_URING
153template <
typename IoPolicy = join::IoDefaultPolicy>
161 static constexpr size_t _queueSize = 1024;
163#ifdef JOIN_HAS_IO_URING
175 : _proactor (proactor)
185 template <
class Wheel>
186 int start ([[maybe_unused]]
Wheel& wheel, [[maybe_unused]] std::chrono::nanoseconds period)
noexcept
195 void stop ()
noexcept
204 bool isRunnerThread ()
const noexcept
206 return _proactor.isProactorThread ();
214 template <
class Wheel>
215 int flush ([[maybe_unused]]
Wheel& wheel)
noexcept
222 return _proactor.wakeup ();
231 template <
class Wheel>
232 static uint64_t advance (
Wheel& wheel)
noexcept
234 return wheel.advance ();
243 template <
class Wheel>
244 static __kernel_timespec* timeout (
const Wheel& wheel, __kernel_timespec& ts)
noexcept
246 const std::chrono::nanoseconds next = wheel.next ();
248 if (next == std::chrono::nanoseconds::max ())
253 ts.tv_sec = next.count () / 1'000'000'000;
254 ts.tv_nsec = next.count () % 1'000'000'000;
271#ifdef JOIN_HAS_IO_URING
273 "tick must not be finer than the backoff sleep");
344#ifdef JOIN_HAS_IO_URING
350 int flush (
bool sync =
false) noexcept;
375 void stop (
bool sync =
true) noexcept;
382#ifdef JOIN_HAS_IO_URING
388 template <
size_t Count,
size_t... Sizes>
395 int unregisterFixedBuffers () noexcept;
405 template <
size_t Count,
size_t Size>
414 template <
size_t Count,
size_t Size>
430 int mbind (
int numa)
const noexcept;
437 int mlock () const noexcept;
461 enum class WakeupState
471 enum class CommandType
477#ifdef JOIN_HAS_IO_URING
485 struct alignas (64) Command
490 std::atomic<bool>* done;
491 std::error_code* errc;
500 int writeCommand (
const Command& cmd)
noexcept;
502#ifdef JOIN_HAS_IO_URING
507 int wakeup () noexcept;
513 void readCommands () noexcept;
519 void processCommand (const Command& cmd) noexcept;
527 int submitOperation (IoOperation& op,
bool flush) noexcept;
535 int cancelOperation (IoOperation& op,
bool flush) noexcept;
540 void cancelAllOperations () noexcept;
555 void notifyOperation (IoOperation& op,
int result,
bool cancelled) noexcept;
563 void dispatchOperation (IoOperation& op,
int result,
bool cancelled) noexcept;
571 void endOperation (IoOperation& op,
int result,
bool cancelled = false) noexcept;
577 void resetOperation (IoOperation& op) noexcept;
579#ifdef JOIN_HAS_IO_URING
586 template <
size_t Count,
size_t... Sizes,
size_t... Is>
593 io_uring_sqe* getSqe () noexcept;
600 void prepareSqe (io_uring_sqe* sqe, IoOperation& op) noexcept;
606 void dispatchCqe (io_uring_cqe* cqe) noexcept;
611 void eventLoop () noexcept;
616 void eventLoop (
std::false_type,
std::false_type) noexcept;
621 void eventLoop (
std::true_type,
std::false_type) noexcept;
626 void eventLoop (
std::true_type,
std::true_type) noexcept;
633 void onEvent (
int fd, uint32_t revents)
noexcept override;
642 void drainQueue (IoOperation& tail,
int result, uint32_t revents,
bool cancelled)
noexcept;
650 void processQueue (
int fd,
bool write, uint32_t revents)
noexcept;
657 void processOperation (IoOperation& op, uint32_t revents)
noexcept;
665 static int executeOperation (IoOperation& op, uint32_t revents)
noexcept;
672 bool unqueueOperation (IoOperation& op)
noexcept;
679 static bool isWriteOperation (
const IoOperation& op)
noexcept;
682#ifdef JOIN_HAS_IO_URING
691#ifdef JOIN_HAS_IO_URING
692 alignas (64) std::atomic<WakeupState> _wakeupState{WakeupState::Spinning};
694 alignas (64) std::atomic<WakeupState> _wakeupState{WakeupState::Sleeping};
700#ifdef JOIN_HAS_IO_URING
702 alignas (64) uint64_t _wakeupBuf = 0;
705 IoOperation _wakeupOp = {};
711 std::vector<IoOperation*> _pendingOps;
717 std::atomic<bool> _running{
false};
720 std::vector<IoOperation*> _readOps;
723 std::vector<IoOperation*> _writeOps;
730 std::unordered_map<uint16_t, IoRingBuffer> _bufferRings;
737#ifdef JOIN_HAS_IO_URING
738template <
typename IoPolicy>
744 if (isProactorThread ())
746 return submitOperation (op, flush);
749 std::atomic<bool> done{
false}, *pdone =
nullptr;
750 std::error_code errc, *perrc =
nullptr;
758 if (
JOIN_UNLIKELY (writeCommand ({CommandType::Submit, &op, flush, pdone, perrc,
nullptr}) == -1))
766 while (!done.load (std::memory_order_acquire))
785#ifdef JOIN_HAS_IO_URING
786template <
typename IoPolicy>
792 if (isProactorThread ())
794 return cancelOperation (op, flush);
797 std::atomic<bool> done{
false}, *pdone =
nullptr;
798 std::error_code errc, *perrc =
nullptr;
806 if (
JOIN_UNLIKELY (writeCommand ({CommandType::Cancel, &op, flush, pdone, perrc,
nullptr}) == -1))
814 while (!done.load (std::memory_order_acquire))
833#ifdef JOIN_HAS_IO_URING
834template <
typename IoPolicy>
840 if (isProactorThread ())
842 return invokeFunction (fn);
845 std::atomic<bool> done{
false}, *pdone =
nullptr;
846 std::error_code errc, *perrc =
nullptr;
854 if (
JOIN_UNLIKELY (writeCommand ({CommandType::Invoke,
nullptr,
false, pdone, perrc, fn}) == -1))
862 while (!done.load (std::memory_order_acquire))
881#ifdef JOIN_HAS_IO_URING
882template <
typename IoPolicy>
892 if ((expected == IoOperation::State::Idle) || (expected == IoOperation::State::Submitted))
894 if (op.state.compare_exchange_strong (expected, IoOperation::State::Suspended, std::memory_order_acquire,
895 std::memory_order_relaxed))
897 op.resume = expected;
901 else if ((expected == IoOperation::State::Busy) && isProactorThread ())
914#ifdef JOIN_HAS_IO_URING
915template <
typename IoPolicy>
921 op.handler = &handler;
923 op.state.compare_exchange_strong (expected, op.resume, std::memory_order_release, std::memory_order_relaxed);
930#ifdef JOIN_HAS_IO_URING
931template <
typename IoPolicy>
934inline int join::BasicProactor::invokeFunction (InvokeHandler* fn)
noexcept
952#ifdef JOIN_HAS_IO_URING
953template <
typename IoPolicy>
956inline void join::BasicProactor::notifyOperation (IoOperation& op,
int result,
bool cancelled)
noexcept
963 op.handler->onCancel (op, result);
967 op.handler->onComplete (op, result);
976#ifdef JOIN_HAS_IO_URING
977template <
typename IoPolicy>
980inline void join::BasicProactor::dispatchOperation (IoOperation& op,
int result,
bool cancelled)
noexcept
986 IoOperation::State expected = op.state.load (std::memory_order_relaxed);
987 if (expected != IoOperation::State::Suspended)
989 if (op.state.compare_exchange_strong (expected, IoOperation::State::Busy, std::memory_order_acquire,
990 std::memory_order_relaxed))
998 if (op.ring !=
nullptr)
1006 notifyOperation (op, result, cancelled);
1013#ifdef JOIN_HAS_IO_URING
1014template <
typename IoPolicy>
1017inline void join::BasicProactor::resetOperation (IoOperation& op)
noexcept
1020 IoOperation::State expected = IoOperation::State::Submitted;
1021 if (!op.state.compare_exchange_strong (expected, IoOperation::State::Idle, std::memory_order_release,
1022 std::memory_order_relaxed) &&
1023 (expected != IoOperation::State::Busy))
1025 op.resume = IoOperation::State::Idle;
1029#ifdef JOIN_HAS_IO_URING
1038#ifdef JOIN_HAS_IO_URING
1039template <
typename IoPolicy = join::IoDefaultPolicy>
1050#ifdef JOIN_HAS_IO_URING
1056 return instance ()._proactor;
1066 return instance ()._dispatcher.
affinity (core);
1075 return instance ()._dispatcher.
affinity ();
1085 return instance ()._dispatcher.
priority (prio);
1094 return instance ()._dispatcher.
priority ();
1103 return instance ()._dispatcher.
handle ();
1112 static int mbind (
int numa)
noexcept
1114 return instance ()._proactor.mbind (numa);
1124 return instance ()._proactor.
mlock ();
1135 return proactorThread;
1141 BasicProactorThread ()
1143 _dispatcher = Thread ([
this] () {
1158 BasicProactorThread (
const BasicProactorThread&) =
delete;
1165 BasicProactorThread& operator= (
const BasicProactorThread&) =
delete;
1171 BasicProactorThread (BasicProactorThread&&) =
delete;
1178 BasicProactorThread& operator= (BasicProactorThread&&) =
delete;
1183 ~BasicProactorThread ()
1186 _dispatcher.
join ();
1189#ifdef JOIN_HAS_IO_URING
1191 BasicProactor<IoPolicy> _proactor;
1194 BasicProactor _proactor;
adaptive backoff for busy-wait loops, requires Rdtsc::calibrate ().
Definition backoff.hpp:47
Convenience class that owns a Proactor running on a dedicated background thread.
Definition proactor.hpp:1044
static int affinity() noexcept
get proactor thread affinity.
Definition proactor.hpp:1073
static int priority(int prio)
set proactor thread scheduling priority.
Definition proactor.hpp:1083
static int mlock() noexcept
lock proactor command queue and timer wheel memory in RAM.
Definition proactor.hpp:1122
static int affinity(int core)
set proactor thread affinity.
Definition proactor.hpp:1064
static pthread_t handle() noexcept
get the native handle of the proactor thread.
Definition proactor.hpp:1101
static BasicProactor & proactor()
get the Proactor instance owned by the singleton ProactorThread.
Definition proactor.hpp:1053
static int priority() noexcept
get proactor thread scheduling priority.
Definition proactor.hpp:1092
basic proactor class.
Definition proactor.hpp:158
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(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:837
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
BasicProactor & operator=(const BasicProactor &other)=delete
copy assignment operator.
Function< void(), 64 > InvokeHandler
function invoked on the proactor thread.
Definition proactor.hpp:269
void stop(bool sync=true) noexcept
stop the event loop, ignored if no event loop is running.
Definition proactor_epoll_impl.hpp:71
int submit(IoOperation &op, bool flush=false, bool sync=false) noexcept
submit an asynchronous operation to the proactor.
Definition proactor.hpp:741
void resume(IoOperation &op, CompletionHandler &handler) noexcept
resume an I/O operation.
Definition proactor.hpp:918
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
void suspend(IoOperation &op) noexcept
suspend an I/O operation.
Definition proactor.hpp:885
int cancel(IoOperation &op, bool flush=false, bool sync=false) noexcept
cancel an in-flight operation.
Definition proactor.hpp:789
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
completion handler interface class.
Definition proactor.hpp:81
virtual void onComplete(IoOperation &op, int result)
method called when an operation completes successfully.
Definition proactor.hpp:133
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:143
Event handler interface class.
Definition reactor.hpp:51
friend class Reactor
friendship with reactor.
Definition reactor.hpp:152
fixed-capacity move-only allocation-free alternative to std::function.
Definition function.hpp:44
BasicSlabAllocator< LocalMem, Count, Sizes... > SlabAllocator
Definition memory.hpp:327
BasicWheel< Monotonic, WheelPolicy, _queueSize, 1 '000 > Wheel
timer wheel, able to arm a timer for every queued command.
Definition reactor.hpp:276
void join() noexcept
block the current thread until the running thread finishes its execution.
Definition thread.cpp:292
static constexpr pid_t invalidId
invalid thread id.
Definition thread.hpp:183
int affinity(int core)
set thread affinity.
Definition thread.cpp:148
int priority(int prio)
set thread priority.
Definition thread.cpp:211
pthread_t handle() const noexcept
get the handle of the thread of execution.
Definition thread.cpp:350
Monotonic::Wheel< 64 > Wheel
Definition monotonic_wheel_test.cpp:41
Definition acceptor.hpp:32
BasicProactorThread ProactorThread
Definition proactor.hpp:73
constexpr uint64_t sleep_ns_v
Definition io_policy.hpp:100
std::error_code make_error_code(join::Errc code) noexcept
Create an std::error_code object.
Definition error.cpp:195
constexpr uint64_t tick_ns_v
Definition io_policy.hpp:110
BasicProactor Proactor
Definition proactor.hpp:72
Describes a single asynchronous operation submitted to the Proactor.
Definition io_operation.hpp:48
State
operation lifecycle state.
Definition io_operation.hpp:53
BasicQueue< Type, Backend, SyncPolicy< Type, Backend > > Queue
queue type alias combining backend and policy.
Definition queue.hpp:1040
#define JOIN_LIKELY(x)
Definition utils.hpp:50
#define JOIN_UNLIKELY(x)
Definition utils.hpp:51