]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/mplexer.hh
Limit the number of queries sent out to get NS addresses per query.
[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
BH
23#include <boost/function.hpp>
24#include <boost/any.hpp>
a1dfcec8 25#include <boost/shared_array.hpp>
0bff046b
BH
26#include <boost/tuple/tuple.hpp>
27#include <boost/tuple/tuple_comparison.hpp>
ac3da0c2
RG
28#include <boost/multi_index_container.hpp>
29#include <boost/multi_index/ordered_index.hpp>
30#include <boost/multi_index/hashed_index.hpp>
31#include <boost/multi_index/key_extractors.hpp>
0bff046b 32#include <vector>
ab3e8a6c
BH
33#include <map>
34#include <stdexcept>
35#include <string>
f1d51ff7 36#include <sys/time.h>
ab3e8a6c 37
ac3da0c2
RG
38using namespace ::boost::multi_index;
39
ab3e8a6c
BH
40class FDMultiplexerException : public std::runtime_error
41{
42public:
43 FDMultiplexerException(const std::string& str) : std::runtime_error(str)
44 {}
45};
46
a1dfcec8
BH
47
48/** Very simple FD multiplexer, based on callbacks and boost::any parameters
49 As a special service, this parameter is kept around and can be modified,
50 allowing for state to be stored inside the multiplexer.
51
52 It has some "interesting" semantics
53*/
1f4abb20 54
ab3e8a6c
BH
55class FDMultiplexer
56{
d8f6d49f 57public:
d8f6d49f 58 typedef boost::any funcparam_t;
d0ae6360 59 typedef boost::function< void(int, funcparam_t&) > callbackfunc_t;
ab3e8a6c 60protected:
d8f6d49f 61
ab3e8a6c
BH
62 struct Callback
63 {
64 callbackfunc_t d_callback;
ac3da0c2 65 mutable funcparam_t d_parameter;
0bff046b 66 struct timeval d_ttd;
ac3da0c2 67 int d_fd;
ab3e8a6c
BH
68 };
69
70public:
71 FDMultiplexer() : d_inrun(false)
72 {}
73 virtual ~FDMultiplexer()
74 {}
75
4226cfd0 76 static FDMultiplexer* getMultiplexerSilent();
77
0e663c3b
RG
78 /* tv will be updated to 'now' before run returns */
79 /* timeout is in ms */
dd9c8246
RG
80 /* returns 0 on timeout, -1 in case of error (but all implementations
81 actually throw in that case) and the number of ready events otherwise */
0e663c3b 82 virtual int run(struct timeval* tv, int timeout=500) = 0;
ab3e8a6c 83
dd9c8246 84 /* timeout is in ms, 0 will return immediately, -1 will block until at least one FD is ready */
5bdbb83d
RG
85 virtual void getAvailableFDs(std::vector<int>& fds, int timeout) = 0;
86
a1dfcec8 87 //! Add an fd to the read watch list - currently an fd can only be on one list at a time!
27ae2e3c 88 virtual void addReadFD(int fd, callbackfunc_t toDo, const funcparam_t& parameter=funcparam_t(), const struct timeval* ttd=nullptr)
ab3e8a6c 89 {
27ae2e3c 90 this->addFD(d_readCallbacks, fd, toDo, parameter, ttd);
ab3e8a6c
BH
91 }
92
a1dfcec8 93 //! Add an fd to the write watch list - currently an fd can only be on one list at a time!
702b1925 94 virtual void addWriteFD(int fd, callbackfunc_t toDo, const funcparam_t& parameter=funcparam_t(), const struct timeval* ttd=nullptr)
ab3e8a6c 95 {
702b1925 96 this->addFD(d_writeCallbacks, fd, toDo, parameter, ttd);
ab3e8a6c
BH
97 }
98
a1dfcec8 99 //! Remove an fd from the read watch list. You can't call this function on an fd that is closed already!
6dcd28c3 100 /** WARNING: references to 'parameter' become invalid after this function! */
ab3e8a6c
BH
101 virtual void removeReadFD(int fd)
102 {
a1dfcec8 103 this->removeFD(d_readCallbacks, fd);
ab3e8a6c
BH
104 }
105
a1dfcec8 106 //! Remove an fd from the write watch list. You can't call this function on an fd that is closed already!
6dcd28c3 107 /** WARNING: references to 'parameter' become invalid after this function! */
a1dfcec8 108 virtual void removeWriteFD(int fd)
ab3e8a6c 109 {
a1dfcec8 110 this->removeFD(d_writeCallbacks, fd);
ab3e8a6c
BH
111 }
112
0bff046b
BH
113 virtual void setReadTTD(int fd, struct timeval tv, int timeout)
114 {
27ae2e3c
RG
115 const auto& it = d_readCallbacks.find(fd);
116 if (it == d_readCallbacks.end()) {
0bff046b 117 throw FDMultiplexerException("attempt to timestamp fd not in the multiplexer");
27ae2e3c
RG
118 }
119
ac3da0c2 120 auto newEntry = *it;
0bff046b 121 tv.tv_sec += timeout;
ac3da0c2
RG
122 newEntry.d_ttd = tv;
123 d_readCallbacks.replace(it, newEntry);
0bff046b
BH
124 }
125
702b1925
RG
126 virtual void setWriteTTD(int fd, struct timeval tv, int timeout)
127 {
128 const auto& it = d_writeCallbacks.find(fd);
129 if (it == d_writeCallbacks.end()) {
130 throw FDMultiplexerException("attempt to timestamp fd not in the multiplexer");
131 }
132
ac3da0c2 133 auto newEntry = *it;
702b1925 134 tv.tv_sec += timeout;
ac3da0c2
RG
135 newEntry.d_ttd = tv;
136 d_writeCallbacks.replace(it, newEntry);
a6ae6414
BH
137 }
138
702b1925 139 virtual std::vector<std::pair<int, funcparam_t> > getTimeouts(const struct timeval& tv, bool writes=false)
0bff046b 140 {
d8f6d49f 141 std::vector<std::pair<int, funcparam_t> > ret;
ac3da0c2
RG
142 const auto tied = boost::tie(tv.tv_sec, tv.tv_usec);
143 auto& idx = writes ? d_writeCallbacks.get<TTDOrderedTag>() : d_readCallbacks.get<TTDOrderedTag>();
27ae2e3c 144
ac3da0c2
RG
145 for (auto it = idx.begin(); it != idx.end(); ++it) {
146 if (it->d_ttd.tv_sec == 0 || tied <= boost::tie(it->d_ttd.tv_sec, it->d_ttd.tv_usec)) {
147 break;
27ae2e3c 148 }
ac3da0c2 149 ret.push_back(std::make_pair(it->d_fd, it->d_parameter));
27ae2e3c
RG
150 }
151
0bff046b
BH
152 return ret;
153 }
154
1f4abb20
BH
155 typedef FDMultiplexer* getMultiplexer_t();
156 typedef std::multimap<int, getMultiplexer_t*> FDMultiplexermap_t;
157
158 static FDMultiplexermap_t& getMultiplexerMap()
159 {
160 static FDMultiplexermap_t theMap;
161 return theMap;
162 }
163
5bdbb83d 164 virtual std::string getName() const = 0;
1f4abb20 165
696e32f5
RG
166 size_t getWatchedFDCount(bool writeFDs) const
167 {
168 return writeFDs ? d_writeCallbacks.size() : d_readCallbacks.size();
169 }
170
ab3e8a6c 171protected:
ac3da0c2
RG
172 struct FDBasedTag {};
173 struct TTDOrderedTag {};
174 struct ttd_compare
175 {
176 /* we want a 0 TTD (no timeout) to come _after_ everything else */
177 bool operator() (const struct timeval& lhs, const struct timeval& rhs) const
178 {
179 /* special treatment if at least one of the TTD is 0,
180 normal comparison otherwise */
181 if (lhs.tv_sec == 0 && rhs.tv_sec == 0) {
182 return false;
183 }
184 if (lhs.tv_sec == 0 && rhs.tv_sec != 0) {
185 return false;
186 }
187 if (lhs.tv_sec != 0 && rhs.tv_sec == 0) {
188 return true;
189 }
190
191 return std::tie(lhs.tv_sec, lhs.tv_usec) < std::tie(rhs.tv_sec, rhs.tv_usec);
192 }
193 };
194
195 typedef multi_index_container<
196 Callback,
197 indexed_by <
198 hashed_unique<tag<FDBasedTag>,
199 member<Callback,int,&Callback::d_fd>
200 >,
201 ordered_non_unique<tag<TTDOrderedTag>,
202 member<Callback,struct timeval,&Callback::d_ttd>,
203 ttd_compare
204 >
205 >
206 > callbackmap_t;
207
ab3e8a6c 208 callbackmap_t d_readCallbacks, d_writeCallbacks;
ab3e8a6c 209
27ae2e3c 210 virtual void addFD(callbackmap_t& cbmap, int fd, callbackfunc_t toDo, const funcparam_t& parameter, const struct timeval* ttd=nullptr)=0;
ab3e8a6c
BH
211 virtual void removeFD(callbackmap_t& cbmap, int fd)=0;
212 bool d_inrun;
a1dfcec8 213 callbackmap_t::iterator d_iter;
c454d11b 214
27ae2e3c 215 void accountingAddFD(callbackmap_t& cbmap, int fd, callbackfunc_t toDo, const funcparam_t& parameter, const struct timeval* ttd=nullptr)
c454d11b
BH
216 {
217 Callback cb;
ac3da0c2 218 cb.d_fd = fd;
c454d11b
BH
219 cb.d_callback=toDo;
220 cb.d_parameter=parameter;
221 memset(&cb.d_ttd, 0, sizeof(cb.d_ttd));
27ae2e3c
RG
222 if (ttd) {
223 cb.d_ttd = *ttd;
224 }
225
ac3da0c2 226 auto pair = cbmap.insert(cb);
27ae2e3c 227 if (!pair.second) {
335da0ba 228 throw FDMultiplexerException("Tried to add fd "+std::to_string(fd)+ " to multiplexer twice");
27ae2e3c 229 }
c454d11b
BH
230 }
231
232 void accountingRemoveFD(callbackmap_t& cbmap, int fd)
233 {
27ae2e3c 234 if(!cbmap.erase(fd)) {
335da0ba 235 throw FDMultiplexerException("Tried to remove unlisted fd "+std::to_string(fd)+ " from multiplexer");
27ae2e3c 236 }
c454d11b 237 }
1f4abb20 238};