-
Notifications
You must be signed in to change notification settings - Fork 923
/
Copy pathinflighter.cc
274 lines (231 loc) · 7.42 KB
/
inflighter.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
/*
* This file is part of PowerDNS or dnsdist.
* Copyright -- PowerDNS.COM B.V. and its contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* In addition, for the avoidance of any doubt, permission is granted to
* link this program with OpenSSL and to (re)distribute the binaries
* produced as the result of such linking.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <vector>
#include <iostream>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/key_extractors.hpp>
#include <boost/format.hpp>
#include <sys/time.h>
#include <time.h>
#include "iputils.hh"
#include "statbag.hh"
#include <sys/socket.h>
#include "namespaces.hh"
using namespace boost::multi_index;
struct TimeTag{};
template<typename Container, typename SenderReceiver> class Inflighter
{
public:
Inflighter(Container& c, SenderReceiver& sr) :
d_container(c), d_sr(sr)
{
d_burst = 2;
d_maxInFlight = 5;
d_timeoutSeconds = 3;
d_unexpectedResponse = d_timeouts = 0;
}
void init()
{
d_iter = d_container.begin();
d_init=true;
}
bool run(); //!< keep calling this as long as it returns 1, or if it throws an exception
unsigned int d_maxInFlight;
unsigned int d_timeoutSeconds;
int d_burst;
uint64_t getTimeouts()
{
return d_timeouts;
}
uint64_t getUnexpecteds()
{
return d_unexpectedResponse;
}
private:
struct TTDItem
{
typename Container::iterator iter;
typename SenderReceiver::Identifier id;
struct timeval sentTime, ttd;
};
typedef multi_index_container<
TTDItem,
indexed_by<
ordered_unique<
member<TTDItem, typename SenderReceiver::Identifier, &TTDItem::id>
>,
ordered_non_unique<
tag<TimeTag>,
member<TTDItem, struct timeval, &TTDItem::ttd>
>
>
>ttdwatch_t;
Container& d_container;
SenderReceiver& d_sr;
ttdwatch_t d_ttdWatch;
typename Container::iterator d_iter;
bool d_init{false};
uint64_t d_unexpectedResponse, d_timeouts;
};
template<typename Container, typename SendReceive> bool Inflighter<Container, SendReceive>::run()
{
if(!d_init)
init();
for(;;) {
int burst = 0;
// 'send' as many items as allowed, limited by 'max in flight' and our burst parameter (which limits query rate growth)
while(d_iter != d_container.end() && d_ttdWatch.size() < d_maxInFlight) {
TTDItem ttdi;
ttdi.iter = d_iter++;
ttdi.id = d_sr.send(*ttdi.iter);
gettimeofday(&ttdi.sentTime, 0);
ttdi.ttd = ttdi.sentTime;
ttdi.ttd.tv_sec += d_timeoutSeconds;
if(d_ttdWatch.count(ttdi.id)) {
// cerr<<"DUPLICATE INSERT!"<<endl;
}
d_ttdWatch.insert(ttdi);
if(++burst == d_burst)
break;
}
int processed=0;
// if there are queries in flight, handle responses
if(!d_ttdWatch.empty()) {
// cerr<<"Have "<< d_ttdWatch.size() <<" queries in flight"<<endl;
typename SendReceive::Answer answer;
typename SendReceive::Identifier id;
// get as many answers as available - 'receive' should block for a short while to wait for an answer
while(d_sr.receive(id, answer)) {
typename ttdwatch_t::iterator ival = d_ttdWatch.find(id); // match up what we received to what we were waiting for
if(ival != d_ttdWatch.end()) { // found something!
++processed;
struct timeval now;
gettimeofday(&now, 0);
unsigned int usec = 1000000*(now.tv_sec - ival->sentTime.tv_sec) + (now.tv_usec - ival->sentTime.tv_usec);
d_sr.deliverAnswer(*ival->iter, answer, usec); // deliver to sender/receiver
d_ttdWatch.erase(ival);
break; // we can send new questions!
}
else {
// cerr<<"UNEXPECTED ANSWER: "<<id<<endl;
d_unexpectedResponse++;
}
}
if(!processed /* || d_ttdWatch.size() > 10000 */ ) { // no new responses, time for some cleanup of the ttdWatch
struct timeval now;
gettimeofday(&now, 0);
typedef typename ttdwatch_t::template index<TimeTag>::type waiters_by_ttd_index_t;
waiters_by_ttd_index_t& waiters_index = boost::multi_index::get<TimeTag>(d_ttdWatch);
// this provides a list of items sorted by age
for(typename waiters_by_ttd_index_t::iterator valiter = waiters_index.begin(); valiter != waiters_index.end(); ) {
if(valiter->ttd.tv_sec < now.tv_sec || (valiter->ttd.tv_sec == now.tv_sec && valiter->ttd.tv_usec < now.tv_usec)) {
d_sr.deliverTimeout(valiter->id); // so backend can release id
waiters_index.erase(valiter++);
// cerr<<"Have timeout for id="<< valiter->id <<endl;
d_timeouts++;
}
else
break; // if this one was too new, rest will be too
}
}
}
if(d_ttdWatch.empty() && d_iter == d_container.end())
break;
}
return false;
}
#if 0
StatBag S;
struct SendReceive
{
typedef int Identifier;
typedef int Answer;
ComboAddress d_remote;
int d_socket;
int d_id;
SendReceive()
{
d_id = 0;
d_socket = socket(AF_INET, SOCK_DGRAM, 0);
int val=1;
setsockopt(d_socket, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
ComboAddress local("0.0.0.0", 1024);
bind(d_socket, (struct sockaddr*)&local, local.getSocklen());
char buf[512];
socklen_t remotelen=sizeof(d_remote);
cerr<<"Waiting for 'hi' on "<<local.toStringWithPort()<<endl;
int len = recvfrom(d_socket, buf, sizeof(buf), 0, (struct sockaddr*)&d_remote, &remotelen);
cerr<<d_remote.toStringWithPort()<<" sent 'hi': "<<string(buf, len);
Utility::setNonBlocking(d_socket);
connect(d_socket, (struct sockaddr*) &d_remote, d_remote.getSocklen());
}
~SendReceive()
{
::send(d_socket, "done\r\n", 6, 0);
}
Identifier send(int& i)
{
cerr<<"Sending a '"<<i<<"'"<<endl;
string msg = (boost::format("%d %d\n") % d_id % i).str();
::send(d_socket, msg.c_str(), msg.length(), 0);
return d_id++;
}
bool receive(Identifier& id, int& i)
{
if(waitForData(d_socket, 0, 500000) > 0) {
char buf[512];
int len = recv(d_socket, buf, sizeof(buf), 0);
string msg(buf, len);
if(sscanf(msg.c_str(), "%d %d", &id, &i) != 2) {
throw runtime_error("Invalid input");
}
return 1;
}
return 0;
}
void deliverAnswer(int& i, int j)
{
cerr<<"We sent "<<i<<", got back: "<<j<<endl;
}
};
int main()
{
vector<int> numbers;
SendReceive sr;
Inflighter<vector<int>, SendReceive> inflighter(numbers, sr);
for(int n=0; n < 100; ++n)
numbers.push_back(n*n);
for(;;) {
try {
inflighter.run();
break;
}
catch(exception& e) {
cerr<<"Caught exception: "<<e.what()<<endl;
}
}
}
#endif