From 55bec839f15b142c609de8c6ba9e4e377827b4ac Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com> Date: Sun, 9 Jul 2017 22:49:29 +0900 Subject: [PATCH] Re-write ack tracker --- lib/Makefile.am | 4 +- lib/ngtcp2_acktr.c | 82 +++++++++++++++++++++++++++++ lib/ngtcp2_acktr.h | 105 ++++++++++++++++++++++++++++++++++++++ lib/ngtcp2_conn.c | 84 ++++++++++-------------------- lib/ngtcp2_conn.h | 14 +---- tests/Makefile.am | 2 + tests/main.c | 4 +- tests/ngtcp2_acktr_test.c | 82 +++++++++++++++++++++++++++++ tests/ngtcp2_acktr_test.h | 34 ++++++++++++ 9 files changed, 340 insertions(+), 71 deletions(-) create mode 100644 lib/ngtcp2_acktr.c create mode 100644 lib/ngtcp2_acktr.h create mode 100644 tests/ngtcp2_acktr_test.c create mode 100644 tests/ngtcp2_acktr_test.h diff --git a/lib/Makefile.am b/lib/Makefile.am index d79cb3c8..2171f843 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -44,7 +44,8 @@ OBJECTS = \ ngtcp2_ppe.c \ ngtcp2_crypto.c \ ngtcp2_err.c \ - ngtcp2_range.c + ngtcp2_range.c \ + ngtcp2_acktr.c HFILES = \ ngtcp2_pkt.h \ @@ -60,6 +61,7 @@ HFILES = \ ngtcp2_crypto.h \ ngtcp2_err.h \ ngtcp2_range.h \ + ngtcp2_acktr.h \ ngtcp2_macro.h libngtcp2_la_SOURCES = $(HFILES) $(OBJECTS) diff --git a/lib/ngtcp2_acktr.c b/lib/ngtcp2_acktr.c new file mode 100644 index 00000000..ac1897c8 --- /dev/null +++ b/lib/ngtcp2_acktr.c @@ -0,0 +1,82 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_acktr.h" + +int ngtcp2_acktr_entry_new(ngtcp2_acktr_entry **ent, uint64_t pkt_num, + ngtcp2_tstamp tstamp, ngtcp2_mem *mem) { + *ent = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_acktr_entry)); + if (*ent == NULL) { + return NGTCP2_ERR_NOMEM; + } + + (*ent)->next = NULL; + (*ent)->pkt_num = pkt_num; + (*ent)->tstamp = tstamp; + + return 0; +} + +void ngtcp2_acktr_entry_del(ngtcp2_acktr_entry *ent, ngtcp2_mem *mem) { + ngtcp2_mem_free(mem, ent); +} + +void ngtcp2_acktr_init(ngtcp2_acktr *acktr) { acktr->ent = NULL; } + +void ngtcp2_acktr_free(ngtcp2_acktr *acktr) { (void)acktr; } + +int ngtcp2_acktr_add(ngtcp2_acktr *acktr, ngtcp2_acktr_entry *ent) { + ngtcp2_acktr_entry **pent; + + for (pent = &acktr->ent; *pent; pent = &(*pent)->next) { + if ((*pent)->pkt_num > ent->pkt_num) { + continue; + } + /* TODO What to do if we receive duplicated packet number? */ + if ((*pent)->pkt_num == ent->pkt_num) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + break; + } + + ent->next = *pent; + *pent = ent; + return 0; +} + +ngtcp2_acktr_entry *ngtcp2_acktr_get(ngtcp2_acktr *acktr) { return acktr->ent; } + +void ngtcp2_acktr_remove(ngtcp2_acktr *acktr, const ngtcp2_acktr_entry *ent) { + ngtcp2_acktr_entry **pent; + + for (pent = &acktr->ent; *pent; pent = &(*pent)->next) { + if (ent->pkt_num != (*pent)->pkt_num) { + continue; + } + + *pent = (*pent)->next; + + return; + } +} diff --git a/lib/ngtcp2_acktr.h b/lib/ngtcp2_acktr.h new file mode 100644 index 00000000..ae63f065 --- /dev/null +++ b/lib/ngtcp2_acktr.h @@ -0,0 +1,105 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_ACKTR_H +#define NGTCP2_ACKTR_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_mem.h" + +struct ngtcp2_acktr_entry; +typedef struct ngtcp2_acktr_entry ngtcp2_acktr_entry; + +/* + * ngtcp2_acktr_entry is a single packet which needs to be acked. + */ +struct ngtcp2_acktr_entry { + ngtcp2_acktr_entry *next; + uint64_t pkt_num; + ngtcp2_tstamp tstamp; +}; + +/* + * ngtcp2_acktr_entry_new allocates memory for ent, and initializes it + * with the given parameters. + */ +int ngtcp2_acktr_entry_new(ngtcp2_acktr_entry **ent, uint64_t pkt_num, + ngtcp2_tstamp tstamp, ngtcp2_mem *mem); + +/* + * ngtcp2_acktr_entry_del deallocates memory allocated for |ent|. It + * deallocates memory pointed by |ent|. + */ +void ngtcp2_acktr_entry_del(ngtcp2_acktr_entry *ent, ngtcp2_mem *mem); + +/* + * ngtcp2_acktr tracks received packets which we have to send ack. + */ +typedef struct { + /* ent points to the head of list which is ordered by the decreasing + order of packet number. */ + ngtcp2_acktr_entry *ent; +} ngtcp2_acktr; + +/* + * ngtcp2_acktr_init initializes |acktr|. + */ +void ngtcp2_acktr_init(ngtcp2_acktr *acktr); + +/* + * ngtcp2_acktr_free frees resources allocated for |acktr|. It does + * not free any ngtcp2_acktr_entry directly or indirectly pointed by + * acktr->ent. + */ +void ngtcp2_acktr_free(ngtcp2_acktr *acktr); + +/* + * ngtcp2_acktr_add adds |ent|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_INVALID_ARGUMENT + * Same packet number has already been included in |acktr|. + */ +int ngtcp2_acktr_add(ngtcp2_acktr *acktr, ngtcp2_acktr_entry *ent); + +/* + * ngtcp2_acktr_get returns the entry which has the largest packet + * number to be acked. If there is no entry, this function returns + * NULL. + */ +ngtcp2_acktr_entry *ngtcp2_acktr_get(ngtcp2_acktr *acktr); + +/* + * ngtcp2_acktr_remove removes the |ent|. + */ +void ngtcp2_acktr_remove(ngtcp2_acktr *acktr, const ngtcp2_acktr_entry *ent); + +#endif /* NGTCP2_ACKTR_H */ diff --git a/lib/ngtcp2_conn.c b/lib/ngtcp2_conn.c index eaa4d0df..380576ba 100644 --- a/lib/ngtcp2_conn.c +++ b/lib/ngtcp2_conn.c @@ -109,15 +109,6 @@ static int conn_call_handshake_completed(ngtcp2_conn *conn) { return 0; } -static int conn_ackq_greater(const void *lhsx, const void *rhsx) { - const ngtcp2_rx_pkt *lhs, *rhs; - - lhs = ngtcp2_struct_of(lhsx, ngtcp2_rx_pkt, pq_entry); - rhs = ngtcp2_struct_of(rhsx, ngtcp2_rx_pkt, pq_entry); - - return lhs->pkt_num > rhs->pkt_num; -} - static int conn_new(ngtcp2_conn **pconn, uint64_t conn_id, uint32_t version, const ngtcp2_conn_callbacks *callbacks, void *user_data) { int rv; @@ -129,16 +120,13 @@ static int conn_new(ngtcp2_conn **pconn, uint64_t conn_id, uint32_t version, goto fail_conn; } - rv = ngtcp2_pq_init(&(*pconn)->ackq, conn_ackq_greater, mem); - if (rv != 0) { - goto fail_pq_init; - } - rv = ngtcp2_strm_init(&(*pconn)->strm0, mem); if (rv != 0) { goto fail_strm_init; } + ngtcp2_acktr_init(&(*pconn)->acktr); + (*pconn)->callbacks = *callbacks; (*pconn)->conn_id = conn_id; (*pconn)->version = version; @@ -148,8 +136,6 @@ static int conn_new(ngtcp2_conn **pconn, uint64_t conn_id, uint32_t version, return 0; fail_strm_init: - ngtcp2_pq_free(&(*pconn)->ackq); -fail_pq_init: ngtcp2_mem_free(mem, *pconn); fail_conn: return rv; @@ -188,16 +174,14 @@ int ngtcp2_conn_server_new(ngtcp2_conn **pconn, uint64_t conn_id, return 0; } -static int ackq_rx_pkt_free(ngtcp2_pq_entry *item, void *arg) { - ngtcp2_rx_pkt *rpkt; - ngtcp2_conn *conn; - - rpkt = ngtcp2_struct_of(item, ngtcp2_rx_pkt, pq_entry); - conn = arg; +static void delete_acktr_entry(ngtcp2_acktr_entry *ent, ngtcp2_mem *mem) { + ngtcp2_acktr_entry *next; - ngtcp2_mem_free(conn->mem, rpkt); - - return 0; + for (; ent;) { + next = ent->next; + ngtcp2_acktr_entry_del(ent, mem); + ent = next; + } } void ngtcp2_conn_del(ngtcp2_conn *conn) { @@ -205,13 +189,14 @@ void ngtcp2_conn_del(ngtcp2_conn *conn) { return; } + delete_acktr_entry(conn->acktr.ent, conn->mem); + ngtcp2_acktr_free(&conn->acktr); + ngtcp2_crypto_km_del(conn->rx_ckm, conn->mem); ngtcp2_crypto_km_del(conn->tx_ckm, conn->mem); ngtcp2_strm_free(&conn->strm0); - ngtcp2_pq_each(&conn->ackq, ackq_rx_pkt_free, conn); - ngtcp2_pq_free(&conn->ackq); ngtcp2_mem_free(conn->mem, conn); } @@ -220,35 +205,31 @@ static int conn_create_ack_frame(ngtcp2_conn *conn, ngtcp2_ack *ack, uint64_t first_pkt_num; ngtcp2_tstamp ack_delay; uint64_t last_pkt_num; - ngtcp2_rx_pkt *rpkt; ngtcp2_ack_blk *blk; int initial = 1; uint64_t gap; + ngtcp2_acktr_entry *rpkt; - if (ngtcp2_pq_empty(&conn->ackq)) { + rpkt = ngtcp2_acktr_get(&conn->acktr); + if (rpkt == NULL) { return 0; } - rpkt = ngtcp2_struct_of(ngtcp2_pq_top(&conn->ackq), ngtcp2_rx_pkt, pq_entry); - ngtcp2_pq_pop(&conn->ackq); - first_pkt_num = last_pkt_num = rpkt->pkt_num; ack_delay = ts - rpkt->tstamp; - ngtcp2_mem_free(conn->mem, rpkt); + ngtcp2_acktr_remove(&conn->acktr, rpkt); + ngtcp2_acktr_entry_del(rpkt, conn->mem); ack->type = NGTCP2_FRAME_ACK; ack->num_ts = 0; ack->num_blks = 0; - for (; !ngtcp2_pq_empty(&conn->ackq);) { - rpkt = - ngtcp2_struct_of(ngtcp2_pq_top(&conn->ackq), ngtcp2_rx_pkt, pq_entry); - + for (; (rpkt = ngtcp2_acktr_get(&conn->acktr));) { if (rpkt->pkt_num + 1 == last_pkt_num) { last_pkt_num = rpkt->pkt_num; - ngtcp2_pq_pop(&conn->ackq); - ngtcp2_mem_free(conn->mem, rpkt); + ngtcp2_acktr_remove(&conn->acktr, rpkt); + ngtcp2_acktr_entry_del(rpkt, conn->mem); continue; } @@ -274,8 +255,8 @@ static int conn_create_ack_frame(ngtcp2_conn *conn, ngtcp2_ack *ack, first_pkt_num = last_pkt_num = rpkt->pkt_num; - ngtcp2_pq_pop(&conn->ackq); - ngtcp2_mem_free(conn->mem, rpkt); + ngtcp2_acktr_remove(&conn->acktr, rpkt); + ngtcp2_acktr_entry_del(rpkt, conn->mem); if (ack->num_blks == 255) { break; @@ -1026,30 +1007,19 @@ int ngtcp2_strm_recv_reordering(ngtcp2_strm *strm, ngtcp2_stream *fr) { return ngtcp2_rob_push(&strm->rob, fr->offset, fr->data, fr->datalen); } -/* TODO This is not efficient and not robust. */ int ngtcp2_conn_sched_ack(ngtcp2_conn *conn, uint64_t pkt_num, ngtcp2_tstamp ts) { - ngtcp2_rx_pkt *rpkt; + ngtcp2_acktr_entry *rpkt; int rv; - if (ngtcp2_pq_size(&conn->ackq) > 1024) { - return NGTCP2_ERR_INTERNAL; - } - - rpkt = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_rx_pkt)); - if (rpkt == NULL) { - return NGTCP2_ERR_NOMEM; - } - - rpkt->pkt_num = pkt_num; - rpkt->tstamp = ts; - - rv = ngtcp2_pq_push(&conn->ackq, &rpkt->pq_entry); + rv = ngtcp2_acktr_entry_new(&rpkt, pkt_num, ts, conn->mem); if (rv != 0) { - ngtcp2_mem_free(conn->mem, rpkt); return rv; } + /* TODO Ignore error for now */ + ngtcp2_acktr_add(&conn->acktr, rpkt); + return 0; } diff --git a/lib/ngtcp2_conn.h b/lib/ngtcp2_conn.h index 1e0e9df6..6ee1d891 100644 --- a/lib/ngtcp2_conn.h +++ b/lib/ngtcp2_conn.h @@ -35,7 +35,7 @@ #include "ngtcp2_buf.h" #include "ngtcp2_rob.h" #include "ngtcp2_crypto.h" -#include "ngtcp2_pq.h" +#include "ngtcp2_acktr.h" typedef enum { /* Client specific handshake states */ @@ -83,19 +83,8 @@ uint64_t ngtcp2_strm_rx_offset(ngtcp2_strm *strm); */ int ngtcp2_strm_recv_reordering(ngtcp2_strm *strm, ngtcp2_stream *fr); -/* - * ngtcp2_rx_pkt records packet number and its reception timestamp for - * its transmission of ACK. - */ -typedef struct { - ngtcp2_pq_entry pq_entry; - uint64_t pkt_num; - ngtcp2_tstamp tstamp; -} ngtcp2_rx_pkt; - struct ngtcp2_conn { int state; - ngtcp2_pq ackq; ngtcp2_conn_callbacks callbacks; ngtcp2_strm strm0; uint64_t conn_id; @@ -103,6 +92,7 @@ struct ngtcp2_conn { uint64_t max_rx_pkt_num; ngtcp2_mem *mem; void *user_data; + ngtcp2_acktr acktr; uint32_t version; int handshake_completed; int server; diff --git a/tests/Makefile.am b/tests/Makefile.am index ad111587..4252e56c 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -32,12 +32,14 @@ OBJECTS = \ ngtcp2_upe_test.c \ ngtcp2_range_test.c \ ngtcp2_rob_test.c \ + ngtcp2_acktr_test.c \ ngtcp2_test_helper.c HFILES= \ ngtcp2_pkt_test.h \ ngtcp2_upe_test.h \ ngtcp2_range_test.h \ ngtcp2_rob_test.h \ + ngtcp2_acktr_test.h \ ngtcp2_test_helper.h main_SOURCES = $(HFILES) $(OBJECTS) diff --git a/tests/main.c b/tests/main.c index 1962df3a..2af9d61d 100644 --- a/tests/main.c +++ b/tests/main.c @@ -35,6 +35,7 @@ #include "ngtcp2_upe_test.h" #include "ngtcp2_range_test.h" #include "ngtcp2_rob_test.h" +#include "ngtcp2_acktr_test.h" static int init_suite1(void) { return 0; } @@ -83,7 +84,8 @@ int main() { !CU_add_test(pSuite, "rob_push", test_ngtcp2_rob_push) || !CU_add_test(pSuite, "rob_data_at", test_ngtcp2_rob_data_at) || !CU_add_test(pSuite, "rob_remove_prefix", - test_ngtcp2_rob_remove_prefix)) { + test_ngtcp2_rob_remove_prefix) || + !CU_add_test(pSuite, "acktr_add", test_ngtcp2_acktr_add)) { CU_cleanup_registry(); return CU_get_error(); } diff --git a/tests/ngtcp2_acktr_test.c b/tests/ngtcp2_acktr_test.c new file mode 100644 index 00000000..03f88e47 --- /dev/null +++ b/tests/ngtcp2_acktr_test.c @@ -0,0 +1,82 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_acktr_test.h" + +#include <CUnit/CUnit.h> + +#include "ngtcp2_acktr.h" +#include "ngtcp2_test_helper.h" + +void test_ngtcp2_acktr_add(void) { + ngtcp2_acktr acktr; + ngtcp2_acktr_entry ents[] = { + {NULL, 1, 1000}, {NULL, 5, 1001}, {NULL, 7, 1002}, {NULL, 4, 1003}, + {NULL, 6, 1004}, {NULL, 2, 1005}, {NULL, 3, 1006}, + }; + uint64_t max_pkt_num[] = {1, 5, 7, 7, 7, 7, 7}; + ngtcp2_acktr_entry *ent; + size_t i; + int rv; + + ngtcp2_acktr_init(&acktr); + + for (i = 0; i < arraylen(ents); ++i) { + rv = ngtcp2_acktr_add(&acktr, &ents[i]); + + CU_ASSERT(0 == rv); + + ent = ngtcp2_acktr_get(&acktr); + + CU_ASSERT(max_pkt_num[i] == ent->pkt_num); + } + + for (i = 0; i < arraylen(ents); ++i) { + ent = ngtcp2_acktr_get(&acktr); + ngtcp2_acktr_remove(&acktr, ent); + + ent = ngtcp2_acktr_get(&acktr); + + if (i != arraylen(ents) - 1) { + CU_ASSERT(arraylen(ents) - i - 1 == ent->pkt_num); + } else { + CU_ASSERT(NULL == ent); + } + } + + ngtcp2_acktr_free(&acktr); + + /* Check duplicates */ + ngtcp2_acktr_init(&acktr); + + rv = ngtcp2_acktr_add(&acktr, &ents[0]); + + CU_ASSERT(0 == rv); + + rv = ngtcp2_acktr_add(&acktr, &ents[0]); + + CU_ASSERT(NGTCP2_ERR_INVALID_ARGUMENT == rv); + + ngtcp2_acktr_free(&acktr); +} diff --git a/tests/ngtcp2_acktr_test.h b/tests/ngtcp2_acktr_test.h new file mode 100644 index 00000000..ed531aab --- /dev/null +++ b/tests/ngtcp2_acktr_test.h @@ -0,0 +1,34 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_ACKTR_TEST_H +#define NGTCP2_ACKTR_TEST_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif /* HAVE_CONFIG_H */ + +void test_ngtcp2_acktr_add(void); + +#endif /* NGTCP2_ACKTR_TEST_H */ -- GitLab