Toolbox snapshot
The Reactive C++ Toolbox
Loading...
Searching...
No Matches
Reactor.hpp
Go to the documentation of this file.
1// The Reactive C++ Toolbox.
2// Copyright (C) 2013-2019 Swirly Cloud Limited
3// Copyright (C) 2021 Reactive Markets Limited
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17#ifndef TOOLBOX_IO_REACTOR_HPP
18#define TOOLBOX_IO_REACTOR_HPP
19
20#include <toolbox/io/Epoll.hpp>
22#include <toolbox/io/Hook.hpp>
23#include <toolbox/io/Timer.hpp>
24#include <toolbox/io/Waker.hpp>
25
26namespace toolbox {
27inline namespace io {
28
29constexpr Duration NoTimeout{-1};
30enum class Priority { High = 0, Low = 1 };
31
33
34class TOOLBOX_API Reactor : public Waker {
35 public:
37 // HookType describes the kind of hook.
38 enum class HookType : int {
39 // EndOfCycleNoWait hooks are called at the end of the Reactor cycle.
40 // The Reactor cycle will not wait for i/o and/or timer events
41 // while any of these hooks are installed.
42 EndOfCycleNoWait = 1,
43 // EndOfEventDispatch hooks are called after all i/o and timer events have been dispatched.
44 // These hooks are called, and only if, work done in the cycle is greater than zero.
45 // And they are always called before EndOfCycleNoWait hooks.
46 EndOfEventDispatch = 2,
47 };
48 class Handle {
49 public:
50 Handle(Reactor& reactor, int fd, int sid)
51 : reactor_{&reactor}
52 , fd_{fd}
53 , sid_{sid}
54 {
55 }
56 constexpr Handle(std::nullptr_t = nullptr) noexcept {} // NOLINT(hicpp-explicit-conversions)
57 ~Handle() { reset(); }
58
59 // Copy.
60 Handle(const Handle&) = delete;
61 Handle& operator=(const Handle&) = delete;
62
63 // Move.
64 Handle(Handle&& rhs) noexcept
65 : reactor_{rhs.reactor_}
66 , fd_{rhs.fd_}
67 , sid_{rhs.sid_}
68 {
69 rhs.reactor_ = nullptr;
70 rhs.fd_ = -1;
71 rhs.sid_ = 0;
72 }
73 Handle& operator=(Handle&& rhs) noexcept
74 {
75 reset();
76 swap(rhs);
77 return *this;
78 }
79 bool empty() const noexcept { return reactor_ == nullptr; }
80 explicit operator bool() const noexcept { return reactor_ != nullptr; }
81 auto fd() const noexcept { return fd_; }
82 auto sid() const noexcept { return sid_; }
83
84 void reset(std::nullptr_t = nullptr) noexcept
85 {
86 if (reactor_) {
87 reactor_->unsubscribe(fd_, sid_);
88 reactor_ = nullptr;
89 fd_ = -1;
90 sid_ = 0;
91 }
92 }
93 void swap(Handle& rhs) noexcept
94 {
95 std::swap(reactor_, rhs.reactor_);
96 std::swap(fd_, rhs.fd_);
97 std::swap(sid_, rhs.sid_);
98 }
99
101 void set_events(unsigned events, IoSlot slot, std::error_code& ec) noexcept
102 {
103 assert(reactor_);
104 reactor_->set_events(fd_, sid_, events, slot, ec);
105 }
106 void set_events(unsigned events, IoSlot slot)
107 {
108 assert(reactor_);
109 reactor_->set_events(fd_, sid_, events, slot);
110 }
111 void set_events(unsigned events, std::error_code& ec) noexcept
112 {
113 assert(reactor_);
114 reactor_->set_events(fd_, sid_, events, ec);
115 }
116 void set_events(unsigned events)
117 {
118 assert(reactor_);
119 reactor_->set_events(fd_, sid_, events);
120 }
121
122 private:
123 Reactor* reactor_{nullptr};
124 int fd_{-1}, sid_{0};
125 };
126
127 explicit Reactor(std::size_t size_hint = 0);
128 ~Reactor() override;
129
130 // Copy.
131 Reactor(const Reactor&) = delete;
132 Reactor& operator=(const Reactor&) = delete;
133
134 // Move.
135 Reactor(Reactor&&) = delete;
137
138 // clang-format off
139 [[nodiscard]] Handle subscribe(int fd, unsigned events, IoSlot slot);
140
142 [[nodiscard]] Timer timer(MonoTime expiry, Duration interval, Priority priority, TimerSlot slot)
143 {
144 return tqs_[static_cast<size_t>(priority)].insert(expiry, interval, slot);
145 }
147 [[nodiscard]] Timer timer(MonoTime expiry, Priority priority, TimerSlot slot)
148 {
149 return tqs_[static_cast<size_t>(priority)].insert(expiry, slot);
150 }
151 // clang-format on
152
153 void add_hook(Hook& hook, HookType ht = HookType::EndOfCycleNoWait) noexcept
154 {
155 switch (ht) {
156 case HookType::EndOfCycleNoWait:
157 end_of_cycle_no_wait_hooks.push_back(hook);
158 break;
159 case HookType::EndOfEventDispatch:
160 end_of_event_dispatch_hooks_.push_back(hook);
161 break;
162 }
163 }
167 int poll(CyclTime now, Duration timeout = NoTimeout);
168
169 protected:
171 void do_wakeup() noexcept final;
172
173 private:
174 MonoTime next_expiry(MonoTime next) const;
175
176 int dispatch(CyclTime now, Event* buf, int size);
177 void set_events(int fd, int sid, unsigned events, IoSlot slot, std::error_code& ec) noexcept;
178 void set_events(int fd, int sid, unsigned events, IoSlot slot);
179 void set_events(int fd, int sid, unsigned events, std::error_code& ec) noexcept;
180 void set_events(int fd, int sid, unsigned events);
181 void unsubscribe(int fd, int sid) noexcept;
182
183 struct Data {
184 int sid{};
185 unsigned events{};
186 IoSlot slot;
187 };
188 Epoll epoll_;
189 std::vector<Data> data_;
190 EventFd notify_{0, EFD_NONBLOCK};
191 static_assert(static_cast<int>(Priority::High) == 0);
192 static_assert(static_cast<int>(Priority::Low) == 1);
193 TimerPool tp_;
194 std::array<TimerQueue, 2> tqs_{tp_, tp_};
195 HookList end_of_cycle_no_wait_hooks, end_of_event_dispatch_hooks_;
196};
197
198} // namespace io
199} // namespace toolbox
200
201#endif // TOOLBOX_IO_REACTOR_HPP
#define TOOLBOX_API
Definition Config.h:39
auto fd() const noexcept
Definition Reactor.hpp:81
Handle(const Handle &)=delete
constexpr Handle(std::nullptr_t=nullptr) noexcept
Definition Reactor.hpp:56
void swap(Handle &rhs) noexcept
Definition Reactor.hpp:93
Handle(Reactor &reactor, int fd, int sid)
Definition Reactor.hpp:50
bool empty() const noexcept
Definition Reactor.hpp:79
Handle & operator=(const Handle &)=delete
void set_events(unsigned events, IoSlot slot)
Definition Reactor.hpp:106
void set_events(unsigned events, IoSlot slot, std::error_code &ec) noexcept
Modify I/O event subscription.
Definition Reactor.hpp:101
void set_events(unsigned events)
Definition Reactor.hpp:116
Handle & operator=(Handle &&rhs) noexcept
Definition Reactor.hpp:73
auto sid() const noexcept
Definition Reactor.hpp:82
void reset(std::nullptr_t=nullptr) noexcept
Definition Reactor.hpp:84
Handle(Handle &&rhs) noexcept
Definition Reactor.hpp:64
void set_events(unsigned events, std::error_code &ec) noexcept
Definition Reactor.hpp:111
Timer timer(MonoTime expiry, Duration interval, Priority priority, TimerSlot slot)
Throws std::bad_alloc only.
Definition Reactor.hpp:142
Reactor(Reactor &&)=delete
Reactor & operator=(Reactor &&)=delete
Reactor & operator=(const Reactor &)=delete
void add_hook(Hook &hook, HookType ht=HookType::EndOfCycleNoWait) noexcept
Definition Reactor.hpp:153
Timer timer(MonoTime expiry, Priority priority, TimerSlot slot)
Throws std::bad_alloc only.
Definition Reactor.hpp:147
Reactor(const Reactor &)=delete
STL namespace.
constexpr Duration NoTimeout
Definition Reactor.hpp:29
boost::intrusive::list< Hook, boost::intrusive::constant_time_size< false > > HookList
Definition Hook.hpp:39
epoll_event EpollEvent
Definition Epoll.hpp:155
MonoClock::time_point MonoTime
Definition Time.hpp:110
Nanos Duration
Definition Time.hpp:40
TOOLBOX_API void reset(std::ostream &os) noexcept