Flow 1.0.2
Flow project: Full implementation reference.
drop_timer.cpp
Go to the documentation of this file.
1/* Flow
2 * Copyright 2023 Akamai Technologies, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the
5 * "License"); you may not use this file except in
6 * compliance with the License. You may obtain a copy
7 * of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in
12 * writing, software distributed under the License is
13 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
14 * CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing
16 * permissions and limitations under the License. */
17
18/// @file
20#include "flow/util/util.hpp"
21
22namespace flow::net_flow
23{
24
25// Implementations.
26
28 (log::Logger* logger_ptr,
29 util::Task_engine* node_task_engine,
30 Fine_duration* sock_drop_timeout,
32 const Function<void (const Error_code& err_code)>& timer_failure,
33 const Function<void (bool drop_all_packets)>& timer_fired) // Static.
34{
35 // See doc comment for rationale.
36 return Ptr(new Drop_timer(logger_ptr,
37 node_task_engine, sock_drop_timeout, std::move(sock),
38 timer_failure, timer_fired));
39}
40
42{
43 FLOW_LOG_TRACE("Drop_timer [" << this << "]: contemporary events group: START.");
44
45 assert(!m_in_events_group);
46 m_in_events_group = true;
47
50}
51
53{
54 using std::pair;
55 using boost::prior;
56 using std::set;
57
58 FLOW_LOG_TRACE("Drop_timer [" << this << "]: packet [" << packet_id << "] now In-flight.");
59
60 assert(m_in_events_group);
61
62#ifndef NDEBUG
63 const auto insert_result =
64#endif
65 m_flying_packets.insert(packet_id);
66 // packet_id must never be reused (method contract).
67 assert(insert_result.second);
68 // packet_id must always be increasing (method contract).
69 assert(insert_result.first == prior(m_flying_packets.end()));
70}
71
73{
74 using std::pair;
75 using boost::prior;
76
77 FLOW_LOG_TRACE("Drop_timer [" << this << "]: packet [" << packet_id << "] acknowledged.");
78
79 assert(m_in_events_group);
80
81 assert(util::key_exists(m_flying_packets, packet_id));
82
83 // Keep track of the newest (right-most) acked packet. Starts off at 0 (reserved -- and also min. possible -- value).
85 {
86 FLOW_LOG_TRACE("Drop_timer [" << this << "]: acknowledged packet [" << packet_id << "] replaces "
87 "[" << m_during_events_newest_acked_packet << "] as right-most acknowledged packet.");
89 }
90}
91
93{
94 FLOW_LOG_TRACE("Drop_timer [" << this << "]: packet [" << packet_id << "] now no longer In-flight.");
95
96 assert(m_in_events_group);
97
98#ifndef NDEBUG
99 const size_t num_erased =
100#endif
101 m_flying_packets.erase(packet_id);
102 assert(num_erased == 1);
103}
104
106{
107 FLOW_LOG_TRACE("Drop_timer [" << this << "]: all packets now no longer In-flight. Send pipe empty.");
108
109 assert(m_in_events_group);
110
111 /* As advertised. (This could be seen as unnecessarily pedantic... but algorithmic tightness is good. This is no
112 * public API but is inner code instead.) */
113 assert(!m_flying_packets.empty());
114
115 m_flying_packets.clear();
116}
117
119{
120 FLOW_LOG_TRACE("Drop_timer [" << this << "]: contemporary events group: END.");
121
122 assert(m_in_events_group);
123 m_in_events_group = false;
124
125 /* Now that the events-tracking time period is done, we check for the various events of interest that affect
126 * the timer itself. Essentially, we treat the on_*() calls since start_contemporaneous_events() up until now
127 * as one operation. By comparing the situation "then" to "now," we detect the various conditions. */
128
129 if (m_at_events_start_oldest_flying_packet == 0) // I.e., m_flying_packets.empty() was true at start_...() time.
130 {
131 if (!m_flying_packets.empty())
132 {
133 FLOW_LOG_TRACE("Drop_timer [" << this << "]: send pipe no longer empty (first packet In-flight).");
134
135 /* Sent the first packet after nothing In-flight, so timer should be off. Simply start it with
136 * current DTO. RFC 6298 and common sense support this. Note, though, that we're slightly different, in that
137 * we react to packet actually being sent off vs. merely being about to be sent off pending pacing. */
138 start_timer();
139 }
140 }
141 else if (m_flying_packets.empty()) // I.e., m_flying_packets.empty() went from false (then) to true (now).
142 {
143 FLOW_LOG_TRACE("Drop_timer [" << this << "]: send pipe empty.");
144
145 /* Timer should be on, but last packet(s) have been acked or Dropped -- nothing else to wait
146 * for; so just turn the timer off. RFC 6298 and common sense support this. */
148 }
149 else // I.e., m_flying_packets.empty() was false (then) -- and still is (now).
150 {
151 const packet_id_t at_events_end_oldest_packet = *m_flying_packets.begin();
152
153 if (at_events_end_oldest_packet != m_at_events_start_oldest_flying_packet)
154 {
155 // A different packet is oldest (now) vs. oldest packet (then). The latter must have been Acked or Dropped.
156
157 FLOW_LOG_TRACE("Drop_timer [" << this << "]: send pipe's left edge moved to the right.");
158
159 /* Timer should be on, but the earliest unacked packet has been Acknowledged or
160 * Dropped; an In-flight packet remains. In all reasonable algorithms the timer has to be
161 * restarted in this situation. RFC 6298 and common sense support this.
162 *
163 * (Otherwise suppose I've sent packets 1-10, one after the previous with 1 second between them.
164 * Thus I started timer at time 0 (when packet 1 was sent, since it was the first packet to be
165 * sent) and sent packet 10 ten seconds later. Suppose DTO is 30 seconds, a second passes since
166 * packet 10 is passed, and packet 1 is acked. If I don't restart the timer now, then that
167 * means (for example) packet 10 will have had ~10 seconds less (overall, relative to send time)
168 * than packet 1 before being Dropped. This violates the RFC 6299 section 5 rule that the
169 * algorithm MUST allow at least a full RTO (DTO) since a given packet is sent before dropping
170 * that packet.) */
172 start_timer();
173 }
175 {
176 /* We have detected an out-of-order acknowledgment, which may mean we should restart time.
177 * Let's discuss in detail.
178 *
179 * Firstly, note that unlike various cases covered so far, this situation is NOT mutually exclusive w/r/t
180 * the pipe-left-edge-moved-right situation covered just above. However, since the potential action
181 * for both situations is to restart the timer, it would be pointless to check for the 2nd event, if the
182 * 1st is already true. So that's why it's an 'else if.' So, from now on, assert that in fact
183 * at_events_end_oldest_packet == m_at_events_start_oldest_flying_packet. In fact this makes the following
184 * reasoning quite simple vs. the alternative (had we not asserted that).
185 *
186 * Now, for what did we just check? 0 != m_during_events_newest_acked_packet
187 * means that, during the events group, the pipe's left edge did not move (previous paragraph again), but
188 * _a_ packet in the pipe was acknowledged (it must therefore no longer be in pipe). In fact this packet
189 * was "to the right of" (sent more recently than) that left-edge packet.
190 * This means at least one packet was acknowledged, but it wasn't the one that's "due" for acknowledgment
191 * (namely the packet m_at_events_start_oldest_flying_packet == at_events_end_oldest_packet). So what do
192 * we do based on this information?
193 *
194 * We can do one of two things here: (1) nothing (let timer run); or (2) restart the timer.
195 * To be clear, (1) comes from a certain classic RFC mentioned just below. We can either follow that RFC's
196 * recommendatio (restart timer here) or not (do nothing). Here are the arguments for each:
197 *
198 * -1- Action: Let timer run (thus giving the earliest packet no recourse: do not follow RFC):
199 *
200 * Pro: If we assume that, essentially, we're simulating a DTO for each individual packet,
201 * then having received acknowledgment for some packet P in no way means that an earlier
202 * packet P' was also acknowledged. I.e., if our one timer is meant to apply to the
203 * next-to-be-dropped (earliest unacked) packet only -- since the other packets' implied
204 * timers can only fire after that one -- then a later packet being acked doesn't negate
205 * the fate that the earliest packet is still outstanding. In other words restarting the
206 * timer here is more lax than the "Drop Timeout per individual packet" setup, thus we
207 * are being more precise w/r/t to the concept of the timer by letting it run.
208 * Pro: RFC 6298-5.3 says "[if] ACK is received that acknowledges new data, restart the
209 * retransmission timer." This can be interpreted to mean a CUMULATIVE ACK that
210 * acknowledges new data, as opposed to an out-of-order SACK (which is what we have
211 * here). I am not sure if the RFC means that. But if it means the former (cumulative ack),
212 * then we're NOT going against the RFC, which is a pro... well, not a con at least.
213 * Con: Assuming the RFC section 5-RECOMMENDED algorithm *doest* mean that SACKs = acknowledging "new
214 * data" (quoting RFC) as well, then we are being MORE aggressive than the RFC algorithm -- and
215 * being more aggressive could be bad and/or wrong.
216 *
217 * -2- Action: Restart the timer (thus giving the earliest packet a new lease on life: follow RFC):
218 *
219 * Pro: RFC 6298-5.3 may mean that either an ACK or a SACK acknowledging any unacked
220 * packets (whether in-order or out-of-order) means we should restart the timer. In that
221 * case we'd be directly following the RFC if restarting the timer.
222 * Pro: Even if not, by restarting the timer we are being LESS aggressive than the RFC, which
223 * is explicitly allowed by the RFC (we MUST not give any packet less than the DTO time,
224 * but we may give it more).
225 * Con: Restarting the timer is more lax than the "Drop Timeout per individual packet"
226 * setup, and the latter has an aesthetically pleasant logic to it.
227 *
228 * Therefore we make this configurable behavior. When in doubt and without experimental backup,
229 * do go for the less aggressive option (Restart the timer).
230 *
231 * @todo We didn't have to keep track of m_during_events_newest_acked_packet (the most-recently-sent
232 * packet to be acked). We could have just replaced it with bool m_during_events_ack_occurred, starting at false
233 * but set to true in any on_ack(). Since a (valid) ack always subsequently removes that packet from
234 * m_flying_packets, m_at_events_start_oldest_flying_packet still being in m_flying_packets implies that a packet
235 * other than the left-most one was in fact acked... which is all we are lookig for in the first place. However,
236 * keeping the packet ID allows us to, at least, sanity-check some things via assert(). We can also log some
237 * arguably useful packet IDs, e.g., in on_ack(). So arguably it's worth keeping. The performance cost looks
238 * minor (though this has not been profiled). */
239
240 FLOW_LOG_TRACE("Drop_timer [" << this << "]: out of order acknowledgment detected.");
241
242 // The following are all sanity checks to ensure situation is what we've reasoned out in comment just above.
243 assert(at_events_end_oldest_packet == m_at_events_start_oldest_flying_packet);
244 assert(at_events_end_oldest_packet < m_during_events_newest_acked_packet);
246
247 /* An ACK acknowledged at least one packet, but the earliest (first) In-flight packet was NOT
248 * acknowledged (and thus remains the earliest In-flight packet).
249 *
250 * We can do one of two things here: (1) nothing (let timer run); or (2) restart the timer.
251 * Here are the arguments for each:
252 *
253 * -1- Let timer run (thus giving the earliest packet no recourse):
254 *
255 * Pro: RFC 6298-5.3 says "ACK is received that acknowledges new data, restart the
256 * retransmission timer." This can be interpreted to mean a CUMULATIVE ACK that
257 * acknowledges new data, as opposed to an out-of-order SACK (which is what we have
258 * here). I am not sure if the RFC means that.
259 * Pro: If we assume that, essentially, we're simulating a DTO for each individual packet,
260 * then having received acknowledgment for some packet P in no way means that an earlier
261 * packet P' was also acknowledged. I.e., if our one timer is meant to apply to the
262 * next-to-be-dropped (earliest unacked) packet only -- since the other packets' implied
263 * timers can only fire after that one -- then a later packet being acked doesn't negate
264 * the fate that the earliest packet is still outstanding. In other words restarting the
265 * timer here is more lax than the "Drop Timeout per individual packet" setup, thus we
266 * are being more precise w/r/t to the concept of the timer by letting it run.
267 * Con: Assuming the RFC section 5 RECOMMENDED algorithm means that SACKs = acknowledging new
268 * data as well, then we are being MORE aggressive than the RFC algorithm which could be
269 * bad/incorrect.
270 *
271 * -2- Restart the timer (thus giving the earliest packet a new lease on life):
272 *
273 * Pro: RFC 6298-5.3 may mean that either an ACK or a SACK acknowledging any unacked
274 * packets (whether in-order or out-of-order) means we should restart the timer. In that
275 * case we'd be directly following the RFC if restarting the timer.
276 * Pro: Even if not, by restarting the timer we are being LESS aggressive than the RFC, which
277 * is explicitly allowed by the RFC (we MUST not give any packet less than the DTO time,
278 * but we may give it more).
279 * Con: Restarting the timer is more lax than the "Drop Timeout per individual packet"
280 * setup, and the latter has an aesthetically pleasant logic to it.
281 *
282 * Therefore we make this configurable behavior. When in doubt and without experimental backup,
283 * do go for the less aggressive option (Restart the timer). */
284 if (m_sock->opt(m_sock->m_opts.m_st_out_of_order_ack_restarts_drop_timer))
285 {
287 start_timer();
288 }
289 } // else if (m_during_events_newest_acked_packet != 0)
290 } // if (m_flying_packets.empty() was false -- and still is)
291} // Drop_timer::end_contemporaneous_events()
292
294 util::Task_engine* node_task_engine,
295 Fine_duration* sock_drop_timeout,
297 const Function<void (const Error_code& err_code)>& timer_failure,
298 const Function<void (bool drop_all_packets)>& timer_fired) :
299 log::Log_context(logger_ptr, Flow_log_component::S_NET_FLOW),
300 m_node_task_engine(*node_task_engine),
301 m_sock_drop_timeout(*sock_drop_timeout),
302 m_sock(std::move(sock)),
303 m_timer(m_node_task_engine),
304 m_timer_running(false),
305 m_current_wait_id(0),
306 m_done(false),
307 m_timer_failure(timer_failure),
308 m_timer_fired(timer_fired),
309 m_at_events_start_oldest_flying_packet(0),
310 m_during_events_newest_acked_packet(0),
311 m_in_events_group(false)
312{
313 FLOW_LOG_TRACE("Drop_timer [" << this << "] created.");
314}
315
317{
318 FLOW_LOG_TRACE("Drop_timer [" << this << "] destroyed.");
319}
320
322{
323 if (m_done)
324 {
325 return;
326 }
327
328 FLOW_LOG_TRACE("Drop_timer [" << this << "] permanently disabled.");
329
330 if (m_timer_running)
331 {
332 // If timer is scheduled, expedite it firing.
334 }
335 // Short-circuit any subsequent callbacks to immediately return.
336 m_done = true;
337
338 /* These function "pointers" will be "pointing" to some function objects that are holding
339 * arguments to the underlying methods to call, via labmda or bind(). These arguments will include
340 * such resources as Peer_socket::Ptr. Clear the "pointers" to delete the function objects, thus
341 * letting those resources expire (in case of Ptr, let the underlying Peer_socket's ref-count get
342 * decremented, avoiding a memory leak). */
343 m_timer_failure.clear();
344 m_timer_fired.clear();
345}
346
348{
349 using log::Sev;
350 using boost::asio::post;
351 using boost::chrono::microseconds;
352 using boost::chrono::milliseconds;
353 using boost::chrono::round;
354
355 // Explicitly documented pre-condition.
356 assert(!m_timer_running);
357
358 /* Determine when the timer will fire. Due to the interface of Timer, we need either a
359 * Fine_duration (for expires_from_now()) or a Fine_time_pt (for expires_at()): how long from now,
360 * or when in absolute terms, respectively.
361 *
362 * The obvious first choice would be simply the DTO from now, so
363 * expires_from_now(m_sock_drop_timeout), where the latter is a Fine_duration.
364 *
365 * A more complex setup we may want is: the timer firing in m_sock_drop_timeout time units since the time
366 * of the earliest (first) current In-flight packet was sent. Call that initial time point P. (P is a
367 * Fine_time_pt.) So this is: expires_at(P + m_sock_drop_timeout), where the terms are Fine_time_pt
368 * and Fine_duration, respectively.
369 *
370 * So which one should we use? RFC 6298 says use RTO (our DTO) from the current time. This is also the
371 * less aggressive choice, towards which the RFC is generally biased. On the other hand, if we want to give
372 * each given packet no less and NO MORE than DTO before Dropping, then we'd use the other policy. So, it's
373 * configurable. However when in doubt and in the absence of experimental data go with the less aggressive
374 * option, which is to just fire DTO from now, as opposed to from P. */
375
376 /* The time point of firing is either duration from now(), or absolute time point. The former is still
377 * computed for logging even if not used directly. */
378 Fine_duration fire_duration_vs_now; // Caution! This is uninitialized.
379 Fine_time_pt fire_time_pt;
380 bool use_time_pt_over_duration;
381 if (m_sock->opt(m_sock->m_opts.m_st_drop_packet_exactly_after_drop_timeout))
382 {
383 // The more aggressive choice. Fire in DTO but adjust by the time that has passed since earliest packet sent.
384
385 // Get time when earliest packet sent.
386 assert(!m_sock->m_snd_flying_pkts_by_sent_when.empty()); // Timer must not be started when no packets In-flight.
387 const Fine_time_pt& first_packet_sent_when
388 = m_sock->m_snd_flying_pkts_by_sent_when.const_back().second->m_sent_when.back().m_sent_time;
389
390 fire_time_pt = first_packet_sent_when + m_sock_drop_timeout;
391 use_time_pt_over_duration = true;
392
393 // Compute firing time for logging (skip it if logging macro will not actually log it though).
394 auto const logger_ptr = get_logger();
395 if (logger_ptr && logger_ptr->should_log(Sev::S_TRACE, get_log_component()))
396 {
397 fire_duration_vs_now = fire_time_pt - Fine_clock::now();
398 }
399 }
400 else
401 {
402 // The less aggressive choice. Just fire in DTO time units.
403 fire_duration_vs_now = m_sock_drop_timeout;
404 use_time_pt_over_duration = false;
405 }
406
407 FLOW_LOG_TRACE("Drop_timer [" << this << "] setting timer to "
408 "[" << round<milliseconds>(fire_duration_vs_now) << "] from now; "
409 "wait_id = [" << (m_current_wait_id + 1) << "].");
410
411 /* Note that fire_duration_vs_now may be somewhat negative, particularly in the
412 * m_st_drop_packet_exactly_after_drop_timeout case above after on_packet_no_longer_in_flight()'s
413 * if-left-most-In-flight-packet-has-shifted case.
414 * That's fine: timer will just fire as soon as m_task_engine gets a chance. It's no different
415 * from it just firing really soon due to a short timeout. Since each timer firing will cause
416 * Node to Drop one packet, this chain of events will finish in short order once it runs out of
417 * packets for which it should be firing. */
418
419 const auto this_ptr = shared_from_this();
420
421 // Set the firing time decided above.
422 Error_code sys_err_code;
423 use_time_pt_over_duration
424 ? m_timer.expires_at(fire_time_pt, sys_err_code)
425 : m_timer.expires_from_now(fire_duration_vs_now, sys_err_code);
426 if (sys_err_code)
427 {
428 FLOW_ERROR_SYS_ERROR_LOG_WARNING(); // Log the non-portable system error code/message.
429
430 // Pretty unlikely, but just leave timer disabled (!m_timer_running) and let the Node know.
431
432 // Call timer_failure() asynchronously. this_ptr will prevent "this" from getting deleted until timer_failure().
433 post(m_node_task_engine, [this, this_ptr]()
434 {
436 });
437 return;
438 }
439 // else can actually schedule it.
440
441 /* Call handle_timer_firing() asynchronously. this_wait_id will be used to identify which start_timer() caused
442 * that callback to fire. this_ptr used as above. */
443 const auto this_wait_id = ++m_current_wait_id;
444 m_timer.async_wait([this, this_ptr, this_wait_id](const Error_code& sys_err_code)
445 {
446 handle_timer_firing(this_ptr, this_wait_id, sys_err_code);
447 });
448
449 m_timer_running = true;
450} // Drop_timer::start_timer()
451
453{
454 using boost::chrono::milliseconds;
455 using boost::chrono::round;
456
457 // Explicitly documented pre-condition.
458 assert(m_timer_running);
459
460 /* As discussed elsewhere, m_timer.cancel() is not enough to ensure handle_timer_firing() doesn't
461 * get called; if the latter is already queued to fire right now then we won't be able stop it.
462 * Moreover even if successful cancel() will still call handle_timer_firing() but with
463 * operation_aborted Error_code. That's why we set m_timer_running = false.
464 * handle_timer_firing() will see that and do nothing, as if it had been canceled. */
465
466 FLOW_LOG_TRACE("Drop_timer [" << this << "] disabling; was set to fire in "
467 "[" << round<milliseconds>(m_timer.expires_from_now()) << "]; "
468 "wait_id = [" << m_current_wait_id << "].");
469
470 // Try to cancel any pending waits (call ASAP with err_code = operation_aborted).
471 m_timer.cancel();
472
473 // Even if something was already queued, this will inform it that it was canceled.
474 m_timer_running = false;
475} // Drop_timer::disable_timer()
476
477void Drop_timer::handle_timer_firing([[maybe_unused]] Ptr prevent_destruction,
478 timer_wait_id_t wait_id,
479 const Error_code& sys_err_code)
480{
481 using std::min;
482 using boost::chrono::milliseconds;
483 using boost::chrono::round;
484
485 if (m_done // done() called.
486 || (sys_err_code == boost::asio::error::operation_aborted) // disable_timer() called cancel() successfully.
487 || (!m_timer_running) // cancel() was too late, but disable_timer() did try to cancel us nonetheless.
488 || (m_current_wait_id != wait_id)) // Someone recently rescheduled timer, but we still fired from earlier.
489 {
490 FLOW_LOG_TRACE("Drop_timer [" << this << "] fired but obsolete/canceled; "
491 "done = [" << m_done << "]; "
492 "aborted = [" << (sys_err_code == boost::asio::error::operation_aborted) << "]; "
493 "timer_running = [" << m_timer_running << "]; "
494 "wait_id = [" << wait_id << "]; current_wait_id = [" << m_current_wait_id << "].");
495 return;
496 }
497 // else
498
499 FLOW_LOG_TRACE("Drop_timer [" << this << "] fired; wait_id = [" << wait_id << "].");
500
501 if (sys_err_code)
502 {
503 FLOW_ERROR_SYS_ERROR_LOG_WARNING(); // Log non-portable error.
504 // Nothing else to do here. We don't know what this means. So just treat it as if timer was triggered.
505 }
506
507 // OK, timer triggered, and we're the active wait handler. Decide what to do.
508
509 /* RFC 6298 says that the RTO (DTO in our case) MUST be doubled each time the timeout expires.
510 * We'll make this a knob for experimentation's sake. */
511
512 // Change the Peer_socket's DTO!
513
514 // Get the option values. Lock, obtain, unlock. (A least overzealous -- but why not?)
515 // @todo That last comment in parentheses is not grammatical, and I now don't know what it may have
516 // meant, when I originally wrote it. Attempt to understand/clarify....
517 unsigned int backoff_factor;
518 Fine_duration ceiling;
519 {
520 Peer_socket::Options_lock lock(m_sock->m_opts_mutex);
521 backoff_factor = m_sock->m_opts.m_dyn_drop_timeout_backoff_factor;
522 ceiling = m_sock->m_opts.m_dyn_drop_timeout_ceiling;
523 }
524
525 if (backoff_factor != 1)
526 {
527 assert(backoff_factor != 0);
528 m_sock_drop_timeout *= backoff_factor;
530 FLOW_LOG_TRACE("Increased DTO by factor [" << backoff_factor << "] with "
531 "ceiling [" << round<milliseconds>(ceiling) << "] to "
532 "[" << round<milliseconds>(m_sock_drop_timeout) << " = " << m_sock_drop_timeout << "].");
533 }
534
535 /* Certainly if the DTO was exceeded, at least the earliest (first) In-flight packet must be
536 * considered Dropped. (BTW it must exist; if there are no In-flight packets then they had to
537 * have called disable_timer() or never called start_timer(), so we cannot be here.)
538 *
539 * Additionally, we could want to drop ALL In-flight packets. RFC 6298 does not say to do that,
540 * so doing so would be MORE aggressive than the RFC, which the RFC says MUST not be the done.
541 * However, that's just more aggressive w/r/t their RECOMMENDED algorithm in section 5. That's
542 * not all however: it also explicitly violates the REQUIREMENT that no packet should be Dropped
543 * for DTO reasons unless it was sent at least DTO ago. E.g., if I sent packet 1 and then in 10
544 * seconds packet 2, then DTO fires in in 1 second, then packet 2 was sent only 1 second ago yet
545 * is Dropped, violating the requirement.
546 *
547 * I've seen certain lesser known custom protocols in fact Drop all packets in this case.
548 * I didn't really know why, but eventually I realized they got that from the DCCP CCID 2
549 * specification (RFC 4341). DCCP is a UDP-based protocol similar to ours (especially similar
550 * when retransmission is disabled). That RFC says specifically that a DTO firing should cause
551 * pipe to be set to zero, which means we should drop all packets. No justification is given, but
552 * this is serious Standards Track RFC, and the first author is Sally Floyd (who wrote the
553 * original SACK RFC among others). It does make some intuitive sense that whatever led to DTO
554 * means all the data are gone from the pipe. On the other hand, straight TCP RFCs like 6298
555 * assume retransmission is involved, in which case my brain can't quite see how the pipe=0 move
556 * is consistent with retransmission and cumulative ACKs, so maybe that's the source of the
557 * difference between the two.
558 *
559 * Anyway, I make it a knob. Given the DCCP CCID 2 instruction, I'd probably go with
560 * m_st_drop_all_on_drop_timeout == true. */
561
562 // Note: m_timer_running = true.
563
564 // Instruct Node what to do with the packet(s) (at least drop one). This happens SYNCHRONOUSLY (right here).
565 m_timer_fired(m_sock->opt(m_sock->m_opts.m_st_drop_all_on_drop_timeout));
566
567 /* They marked one or all In-flight packets Dropped just now. It was also their
568 * responsibility to call on_packet_no_longer_in_flight() accordingly. */
569} // Drop_timer::handle_timer_firing()
570
571void Drop_timer::timer_failure([[maybe_unused]] Ptr prevent_destruction, const Error_code& err_code)
572{
573 if (!m_done)
574 {
575 m_timer_failure(err_code);
576 }
577 // else they're not interested in the timer anymore, including any error, so don't call their failure callback.
578}
579
580} // namespace flow::net_flow
const Component & get_log_component() const
Returns reference to the stored Component object, particularly as many FLOW_LOG_*() macros expect.
Definition: log.cpp:230
Logger * get_logger() const
Returns the stored Logger pointer, particularly as many FLOW_LOG_*() macros expect.
Definition: log.cpp:225
Interface that the user should implement, passing the implementing Logger into logging classes (Flow'...
Definition: log.hpp:1291
virtual bool should_log(Sev sev, const Component &component) const =0
Given attributes of a hypothetical message that would be logged, return true if that message should b...
Sequence_number::seq_num_t packet_id_t
Type to uniquely identify a packet sent over the wire in the socket to which this Drop_timer applies.
Definition: drop_timer.hpp:162
packet_id_t m_during_events_newest_acked_packet
During the time period starting with the last start_contemporaneous_events() call and ending with the...
Definition: drop_timer.hpp:467
Function< void(const Error_code &err_code)> m_timer_failure
Called on error. See Drop_timer constructor.
Definition: drop_timer.hpp:436
packet_id_t m_at_events_start_oldest_flying_packet
The packet ID of the least recently sent In-flight packet at last start_contemporaneous_events() call...
Definition: drop_timer.hpp:459
void timer_failure(Ptr prevent_destruction, const Error_code &err_code)
Called by boost.asio after we post it, in the event of some timer error.
Definition: drop_timer.cpp:571
void end_contemporaneous_events()
Finishes the group started by start start_contemporaneous_events().
Definition: drop_timer.cpp:118
void on_ack(packet_id_t packet_id)
Indicates that a packet for which on_packet_in_flight() was called has just been validly acked.
Definition: drop_timer.cpp:72
void start_timer()
Starts a new wait on the timer, so that is it asynchronously triggered according to the current DTO v...
Definition: drop_timer.cpp:347
void on_packet_no_longer_in_flight(packet_id_t packet_id)
Indicates that a packet for which on_packet_in_flight() was called is now no longer considered In-fli...
Definition: drop_timer.cpp:92
static Ptr create_drop_timer(log::Logger *logger_ptr, util::Task_engine *node_task_engine, Fine_duration *sock_drop_timeout, Peer_socket::Const_ptr &&sock, const Function< void(const Error_code &err_code)> &timer_failure, const Function< void(bool drop_all_packets)> &timer_fired)
Constructs Drop_timer and returns a ref-counted pointer wrapping it.
Definition: drop_timer.cpp:28
const Peer_socket::Const_ptr m_sock
The containing Peer_socket (note that this is read-only access).
Definition: drop_timer.hpp:392
std::set< packet_id_t > m_flying_packets
Packet IDs of packets to have been sent over wire and still considered In-flight, ordered from earlie...
Definition: drop_timer.hpp:453
Function< void(bool drop_all_packets)> m_timer_fired
Called on Drop Timeout. See Drop_timer constructor.
Definition: drop_timer.hpp:439
bool m_timer_running
Is there an active (non-obsolete, not-canceled) asynchronous wait in progress on m_timer?...
Definition: drop_timer.hpp:408
Drop_timer(log::Logger *logger, util::Task_engine *node_task_engine, Fine_duration *sock_drop_timeout, Peer_socket::Const_ptr &&sock, const Function< void(const Error_code &err_code)> &timer_failure, const Function< void(bool drop_all_packets)> &timer_fired)
Constructs Drop_timer as described in the factory constructor create_drop_timer().
Definition: drop_timer.cpp:293
bool m_done
true if and only if done() has been called. Starts at false, can only change to true.
Definition: drop_timer.hpp:433
timer_wait_id_t m_current_wait_id
Unique (within this object) identifier of the last start_timer() call.
Definition: drop_timer.hpp:430
void handle_timer_firing(Ptr prevent_destruction, timer_wait_id_t wait_id, const Error_code &sys_err_code)
Called by boost.asio when the Drop Timer fires; disables timer and calls an outside action callback (...
Definition: drop_timer.cpp:477
void done()
Causes the Drop_timer to guarantee none of the action callbacks provided at construction will be call...
Definition: drop_timer.cpp:321
void start_contemporaneous_events()
Indicates the start of a series of zero or more contemporary on_*() event calls, to be marked as fini...
Definition: drop_timer.cpp:41
util::Task_engine & m_node_task_engine
Node::m_task_engine of the containing Node. Used to schedule timer events.
Definition: drop_timer.hpp:376
void on_no_packets_in_flight_any_longer()
Equivalent to on_packet_no_longer_in_flight(P), for all P currently In-flight as registered by on_pac...
Definition: drop_timer.cpp:105
util::Timer m_timer
The Drop Timer itself.
Definition: drop_timer.hpp:398
void disable_timer()
Invalidates the running asynchronous wait on m_timer. Pre-condition: m_timer_running.
Definition: drop_timer.cpp:452
uint64_t timer_wait_id_t
The counter type used to distinguish a given start_timer() call from any other such call (for this ob...
Definition: drop_timer.hpp:297
Fine_duration & m_sock_drop_timeout
Reference to the containing Peer_socket's Peer_socket::m_snd_drop_timeout data member (= DTO,...
Definition: drop_timer.hpp:385
bool m_in_events_group
true if and only if the last start_contemporaneous_events() call exists, and either end_contemporaneo...
Definition: drop_timer.hpp:473
void on_packet_in_flight(packet_id_t packet_id)
Indicates that a packet identified by the given unique ID has just been sent over the wire (the low-l...
Definition: drop_timer.cpp:52
util::Lock_guard< Options_mutex > Options_lock
Short-hand for lock that acquires exclusive access to an Options_mutex.
boost::shared_ptr< Drop_timer > Ptr
Short-hand for ref-counted pointer to mutable values of type Target_type::element_type (a-la T*).
Const_target_ptr Const_ptr
Short-hand for ref-counted pointer to immutable values of type Target_type::element_type (a-la T cons...
#define FLOW_ERROR_SYS_ERROR_LOG_WARNING()
Logs a warning about the (often errno-based or from a library) error code in sys_err_code.
Definition: error.hpp:269
#define FLOW_LOG_TRACE(ARG_stream_fragment)
Logs a TRACE message into flow::log::Logger *get_logger() with flow::log::Component get_log_component...
Definition: log.hpp:227
Sev
Enumeration containing one of several message severity levels, ordered from highest to lowest.
Definition: log_fwd.hpp:224
@ S_INTERNAL_ERROR_SYSTEM_ERROR_ASIO_TIMER
Internal error: System error: Something went wrong with boost.asio timer subsystem.
Flow module containing the API and implementation of the Flow network protocol, a TCP-inspired stream...
Definition: node.cpp:25
bool key_exists(const Container &container, const typename Container::key_type &key)
Returns true if and only if the given key is present at least once in the given associative container...
Definition: util.hpp:276
boost::asio::io_service Task_engine
Short-hand for boost.asio event service, the central class of boost.asio.
Definition: util_fwd.hpp:135
boost::system::error_code Error_code
Short-hand for a boost.system error code (which basically encapsulates an integer/enum error code and...
Definition: common.hpp:503
Flow_log_component
The flow::log::Component payload enumeration comprising various log components used by Flow's own int...
Definition: common.hpp:633
Fine_clock::duration Fine_duration
A high-res time duration as computed from two Fine_time_pts.
Definition: common.hpp:411
Fine_clock::time_point Fine_time_pt
A high-res time point as returned by Fine_clock::now() and suitable for precise time math in general.
Definition: common.hpp:408