diff --git a/doc/programmers-guide.rst b/doc/programmers-guide.rst index 3d504064a4f840294c490f1a6460e4e52cee468f..8a4bed47b485ce51b674d656d7745a9eb4533a91 100644 --- a/doc/programmers-guide.rst +++ b/doc/programmers-guide.rst @@ -271,6 +271,7 @@ timestamp is equal to or larger than the value returned from (or `ngtcp2_conn_write_handshake()` if handshake has not completed yet). If the current timestamp is equal to or larger than the value returned from `ngtcp2_conn_ack_delay_expiry()`, it has to call +`ngtcp2_conn_cancel_expired_ack_delay_timer()` and `ngtcp2_conn_write_pkt()` (or `ngtcp2_conn_write_handshake()` if handshake has not completed yet). After calling these functions, new expiry will be set. The application should call diff --git a/examples/client.cc b/examples/client.cc index 6041c1a2ff5154f7d8d8c7cb7d9ca82ccf563465..7cf29c3c6db1008c108048810ee7e5a9e17c29aa 100644 --- a/examples/client.cc +++ b/examples/client.cc @@ -361,6 +361,7 @@ void retransmitcb(struct ev_loop *loop, ev_timer *w, int revents) { } if (ngtcp2_conn_ack_delay_expiry(conn) <= now) { + ngtcp2_conn_cancel_expired_ack_delay_timer(conn, now); rv = c->on_write(); if (rv != 0) { goto fail; diff --git a/examples/server.cc b/examples/server.cc index 4556adb101549bb34f6e7b69d3c157939ffa88f2..660999279a28f27bdf1850b07d03afe6126deaa8 100644 --- a/examples/server.cc +++ b/examples/server.cc @@ -835,6 +835,7 @@ void retransmitcb(struct ev_loop *loop, ev_timer *w, int revents) { if (!config.quiet) { std::cerr << "Delayed ACK timer expired" << std::endl; } + ngtcp2_conn_cancel_expired_ack_delay_timer(conn, now); rv = h->on_write(); switch (rv) { case 0: diff --git a/lib/includes/ngtcp2/ngtcp2.h b/lib/includes/ngtcp2/ngtcp2.h index 9428fd5f1a7e4bbf2e01184467b952dce22d6589..8b6bb4c9995ea2151a9d71cbc1a0f8b281746077 100644 --- a/lib/includes/ngtcp2/ngtcp2.h +++ b/lib/includes/ngtcp2/ngtcp2.h @@ -2030,6 +2030,7 @@ ngtcp2_conn_loss_detection_expiry(ngtcp2_conn *conn); * * `ngtcp2_conn_ack_delay_expiry` returns the expiry time point of * delayed protected ACK. Application should call + * ngtcp2_conn_cancel_expired_ack_delay_timer() and * `ngtcp2_conn_write_pkt` (or `ngtcp2_conn_write_handshake` if * handshake has not finished yet) when it expires. It returns * UINT64_MAX if there is no expiry. @@ -2046,6 +2047,16 @@ NGTCP2_EXTERN ngtcp2_tstamp ngtcp2_conn_ack_delay_expiry(ngtcp2_conn *conn); */ NGTCP2_EXTERN ngtcp2_tstamp ngtcp2_conn_get_expiry(ngtcp2_conn *conn); +/** + * @function + * + * `ngtcp2_conn_cancel_expired_ack_delay_timer` stops expired ACK + * delay timer. |ts| is the current time. This function must be + * called when ngtcp2_conn_ack_delay_expiry() <= ts. + */ +NGTCP2_EXTERN void ngtcp2_conn_cancel_expired_ack_delay_timer(ngtcp2_conn *conn, + ngtcp2_tstamp ts); + /** * @function * diff --git a/lib/ngtcp2_acktr.c b/lib/ngtcp2_acktr.c index e7403db618c9387abe0483139b9cf5065dc4c3b1..494e4cd616fd5309aa8cfced3e0126d0c3281d83 100644 --- a/lib/ngtcp2_acktr.c +++ b/lib/ngtcp2_acktr.c @@ -316,15 +316,15 @@ void ngtcp2_acktr_recv_ack(ngtcp2_acktr *acktr, const ngtcp2_ack *fr) { void ngtcp2_acktr_commit_ack(ngtcp2_acktr *acktr) { acktr->flags &= (uint16_t) ~(NGTCP2_ACKTR_FLAG_ACTIVE_ACK | - NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK); + NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK | + NGTCP2_ACKTR_FLAG_CANCEL_TIMER); acktr->first_unacked_ts = UINT64_MAX; acktr->rx_npkt = 0; } int ngtcp2_acktr_require_active_ack(ngtcp2_acktr *acktr, uint64_t max_ack_delay, ngtcp2_tstamp ts) { - return (acktr->flags & NGTCP2_ACKTR_FLAG_ACTIVE_ACK) && - acktr->first_unacked_ts <= ts - max_ack_delay; + return acktr->first_unacked_ts <= ts - max_ack_delay; } void ngtcp2_acktr_immediate_ack(ngtcp2_acktr *acktr) { diff --git a/lib/ngtcp2_acktr.h b/lib/ngtcp2_acktr.h index 35e00d76bd4904e1f6f46c71adef8154200146b1..1986420391f2a5a2ba578cac1f5bfd9c38e54b91 100644 --- a/lib/ngtcp2_acktr.h +++ b/lib/ngtcp2_acktr.h @@ -108,6 +108,9 @@ typedef enum { acknowledgement for ACK which acknowledges the last handshake packet from client (which contains TLSv1.3 Finished message). */ NGTCP2_ACKTR_FLAG_ACK_FINISHED_ACK = 0x80, + /* NGTCP2_ACKTR_FLAG_CANCEL_TIMER is set when ACK delay timer is + expired and canceled. */ + NGTCP2_ACKTR_FLAG_CANCEL_TIMER = 0x0100, } ngtcp2_acktr_flag; /* diff --git a/lib/ngtcp2_conn.c b/lib/ngtcp2_conn.c index cafd051d77f3784d3aff71a04f1513600f020e77..ba8864620df93f3e020890edd00b44ae8783a614 100644 --- a/lib/ngtcp2_conn.c +++ b/lib/ngtcp2_conn.c @@ -1754,8 +1754,7 @@ static ssize_t conn_write_handshake_ack_pkt(ngtcp2_conn *conn, uint8_t *dest, * Initial and Handshake packet. */ static ssize_t conn_write_handshake_ack_pkts(ngtcp2_conn *conn, uint8_t *dest, - size_t destlen, - ngtcp2_tstamp ts) { + size_t destlen, ngtcp2_tstamp ts) { ssize_t res = 0, nwrite = 0; int require_padding; @@ -7511,15 +7510,18 @@ ngtcp2_tstamp ngtcp2_conn_ack_delay_expiry(ngtcp2_conn *conn) { ngtcp2_acktr *acktr = &conn->pktns.acktr; ngtcp2_tstamp ts = UINT64_MAX, t; - if (in_acktr->first_unacked_ts != UINT64_MAX) { + if (!(in_acktr->flags & NGTCP2_ACKTR_FLAG_CANCEL_TIMER) && + in_acktr->first_unacked_ts != UINT64_MAX) { t = in_acktr->first_unacked_ts + NGTCP2_HS_ACK_DELAY; ts = ngtcp2_min(ts, t); } - if (hs_acktr->first_unacked_ts != UINT64_MAX) { + if (!(hs_acktr->flags & NGTCP2_ACKTR_FLAG_CANCEL_TIMER) && + hs_acktr->first_unacked_ts != UINT64_MAX) { t = hs_acktr->first_unacked_ts + NGTCP2_HS_ACK_DELAY; ts = ngtcp2_min(ts, t); } - if (acktr->first_unacked_ts != UINT64_MAX) { + if (!(acktr->flags & NGTCP2_ACKTR_FLAG_CANCEL_TIMER) && + acktr->first_unacked_ts != UINT64_MAX) { t = acktr->first_unacked_ts + conn_compute_ack_delay(conn); ts = ngtcp2_min(ts, t); } @@ -7532,6 +7534,21 @@ ngtcp2_tstamp ngtcp2_conn_get_expiry(ngtcp2_conn *conn) { return ngtcp2_min(t1, t2); } +static void acktr_cancel_expired_ack_delay_timer(ngtcp2_acktr *acktr, + ngtcp2_tstamp ts) { + if (!(acktr->flags & NGTCP2_ACKTR_FLAG_CANCEL_TIMER) && + acktr->first_unacked_ts <= ts) { + acktr->flags |= NGTCP2_ACKTR_FLAG_CANCEL_TIMER; + } +} + +void ngtcp2_conn_cancel_expired_ack_delay_timer(ngtcp2_conn *conn, + ngtcp2_tstamp ts) { + acktr_cancel_expired_ack_delay_timer(&conn->in_pktns.acktr, ts); + acktr_cancel_expired_ack_delay_timer(&conn->hs_pktns.acktr, ts); + acktr_cancel_expired_ack_delay_timer(&conn->pktns.acktr, ts); +} + /* * settings_copy_from_transport_params translates * ngtcp2_transport_params to ngtcp2_settings.