join 1.0
lightweight network framework library
Loading...
Searching...
No Matches
reactor.hpp
Go to the documentation of this file.
1
25#ifndef JOIN_CORE_REACTOR_HPP
26#define JOIN_CORE_REACTOR_HPP
27
28// libjoin.
29#include <join/function.hpp>
30#include <join/thread.hpp>
31#include <join/queue.hpp>
32#include <join/wheel.hpp>
33#include <join/utils.hpp>
34
35// C++.
36#include <unordered_map>
37#include <unordered_set>
38#include <atomic>
39#include <chrono>
40#include <limits>
41
42// C.
43#include <sys/epoll.h>
44
45namespace join
46{
51 {
52 public:
56 EventHandler () = default;
57
62 EventHandler (const EventHandler& other) = default;
63
69 EventHandler& operator= (const EventHandler& other) = default;
70
75 EventHandler (EventHandler&& other) = default;
76
83
87 virtual ~EventHandler () = default;
88
89 protected:
95 virtual void onEvent (int fd, uint32_t revents)
96 {
97 if (JOIN_UNLIKELY (revents & EPOLLERR))
98 {
99 onError (fd);
100 }
101 else if (JOIN_UNLIKELY (revents & (EPOLLRDHUP | EPOLLHUP)))
102 {
103 onClose (fd);
104 }
105 else if (JOIN_LIKELY (revents & EPOLLIN))
106 {
107 onReadable (fd);
108 }
109 else if (revents & EPOLLOUT)
110 {
111 onWriteable (fd);
112 }
113 }
114
119 virtual void onReadable ([[maybe_unused]] int fd)
120 {
121 // do nothing.
122 }
123
128 virtual void onWriteable ([[maybe_unused]] int fd)
129 {
130 // do nothing.
131 }
132
137 virtual void onClose ([[maybe_unused]] int fd)
138 {
139 // do nothing.
140 }
141
146 virtual void onError ([[maybe_unused]] int fd)
147 {
148 // do nothing.
149 }
150
152 friend class Reactor;
153 };
154
159 {
160 private:
162 static constexpr size_t _deletedReserve = 64;
163
165 static constexpr size_t _queueSize = 1024;
166
168 static constexpr size_t _maxEvents = 1024;
169
173 class WheelPolicy
174 {
175 public:
180 explicit WheelPolicy (Reactor& reactor) noexcept
181 : _reactor (reactor)
182 {
183 }
184
191 template <class Wheel>
192 int start ([[maybe_unused]] Wheel& wheel, [[maybe_unused]] std::chrono::nanoseconds period) noexcept
193 {
194 // do nothing, the event loop advances the wheel.
195 return 0;
196 }
197
201 void stop () noexcept
202 {
203 // do nothing, the event loop advances the wheel.
204 }
205
210 bool isRunnerThread () const noexcept
211 {
212 return _reactor.isReactorThread ();
213 }
214
220 template <class Wheel>
221 int flush ([[maybe_unused]] Wheel& wheel) noexcept
222 {
223 if (JOIN_UNLIKELY (!_reactor.isRunning ()))
224 {
225 return -1;
226 }
227
228 return _reactor.wakeup ();
229 }
230
231 private:
237 template <class Wheel>
238 static uint64_t advance (Wheel& wheel) noexcept
239 {
240 return wheel.advance ();
241 }
242
249 template <class Wheel>
250 static timespec* timeout (const Wheel& wheel, timespec& ts) noexcept
251 {
252 const std::chrono::nanoseconds next = wheel.next ();
253
254 if (next == std::chrono::nanoseconds::max ())
255 {
256 return nullptr;
257 }
258
259 ts = toTimespec (next);
260
261 return &ts;
262 }
263
265 Reactor& _reactor;
266
268 friend class Reactor;
269 };
270
271 public:
273 using InvokeHandler = Function<void (), 64>;
274
276 using Wheel = BasicWheel<Monotonic, WheelPolicy, _queueSize, 1'000>;
277
281 Reactor ();
282
287 Reactor (const Reactor& other) = delete;
288
294 Reactor& operator= (const Reactor& other) = delete;
295
300 Reactor (Reactor&& other) = delete;
301
307 Reactor& operator= (Reactor&& other) = delete;
308
312 ~Reactor () noexcept;
313
323 int addHandler (int fd, EventHandler* handler, bool wantRead = true, bool wantWrite = false,
324 bool sync = true) noexcept;
325
332 int delHandler (int fd, bool sync = true) noexcept;
333
340 int invoke (InvokeHandler* fn, bool sync = true) noexcept;
341
345 void run ();
346
351 void stop (bool sync = true) noexcept;
352
356 void waitStopped () const noexcept;
357
358#ifdef JOIN_HAS_NUMA
364 int mbind (int numa) const noexcept;
365#endif
366
371 int mlock () const noexcept;
372
377 bool isRunning () const noexcept;
378
383 bool isReactorThread () const noexcept;
384
389 Wheel& wheel () noexcept
390 {
391 return _wheel;
392 }
393
394 private:
398 enum class CommandType
399 {
400 Add,
401 Del,
402 Invoke,
403 Stop
404 };
405
409 struct alignas (64) Command
410 {
411 CommandType type;
412 int fd;
413 uint32_t events;
414 EventHandler* handler;
415 std::atomic<bool>* done;
416 std::error_code* errc;
417 InvokeHandler* fn;
418 };
419
427 int registerHandler (int fd, EventHandler* handler, uint32_t events) noexcept;
428
434 int unregisterHandler (int fd) noexcept;
435
441 int invokeFunction (InvokeHandler* fn) noexcept;
442
448 int writeCommand (const Command& cmd) noexcept;
449
454 int wakeup () noexcept;
455
460 void processCommand (const Command& cmd) noexcept;
461
465 void readCommands () noexcept;
466
471 void dispatchEvent (const epoll_event& event);
472
476 void eventLoop ();
477
483 bool isActive (int fd) const noexcept;
484
486 Wheel _wheel{*this};
487
489 int _wakeup = -1;
490
492 int _epoll = -1;
493
495 alignas (64) std::atomic<bool> _notified{false};
496
499
501 std::unordered_map<int, EventHandler*> _handlers;
502
504 std::unordered_set<int> _deleted;
505
507 std::atomic<bool> _running{false};
508
510 std::atomic<pid_t> _threadId{Thread::invalidId};
511 };
512
517 {
518 public:
523 static Reactor& reactor ();
524
530 static int affinity (int core);
531
536 static int affinity ();
537
543 static int priority (int prio);
544
549 static int priority ();
550
555 static pthread_t handle ();
556
557#ifdef JOIN_HAS_NUMA
563 static int mbind (int numa);
564#endif
565
570 static int mlock ();
571
572 private:
577 static ReactorThread& instance ();
578
582 ReactorThread ();
583
588 ReactorThread (const ReactorThread& other) = delete;
589
595 ReactorThread& operator= (const ReactorThread& other) = delete;
596
601 ReactorThread (ReactorThread&& other) noexcept = delete;
602
608 ReactorThread& operator= (ReactorThread&& other) noexcept = delete;
609
614
616 Reactor _reactor;
617
619 Thread _dispatcher;
620 };
621}
622
623#endif
timing wheel.
Definition wheel.hpp:58
Event handler interface class.
Definition reactor.hpp:51
EventHandler(const EventHandler &other)=default
copy constructor.
virtual void onClose(int fd)
method called when handle was closed by the peer.
Definition reactor.hpp:137
virtual ~EventHandler()=default
destroy instance.
virtual void onError(int fd)
method called when an error occurred on handle.
Definition reactor.hpp:146
EventHandler()=default
create instance.
virtual void onReadable(int fd)
method called when data are ready to be read on handle.
Definition reactor.hpp:119
virtual void onEvent(int fd, uint32_t revents)
method called when events are reported on handle.
Definition reactor.hpp:95
EventHandler & operator=(const EventHandler &other)=default
copy assignment operator.
EventHandler(EventHandler &&other)=default
move constructor.
virtual void onWriteable(int fd)
method called when data are ready to be written on handle.
Definition reactor.hpp:128
fixed-capacity move-only allocation-free alternative to std::function.
Definition function.hpp:44
monotonic clock policy (stable, adjusted, recommended default).
Definition clock.hpp:105
Convenience class that owns a Reactor running on a dedicated background thread.
Definition reactor.hpp:517
static int affinity()
get reactor thread affinity.
Definition reactor.cpp:630
static int priority()
get reactor thread priority.
Definition reactor.cpp:648
static pthread_t handle()
get the handle of the reactor thread.
Definition reactor.cpp:657
static Reactor & reactor()
get the global Reactor instance.
Definition reactor.cpp:612
static int mlock()
lock command queue and timer wheel memory in RAM.
Definition reactor.cpp:677
Reactor class.
Definition reactor.hpp:159
bool isRunning() const noexcept
check if the event loop is running.
Definition reactor.cpp:328
Reactor(Reactor &&other)=delete
move constructor.
Reactor()
default constructor.
Definition reactor.cpp:47
Function< void(), 64 > InvokeHandler
function invoked on the reactor thread.
Definition reactor.hpp:273
void waitStopped() const noexcept
wait for the event loop thread to terminate.
Definition reactor.cpp:284
bool isReactorThread() const noexcept
check if the calling thread is the reactor thread.
Definition reactor.cpp:337
int mlock() const noexcept
lock command queue and timer wheel memory in RAM.
Definition reactor.cpp:314
int delHandler(int fd, bool sync=true) noexcept
delete handler from reactor.
Definition reactor.cpp:156
Reactor & operator=(const Reactor &other)=delete
copy assignment operator.
void run()
run the event loop (blocking).
Definition reactor.cpp:242
Reactor(const Reactor &other)=delete
copy constructor.
~Reactor() noexcept
destroy instance.
Definition reactor.cpp:89
Wheel & wheel() noexcept
get the timer wheel advanced by the event loop.
Definition reactor.hpp:389
void stop(bool sync=true) noexcept
stop the event loop, ignored if no event loop is running.
Definition reactor.cpp:259
int addHandler(int fd, EventHandler *handler, bool wantRead=true, bool wantWrite=false, bool sync=true) noexcept
add handler to reactor.
Definition reactor.cpp:101
int invoke(InvokeHandler *fn, bool sync=true) noexcept
invoke a function from the reactor thread.
Definition reactor.cpp:199
thread class.
Definition thread.hpp:180
static constexpr pid_t invalidId
invalid thread id.
Definition thread.hpp:183
Monotonic::Wheel< 64 > Wheel
Definition monotonic_wheel_test.cpp:41
Definition acceptor.hpp:32
struct timespec toTimespec(std::chrono::time_point< Clock, Duration > timePoint)
converts time_point to timespec.
Definition utils.hpp:431
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