]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/mplexer.hh
pkcs11signers: Use emplace_back for attributes
[thirdparty/pdns.git] / pdns / mplexer.hh
CommitLineData
12471842
PL
1/*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
e8c59f2d 22#pragma once
ab3e8a6c 23#include <boost/any.hpp>
ac3da0c2
RG
24#include <boost/multi_index_container.hpp>
25#include <boost/multi_index/ordered_index.hpp>
26#include <boost/multi_index/hashed_index.hpp>
27#include <boost/multi_index/key_extractors.hpp>
0bff046b 28#include <vector>
ab3e8a6c
BH
29#include <map>
30#include <stdexcept>
31#include <string>
f1d51ff7 32#include <sys/time.h>
ab3e8a6c 33
ac3da0c2
RG
34using namespace ::boost::multi_index;
35
ab3e8a6c
BH
36class FDMultiplexerException : public std::runtime_error
37{
38public:
e16e673d
RG
39 FDMultiplexerException(const std::string& str) :
40 std::runtime_error(str)
ab3e8a6c
BH
41 {}
42};
43
a1dfcec8
BH
44/** Very simple FD multiplexer, based on callbacks and boost::any parameters
45 As a special service, this parameter is kept around and can be modified,
46 allowing for state to be stored inside the multiplexer.
47
48 It has some "interesting" semantics
49*/
1f4abb20 50
ab3e8a6c
BH
51class FDMultiplexer
52{
d8f6d49f 53public:
d8f6d49f 54 typedef boost::any funcparam_t;
905dae56 55 typedef std::function<void(int, funcparam_t&)> callbackfunc_t;
e16e673d
RG
56 enum class EventKind : uint8_t
57 {
58 Read,
59 Write,
60 Both
61 };
d8f6d49f 62
e16e673d 63protected:
ab3e8a6c
BH
64 struct Callback
65 {
66 callbackfunc_t d_callback;
ac3da0c2 67 mutable funcparam_t d_parameter;
0bff046b 68 struct timeval d_ttd;
ac3da0c2 69 int d_fd;
ab3e8a6c
BH
70 };
71
72public:
e16e673d
RG
73 FDMultiplexer() :
74 d_inrun(false)
ab3e8a6c
BH
75 {}
76 virtual ~FDMultiplexer()
77 {}
78
7c58a81f
RG
79 // The maximum number of events processed in a single run, not the maximum of watched descriptors
80 static constexpr unsigned int s_maxevents = 1024;
81 /* The maximum number of events processed in a single run will be capped to the
82 minimum value of maxEventsHint and s_maxevents, to reduce memory usage. */
83 static FDMultiplexer* getMultiplexerSilent(unsigned int maxEventsHint = s_maxevents);
e16e673d 84
0e663c3b 85 /* tv will be updated to 'now' before run returns */
c0c6d1e7
RG
86 /* timeout is in ms, 0 will return immediately, -1 will block until at
87 least one descriptor is ready */
dd9c8246 88 /* returns 0 on timeout, -1 in case of error (but all implementations
c7a9f1b4
RG
89 actually throw in that case) and the number of ready events otherwise.
90 Note that We might have two events (read AND write) for the same descriptor */
e16e673d 91 virtual int run(struct timeval* tv, int timeout = 500) = 0;
ab3e8a6c 92
dd9c8246 93 /* timeout is in ms, 0 will return immediately, -1 will block until at least one FD is ready */
5bdbb83d
RG
94 virtual void getAvailableFDs(std::vector<int>& fds, int timeout) = 0;
95
a1dfcec8 96 //! Add an fd to the read watch list - currently an fd can only be on one list at a time!
e16e673d 97 void addReadFD(int fd, callbackfunc_t toDo, const funcparam_t& parameter = funcparam_t(), const struct timeval* ttd = nullptr)
ab3e8a6c 98 {
e16e673d
RG
99 bool alreadyWatched = d_writeCallbacks.count(fd) > 0;
100
101 if (alreadyWatched) {
510d368d 102 this->alterFD(fd, EventKind::Write, EventKind::Both);
e16e673d
RG
103 }
104 else {
105 this->addFD(fd, EventKind::Read);
106 }
107
108 /* do the addition _after_ so the entry is not added if there is an error */
c92e6020 109 accountingAddFD(d_readCallbacks, fd, std::move(toDo), parameter, ttd);
ab3e8a6c
BH
110 }
111
a1dfcec8 112 //! Add an fd to the write watch list - currently an fd can only be on one list at a time!
e16e673d 113 void addWriteFD(int fd, callbackfunc_t toDo, const funcparam_t& parameter = funcparam_t(), const struct timeval* ttd = nullptr)
ab3e8a6c 114 {
e16e673d
RG
115 bool alreadyWatched = d_readCallbacks.count(fd) > 0;
116
117 if (alreadyWatched) {
510d368d 118 this->alterFD(fd, EventKind::Read, EventKind::Both);
e16e673d
RG
119 }
120 else {
121 this->addFD(fd, EventKind::Write);
122 }
123
124 /* do the addition _after_ so the entry is not added if there is an error */
c92e6020 125 accountingAddFD(d_writeCallbacks, fd, std::move(toDo), parameter, ttd);
ab3e8a6c
BH
126 }
127
a1dfcec8 128 //! Remove an fd from the read watch list. You can't call this function on an fd that is closed already!
6dcd28c3 129 /** WARNING: references to 'parameter' become invalid after this function! */
e16e673d 130 void removeReadFD(int fd)
ab3e8a6c 131 {
e16e673d
RG
132 const auto& iter = d_writeCallbacks.find(fd);
133 accountingRemoveFD(d_readCallbacks, fd);
134
135 if (iter != d_writeCallbacks.end()) {
510d368d 136 this->alterFD(fd, EventKind::Both, EventKind::Write);
e16e673d
RG
137 }
138 else {
139 this->removeFD(fd, EventKind::Read);
140 }
ab3e8a6c
BH
141 }
142
a1dfcec8 143 //! Remove an fd from the write watch list. You can't call this function on an fd that is closed already!
6dcd28c3 144 /** WARNING: references to 'parameter' become invalid after this function! */
e16e673d 145 void removeWriteFD(int fd)
ab3e8a6c 146 {
e16e673d
RG
147 const auto& iter = d_readCallbacks.find(fd);
148 accountingRemoveFD(d_writeCallbacks, fd);
149
150 if (iter != d_readCallbacks.end()) {
510d368d 151 this->alterFD(fd, EventKind::Both, EventKind::Read);
e16e673d
RG
152 }
153 else {
154 this->removeFD(fd, EventKind::Write);
155 }
ab3e8a6c
BH
156 }
157
e16e673d 158 void setReadTTD(int fd, struct timeval tv, int timeout)
0bff046b 159 {
27ae2e3c
RG
160 const auto& it = d_readCallbacks.find(fd);
161 if (it == d_readCallbacks.end()) {
0bff046b 162 throw FDMultiplexerException("attempt to timestamp fd not in the multiplexer");
27ae2e3c
RG
163 }
164
ac3da0c2 165 auto newEntry = *it;
0bff046b 166 tv.tv_sec += timeout;
ac3da0c2
RG
167 newEntry.d_ttd = tv;
168 d_readCallbacks.replace(it, newEntry);
0bff046b
BH
169 }
170
e16e673d 171 void setWriteTTD(int fd, struct timeval tv, int timeout)
702b1925
RG
172 {
173 const auto& it = d_writeCallbacks.find(fd);
174 if (it == d_writeCallbacks.end()) {
175 throw FDMultiplexerException("attempt to timestamp fd not in the multiplexer");
176 }
177
ac3da0c2 178 auto newEntry = *it;
702b1925 179 tv.tv_sec += timeout;
ac3da0c2
RG
180 newEntry.d_ttd = tv;
181 d_writeCallbacks.replace(it, newEntry);
a6ae6414
BH
182 }
183
e16e673d 184 void alterFDToRead(int fd, callbackfunc_t toDo, const funcparam_t& parameter = funcparam_t(), const struct timeval* ttd = nullptr)
92329526 185 {
e16e673d 186 accountingRemoveFD(d_writeCallbacks, fd);
510d368d 187 this->alterFD(fd, EventKind::Write, EventKind::Read);
c92e6020 188 accountingAddFD(d_readCallbacks, fd, std::move(toDo), parameter, ttd);
92329526
RG
189 }
190
e16e673d 191 void alterFDToWrite(int fd, callbackfunc_t toDo, const funcparam_t& parameter = funcparam_t(), const struct timeval* ttd = nullptr)
92329526 192 {
e16e673d 193 accountingRemoveFD(d_readCallbacks, fd);
510d368d 194 this->alterFD(fd, EventKind::Read, EventKind::Write);
c92e6020 195 accountingAddFD(d_writeCallbacks, fd, std::move(toDo), parameter, ttd);
92329526
RG
196 }
197
e16e673d 198 std::vector<std::pair<int, funcparam_t>> getTimeouts(const struct timeval& tv, bool writes = false)
0bff046b 199 {
e16e673d 200 std::vector<std::pair<int, funcparam_t>> ret;
905dae56 201 const auto tied = std::tie(tv.tv_sec, tv.tv_usec);
ac3da0c2 202 auto& idx = writes ? d_writeCallbacks.get<TTDOrderedTag>() : d_readCallbacks.get<TTDOrderedTag>();
27ae2e3c 203
ac3da0c2 204 for (auto it = idx.begin(); it != idx.end(); ++it) {
905dae56 205 if (it->d_ttd.tv_sec == 0 || tied <= std::tie(it->d_ttd.tv_sec, it->d_ttd.tv_usec)) {
ac3da0c2 206 break;
27ae2e3c 207 }
e32a8d46 208 ret.emplace_back(it->d_fd, it->d_parameter);
27ae2e3c
RG
209 }
210
0bff046b
BH
211 return ret;
212 }
213
7c58a81f 214 typedef FDMultiplexer* getMultiplexer_t(unsigned int);
1f4abb20
BH
215 typedef std::multimap<int, getMultiplexer_t*> FDMultiplexermap_t;
216
217 static FDMultiplexermap_t& getMultiplexerMap()
218 {
219 static FDMultiplexermap_t theMap;
220 return theMap;
221 }
e16e673d 222
5bdbb83d 223 virtual std::string getName() const = 0;
1f4abb20 224
696e32f5
RG
225 size_t getWatchedFDCount(bool writeFDs) const
226 {
227 return writeFDs ? d_writeCallbacks.size() : d_readCallbacks.size();
228 }
229
e16e673d 230 void runForAllWatchedFDs(void (*watcher)(bool isRead, int fd, const funcparam_t&, struct timeval))
f75b8a3b
RG
231 {
232 for (const auto& entry : d_readCallbacks) {
233 watcher(true, entry.d_fd, entry.d_parameter, entry.d_ttd);
234 }
235 for (const auto& entry : d_writeCallbacks) {
236 watcher(false, entry.d_fd, entry.d_parameter, entry.d_ttd);
237 }
238 }
239
ab3e8a6c 240protected:
e16e673d
RG
241 struct FDBasedTag
242 {
243 };
244 struct TTDOrderedTag
245 {
246 };
ac3da0c2
RG
247 struct ttd_compare
248 {
249 /* we want a 0 TTD (no timeout) to come _after_ everything else */
e16e673d 250 bool operator()(const struct timeval& lhs, const struct timeval& rhs) const
ac3da0c2
RG
251 {
252 /* special treatment if at least one of the TTD is 0,
253 normal comparison otherwise */
254 if (lhs.tv_sec == 0 && rhs.tv_sec == 0) {
255 return false;
256 }
257 if (lhs.tv_sec == 0 && rhs.tv_sec != 0) {
258 return false;
259 }
260 if (lhs.tv_sec != 0 && rhs.tv_sec == 0) {
261 return true;
262 }
263
264 return std::tie(lhs.tv_sec, lhs.tv_usec) < std::tie(rhs.tv_sec, rhs.tv_usec);
265 }
266 };
267
268 typedef multi_index_container<
269 Callback,
e16e673d
RG
270 indexed_by<
271 hashed_unique<tag<FDBasedTag>,
272 member<Callback, int, &Callback::d_fd>>,
273 ordered_non_unique<tag<TTDOrderedTag>,
274 member<Callback, struct timeval, &Callback::d_ttd>,
275 ttd_compare>>>
276 callbackmap_t;
ac3da0c2 277
ab3e8a6c 278 callbackmap_t d_readCallbacks, d_writeCallbacks;
ab3e8a6c 279 bool d_inrun;
c454d11b 280
e16e673d 281 void accountingAddFD(callbackmap_t& cbmap, int fd, callbackfunc_t toDo, const funcparam_t& parameter, const struct timeval* ttd)
c454d11b
BH
282 {
283 Callback cb;
ac3da0c2 284 cb.d_fd = fd;
c92e6020 285 cb.d_callback = std::move(toDo);
e16e673d 286 cb.d_parameter = parameter;
c454d11b 287 memset(&cb.d_ttd, 0, sizeof(cb.d_ttd));
27ae2e3c
RG
288 if (ttd) {
289 cb.d_ttd = *ttd;
290 }
291
92329526 292 auto pair = cbmap.insert(std::move(cb));
27ae2e3c 293 if (!pair.second) {
e16e673d 294 throw FDMultiplexerException("Tried to add fd " + std::to_string(fd) + " to multiplexer twice");
27ae2e3c 295 }
c454d11b
BH
296 }
297
e16e673d 298 void accountingRemoveFD(callbackmap_t& cbmap, int fd)
c454d11b 299 {
e16e673d
RG
300 if (!cbmap.erase(fd)) {
301 throw FDMultiplexerException("Tried to remove unlisted fd " + std::to_string(fd) + " from multiplexer");
27ae2e3c 302 }
c454d11b 303 }
92329526 304
e16e673d
RG
305 virtual void addFD(int fd, EventKind kind) = 0;
306 /* most implementations do not care about which event has to be removed, except for kqueue */
307 virtual void removeFD(int fd, EventKind kind) = 0;
510d368d
RG
308 /* most implementations do not care about which event has to be removed, except for kqueue */
309 virtual void alterFD(int fd, EventKind from, EventKind to)
92329526
RG
310 {
311 /* naive implementation */
510d368d
RG
312 removeFD(fd, from);
313 addFD(fd, to);
92329526 314 }
1f4abb20 315};