1 1.1 christos /* 2 1.1 christos * testcode/unittcpreuse.c - unit test for tcp_reuse. 3 1.1 christos * 4 1.1 christos * Copyright (c) 2021, NLnet Labs. All rights reserved. 5 1.1 christos * 6 1.1 christos * This software is open source. 7 1.1 christos * 8 1.1 christos * Redistribution and use in source and binary forms, with or without 9 1.1 christos * modification, are permitted provided that the following conditions 10 1.1 christos * are met: 11 1.1 christos * 12 1.1 christos * Redistributions of source code must retain the above copyright notice, 13 1.1 christos * this list of conditions and the following disclaimer. 14 1.1 christos * 15 1.1 christos * Redistributions in binary form must reproduce the above copyright notice, 16 1.1 christos * this list of conditions and the following disclaimer in the documentation 17 1.1 christos * and/or other materials provided with the distribution. 18 1.1 christos * 19 1.1 christos * Neither the name of the NLNET LABS nor the names of its contributors may 20 1.1 christos * be used to endorse or promote products derived from this software without 21 1.1 christos * specific prior written permission. 22 1.1 christos * 23 1.1 christos * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 1.1 christos * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 1.1 christos * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 26 1.1 christos * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27 1.1 christos * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 1.1 christos * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 29 1.1 christos * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 30 1.1 christos * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 31 1.1 christos * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 32 1.1 christos * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 33 1.1 christos * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 1.1 christos * 35 1.1 christos */ 36 1.1 christos /** 37 1.1 christos * \file 38 1.1 christos * Tests the tcp_reuse functionality. 39 1.1 christos */ 40 1.1 christos 41 1.1 christos #include "config.h" 42 1.1 christos #include "testcode/unitmain.h" 43 1.1 christos #include "util/log.h" 44 1.1 christos #include "util/random.h" 45 1.1 christos #include "services/outside_network.h" 46 1.1 christos 47 1.1.1.2 christos #define MAX_TCP_WAITING_NODES 5 48 1.1.1.2 christos 49 1.1 christos /** add number of new IDs to the reuse tree, randomly chosen */ 50 1.1 christos static void tcpid_addmore(struct reuse_tcp* reuse, 51 1.1 christos struct outside_network* outnet, unsigned int addnum) 52 1.1 christos { 53 1.1 christos unsigned int i; 54 1.1 christos struct waiting_tcp* w; 55 1.1 christos for(i=0; i<addnum; i++) { 56 1.1 christos uint16_t id = reuse_tcp_select_id(reuse, outnet); 57 1.1 christos unit_assert(!reuse_tcp_by_id_find(reuse, id)); 58 1.1 christos w = calloc(1, sizeof(*w)); 59 1.1 christos unit_assert(w); 60 1.1 christos w->id = id; 61 1.1 christos w->outnet = outnet; 62 1.1 christos w->next_waiting = (void*)reuse->pending; 63 1.1 christos reuse_tree_by_id_insert(reuse, w); 64 1.1 christos } 65 1.1 christos } 66 1.1 christos 67 1.1 christos /** fill up the reuse ID tree and test assertions */ 68 1.1 christos static void tcpid_fillup(struct reuse_tcp* reuse, 69 1.1 christos struct outside_network* outnet) 70 1.1 christos { 71 1.1 christos int t, numtest=3; 72 1.1 christos for(t=0; t<numtest; t++) { 73 1.1 christos rbtree_init(&reuse->tree_by_id, reuse_id_cmp); 74 1.1 christos tcpid_addmore(reuse, outnet, 65535); 75 1.1 christos reuse_del_readwait(&reuse->tree_by_id); 76 1.1 christos } 77 1.1 christos } 78 1.1 christos 79 1.1 christos /** test TCP ID selection */ 80 1.1 christos static void tcpid_test(void) 81 1.1 christos { 82 1.1 christos struct pending_tcp pend; 83 1.1 christos struct outside_network outnet; 84 1.1 christos unit_show_func("services/outside_network.c", "reuse_tcp_select_id"); 85 1.1 christos memset(&pend, 0, sizeof(pend)); 86 1.1 christos pend.reuse.pending = &pend; 87 1.1 christos memset(&outnet, 0, sizeof(outnet)); 88 1.1 christos outnet.rnd = ub_initstate(NULL); 89 1.1 christos rbtree_init(&pend.reuse.tree_by_id, reuse_id_cmp); 90 1.1 christos tcpid_fillup(&pend.reuse, &outnet); 91 1.1 christos ub_randfree(outnet.rnd); 92 1.1 christos } 93 1.1 christos 94 1.1 christos /** check that the tree has present number of nodes and the LRU is linked 95 1.1 christos * properly. */ 96 1.1 christos static void check_tree_and_list(struct outside_network* outnet, int present) 97 1.1 christos { 98 1.1 christos int i; 99 1.1 christos struct reuse_tcp *reuse, *next_reuse; 100 1.1 christos unit_assert(present == (int)outnet->tcp_reuse.count); 101 1.1 christos if(present < 1) { 102 1.1 christos unit_assert(outnet->tcp_reuse_first == NULL); 103 1.1 christos unit_assert(outnet->tcp_reuse_last == NULL); 104 1.1 christos return; 105 1.1 christos } 106 1.1 christos unit_assert(outnet->tcp_reuse_first->item_on_lru_list); 107 1.1 christos unit_assert(!outnet->tcp_reuse_first->lru_prev); 108 1.1 christos reuse = outnet->tcp_reuse_first; 109 1.1 christos for(i=0; i<present-1; i++) { 110 1.1 christos unit_assert(reuse->item_on_lru_list); 111 1.1 christos unit_assert(reuse->lru_next); 112 1.1 christos unit_assert(reuse->lru_next != reuse); 113 1.1 christos next_reuse = reuse->lru_next; 114 1.1 christos unit_assert(next_reuse->lru_prev == reuse); 115 1.1 christos reuse = next_reuse; 116 1.1 christos } 117 1.1 christos unit_assert(!reuse->lru_next); 118 1.1 christos unit_assert(outnet->tcp_reuse_last->item_on_lru_list); 119 1.1 christos unit_assert(outnet->tcp_reuse_last == reuse); 120 1.1 christos } 121 1.1 christos 122 1.1 christos /** creates pending_tcp. Copy of outside_network.c:create_pending_tcp without 123 1.1 christos * the comm_point creation */ 124 1.1 christos static int create_pending_tcp(struct outside_network* outnet) 125 1.1 christos { 126 1.1 christos size_t i; 127 1.1 christos if(outnet->num_tcp == 0) 128 1.1 christos return 1; /* no tcp needed, nothing to do */ 129 1.1 christos if(!(outnet->tcp_conns = (struct pending_tcp **)calloc( 130 1.1 christos outnet->num_tcp, sizeof(struct pending_tcp*)))) 131 1.1 christos return 0; 132 1.1 christos for(i=0; i<outnet->num_tcp; i++) { 133 1.1 christos if(!(outnet->tcp_conns[i] = (struct pending_tcp*)calloc(1, 134 1.1 christos sizeof(struct pending_tcp)))) 135 1.1 christos return 0; 136 1.1 christos outnet->tcp_conns[i]->next_free = outnet->tcp_free; 137 1.1 christos outnet->tcp_free = outnet->tcp_conns[i]; 138 1.1 christos } 139 1.1 christos return 1; 140 1.1 christos } 141 1.1 christos 142 1.1 christos /** empty the tcp_reuse tree and LRU list */ 143 1.1 christos static void empty_tree(struct outside_network* outnet) 144 1.1 christos { 145 1.1 christos size_t i; 146 1.1 christos struct reuse_tcp* reuse; 147 1.1 christos reuse = outnet->tcp_reuse_first; 148 1.1 christos i = outnet->tcp_reuse.count; 149 1.1 christos while(reuse) { 150 1.1 christos reuse_tcp_remove_tree_list(outnet, reuse); 151 1.1 christos check_tree_and_list(outnet, --i); 152 1.1 christos reuse = outnet->tcp_reuse_first; 153 1.1 christos } 154 1.1 christos } 155 1.1 christos 156 1.1 christos /** check removal of the LRU element on the given position of total elements */ 157 1.1 christos static void check_removal(struct outside_network* outnet, int position, int total) 158 1.1 christos { 159 1.1 christos int i; 160 1.1 christos struct reuse_tcp* reuse; 161 1.1 christos empty_tree(outnet); 162 1.1 christos for(i=0; i<total; i++) { 163 1.1 christos reuse_tcp_insert(outnet, outnet->tcp_conns[i]); 164 1.1 christos } 165 1.1 christos check_tree_and_list(outnet, total); 166 1.1 christos reuse = outnet->tcp_reuse_first; 167 1.1 christos for(i=0; i<position; i++) reuse = reuse->lru_next; 168 1.1 christos reuse_tcp_remove_tree_list(outnet, reuse); 169 1.1 christos check_tree_and_list(outnet, total-1); 170 1.1 christos } 171 1.1 christos 172 1.1 christos /** check snipping off the last element of the LRU with total elements */ 173 1.1 christos static void check_snip(struct outside_network* outnet, int total) 174 1.1 christos { 175 1.1 christos int i; 176 1.1 christos struct reuse_tcp* reuse; 177 1.1 christos empty_tree(outnet); 178 1.1 christos for(i=0; i<total; i++) { 179 1.1 christos reuse_tcp_insert(outnet, outnet->tcp_conns[i]); 180 1.1 christos } 181 1.1 christos check_tree_and_list(outnet, total); 182 1.1 christos reuse = reuse_tcp_lru_snip(outnet); 183 1.1 christos while(reuse) { 184 1.1 christos reuse_tcp_remove_tree_list(outnet, reuse); 185 1.1 christos check_tree_and_list(outnet, --total); 186 1.1 christos reuse = reuse_tcp_lru_snip(outnet); 187 1.1 christos } 188 1.1 christos unit_assert(outnet->tcp_reuse_first == NULL); 189 1.1 christos unit_assert(outnet->tcp_reuse_last == NULL); 190 1.1 christos unit_assert(outnet->tcp_reuse.count == 0); 191 1.1 christos } 192 1.1 christos 193 1.1 christos /** test tcp_reuse tree and LRU list functions */ 194 1.1 christos static void tcp_reuse_tree_list_test(void) 195 1.1 christos { 196 1.1 christos size_t i; 197 1.1 christos struct outside_network outnet; 198 1.1 christos struct reuse_tcp* reuse; 199 1.1 christos memset(&outnet, 0, sizeof(outnet)); 200 1.1 christos rbtree_init(&outnet.tcp_reuse, reuse_cmp); 201 1.1 christos outnet.num_tcp = 5; 202 1.1 christos outnet.tcp_reuse_max = outnet.num_tcp; 203 1.1 christos if(!create_pending_tcp(&outnet)) fatal_exit("out of memory"); 204 1.1 christos /* add all to the tree */ 205 1.1 christos unit_show_func("services/outside_network.c", "reuse_tcp_insert"); 206 1.1 christos for(i=0; i<outnet.num_tcp; i++) { 207 1.1 christos reuse_tcp_insert(&outnet, outnet.tcp_conns[i]); 208 1.1 christos check_tree_and_list(&outnet, i+1); 209 1.1 christos } 210 1.1 christos /* check touching */ 211 1.1 christos unit_show_func("services/outside_network.c", "reuse_tcp_lru_touch"); 212 1.1 christos for(i=0; i<outnet.tcp_reuse.count; i++) { 213 1.1 christos for(reuse = outnet.tcp_reuse_first; reuse->lru_next; reuse = reuse->lru_next); 214 1.1 christos reuse_tcp_lru_touch(&outnet, reuse); 215 1.1 christos check_tree_and_list(&outnet, outnet.num_tcp); 216 1.1 christos } 217 1.1 christos /* check removal */ 218 1.1 christos unit_show_func("services/outside_network.c", "reuse_tcp_remove_tree_list"); 219 1.1 christos check_removal(&outnet, 2, 5); 220 1.1 christos check_removal(&outnet, 1, 3); 221 1.1 christos check_removal(&outnet, 1, 2); 222 1.1 christos /* check snip */ 223 1.1 christos unit_show_func("services/outside_network.c", "reuse_tcp_lru_snip"); 224 1.1 christos check_snip(&outnet, 4); 225 1.1 christos 226 1.1 christos for(i=0; i<outnet.num_tcp; i++) 227 1.1 christos if(outnet.tcp_conns[i]) { 228 1.1 christos free(outnet.tcp_conns[i]); 229 1.1 christos } 230 1.1 christos free(outnet.tcp_conns); 231 1.1 christos } 232 1.1 christos 233 1.1.1.2 christos static void check_waiting_tcp_list(struct outside_network* outnet, 234 1.1.1.2 christos struct waiting_tcp* first, struct waiting_tcp* last, size_t total) 235 1.1.1.2 christos { 236 1.1.1.2 christos size_t i, j; 237 1.1.1.2 christos struct waiting_tcp* w = outnet->tcp_wait_first; 238 1.1.1.2 christos struct waiting_tcp* n = NULL; 239 1.1.1.2 christos if(first) unit_assert(outnet->tcp_wait_first == first); 240 1.1.1.2 christos if(last) unit_assert(outnet->tcp_wait_last == last && !last->next_waiting); 241 1.1.1.2 christos for(i=0; w; i++) { 242 1.1.1.2 christos unit_assert(i<total); /* otherwise we are looping */ 243 1.1.1.2 christos unit_assert(w->on_tcp_waiting_list); 244 1.1.1.2 christos n = w->next_waiting; 245 1.1.1.2 christos for(j=0; n; j++) { 246 1.1.1.2 christos unit_assert(j<total-i-1); /* otherwise we are looping */ 247 1.1.1.2 christos unit_assert(n != w); 248 1.1.1.2 christos n = n->next_waiting; 249 1.1.1.2 christos } 250 1.1.1.2 christos w = w->next_waiting; 251 1.1.1.2 christos } 252 1.1.1.2 christos } 253 1.1.1.2 christos 254 1.1.1.2 christos /** clear the tcp waiting list */ 255 1.1.1.2 christos static void waiting_tcp_list_clear(struct outside_network* outnet) 256 1.1.1.2 christos { 257 1.1.1.2 christos struct waiting_tcp* w = outnet->tcp_wait_first, *n = NULL; 258 1.1.1.2 christos if(!w) return; 259 1.1.1.2 christos unit_assert(outnet->tcp_wait_first); 260 1.1.1.2 christos unit_assert(outnet->tcp_wait_last); 261 1.1.1.2 christos while(w) { 262 1.1.1.2 christos n = w->next_waiting; 263 1.1.1.2 christos w->on_tcp_waiting_list = 0; 264 1.1.1.2 christos w->next_waiting = (struct waiting_tcp*)1; /* In purpose faux value */ 265 1.1.1.2 christos w = n; 266 1.1.1.2 christos } 267 1.1.1.2 christos outnet->tcp_wait_first = NULL; 268 1.1.1.2 christos outnet->tcp_wait_last = NULL; 269 1.1.1.2 christos } 270 1.1.1.2 christos 271 1.1.1.2 christos /** check removal of the waiting_tcp element on the given position of total 272 1.1.1.2 christos * elements */ 273 1.1.1.2 christos static void check_waiting_tcp_removal(int is_pop, 274 1.1.1.2 christos struct outside_network* outnet, struct waiting_tcp* store, 275 1.1.1.2 christos size_t position, size_t total) 276 1.1.1.2 christos { 277 1.1.1.2 christos size_t i; 278 1.1.1.2 christos struct waiting_tcp* w; 279 1.1.1.2 christos waiting_tcp_list_clear(outnet); 280 1.1.1.2 christos for(i=0; i<total; i++) { 281 1.1.1.2 christos outnet_waiting_tcp_list_add(outnet, &store[i], 0); 282 1.1.1.2 christos } 283 1.1.1.2 christos check_waiting_tcp_list(outnet, &store[0], &store[total-1], total); 284 1.1.1.2 christos 285 1.1.1.2 christos if(is_pop) { 286 1.1.1.2 christos w = outnet_waiting_tcp_list_pop(outnet); 287 1.1.1.2 christos unit_assert(w); /* please clang-analyser */ 288 1.1.1.2 christos } else { 289 1.1.1.2 christos w = outnet->tcp_wait_first; 290 1.1.1.2 christos for(i=0; i<position; i++) { 291 1.1.1.2 christos unit_assert(w); /* please clang-analyser */ 292 1.1.1.2 christos w = w->next_waiting; 293 1.1.1.2 christos } 294 1.1.1.2 christos unit_assert(w); /* please clang-analyser */ 295 1.1.1.2 christos outnet_waiting_tcp_list_remove(outnet, w); 296 1.1.1.2 christos } 297 1.1.1.2 christos unit_assert(!(w->on_tcp_waiting_list || w->next_waiting)); 298 1.1.1.2 christos 299 1.1.1.2 christos if(position == 0 && total == 1) { 300 1.1.1.2 christos /* the list should be empty */ 301 1.1.1.2 christos check_waiting_tcp_list(outnet, NULL, NULL, total-1); 302 1.1.1.2 christos } else if(position == 0) { 303 1.1.1.2 christos /* first element should be gone */ 304 1.1.1.2 christos check_waiting_tcp_list(outnet, &store[1], &store[total-1], total-1); 305 1.1.1.2 christos } else if(position == total - 1) { 306 1.1.1.2 christos /* last element should be gone */ 307 1.1.1.2 christos check_waiting_tcp_list(outnet, &store[0], &store[total-2], total-1); 308 1.1.1.2 christos } else { 309 1.1.1.2 christos /* an element should be gone */ 310 1.1.1.2 christos check_waiting_tcp_list(outnet, &store[0], &store[total-1], total-1); 311 1.1.1.2 christos } 312 1.1.1.2 christos } 313 1.1.1.2 christos 314 1.1.1.2 christos static void waiting_tcp_list_test(void) 315 1.1.1.2 christos { 316 1.1.1.2 christos size_t i = 0; 317 1.1.1.2 christos struct outside_network outnet; 318 1.1.1.2 christos struct waiting_tcp* w, *t = NULL; 319 1.1.1.2 christos struct waiting_tcp store[MAX_TCP_WAITING_NODES]; 320 1.1.1.2 christos memset(&outnet, 0, sizeof(outnet)); 321 1.1.1.2 christos memset(&store, 0, sizeof(store)); 322 1.1.1.2 christos 323 1.1.1.2 christos /* Check add first on empty list */ 324 1.1.1.2 christos unit_show_func("services/outside_network.c", "outnet_waiting_tcp_list_add_first"); 325 1.1.1.2 christos t = &store[i]; 326 1.1.1.2 christos outnet_waiting_tcp_list_add_first(&outnet, t, 0); 327 1.1.1.2 christos check_waiting_tcp_list(&outnet, t, t, 1); 328 1.1.1.2 christos 329 1.1.1.2 christos /* Check add */ 330 1.1.1.2 christos unit_show_func("services/outside_network.c", "outnet_waiting_tcp_list_add"); 331 1.1.1.2 christos for(i=1; i<MAX_TCP_WAITING_NODES-1; i++) { 332 1.1.1.2 christos w = &store[i]; 333 1.1.1.2 christos outnet_waiting_tcp_list_add(&outnet, w, 0); 334 1.1.1.2 christos } 335 1.1.1.2 christos check_waiting_tcp_list(&outnet, t, w, MAX_TCP_WAITING_NODES-1); 336 1.1.1.2 christos 337 1.1.1.2 christos /* Check add first on populated list */ 338 1.1.1.2 christos unit_show_func("services/outside_network.c", "outnet_waiting_tcp_list_add_first"); 339 1.1.1.2 christos w = &store[i]; 340 1.1.1.2 christos t = outnet.tcp_wait_last; 341 1.1.1.2 christos outnet_waiting_tcp_list_add_first(&outnet, w, 0); 342 1.1.1.2 christos check_waiting_tcp_list(&outnet, w, t, MAX_TCP_WAITING_NODES); 343 1.1.1.2 christos 344 1.1.1.2 christos /* Check removal */ 345 1.1.1.2 christos unit_show_func("services/outside_network.c", "outnet_waiting_tcp_list_remove"); 346 1.1.1.2 christos check_waiting_tcp_removal(0, &outnet, store, 2, 5); 347 1.1.1.2 christos check_waiting_tcp_removal(0, &outnet, store, 1, 3); 348 1.1.1.2 christos check_waiting_tcp_removal(0, &outnet, store, 0, 2); 349 1.1.1.2 christos check_waiting_tcp_removal(0, &outnet, store, 1, 2); 350 1.1.1.2 christos check_waiting_tcp_removal(0, &outnet, store, 0, 1); 351 1.1.1.2 christos 352 1.1.1.2 christos /* Check pop */ 353 1.1.1.2 christos unit_show_func("services/outside_network.c", "outnet_waiting_tcp_list_pop"); 354 1.1.1.2 christos check_waiting_tcp_removal(1, &outnet, store, 0, 3); 355 1.1.1.2 christos check_waiting_tcp_removal(1, &outnet, store, 0, 2); 356 1.1.1.2 christos check_waiting_tcp_removal(1, &outnet, store, 0, 1); 357 1.1.1.2 christos } 358 1.1.1.2 christos 359 1.1.1.2 christos static void check_reuse_write_wait(struct reuse_tcp* reuse, 360 1.1.1.2 christos struct waiting_tcp* first, struct waiting_tcp* last, size_t total) 361 1.1.1.2 christos { 362 1.1.1.2 christos size_t i, j; 363 1.1.1.2 christos struct waiting_tcp* w = reuse->write_wait_first; 364 1.1.1.2 christos struct waiting_tcp* n = NULL; 365 1.1.1.2 christos if(first) unit_assert(reuse->write_wait_first == first && !first->write_wait_prev); 366 1.1.1.2 christos if(last) unit_assert(reuse->write_wait_last == last && !last->write_wait_next); 367 1.1.1.2 christos /* check one way */ 368 1.1.1.2 christos for(i=0; w; i++) { 369 1.1.1.2 christos unit_assert(i<total); /* otherwise we are looping */ 370 1.1.1.2 christos unit_assert(w->write_wait_queued); 371 1.1.1.2 christos n = w->write_wait_next; 372 1.1.1.2 christos for(j=0; n; j++) { 373 1.1.1.2 christos unit_assert(j<total-i-1); /* otherwise we are looping */ 374 1.1.1.2 christos unit_assert(n != w); 375 1.1.1.2 christos n = n->write_wait_next; 376 1.1.1.2 christos } 377 1.1.1.2 christos w = w->write_wait_next; 378 1.1.1.2 christos } 379 1.1.1.2 christos /* check the other way */ 380 1.1.1.2 christos w = reuse->write_wait_last; 381 1.1.1.2 christos for(i=0; w; i++) { 382 1.1.1.2 christos unit_assert(i<total); /* otherwise we are looping */ 383 1.1.1.2 christos unit_assert(w->write_wait_queued); 384 1.1.1.2 christos n = w->write_wait_prev; 385 1.1.1.2 christos for(j=0; n; j++) { 386 1.1.1.2 christos unit_assert(j<total-i-1); /* otherwise we are looping */ 387 1.1.1.2 christos unit_assert(n != w); 388 1.1.1.2 christos n = n->write_wait_prev; 389 1.1.1.2 christos } 390 1.1.1.2 christos w = w->write_wait_prev; 391 1.1.1.2 christos } 392 1.1.1.2 christos } 393 1.1.1.2 christos 394 1.1.1.2 christos /** clear the tcp waiting list */ 395 1.1.1.2 christos static void reuse_write_wait_clear(struct reuse_tcp* reuse) 396 1.1.1.2 christos { 397 1.1.1.2 christos struct waiting_tcp* w = reuse->write_wait_first, *n = NULL; 398 1.1.1.2 christos if(!w) return; 399 1.1.1.2 christos unit_assert(reuse->write_wait_first); 400 1.1.1.2 christos unit_assert(reuse->write_wait_last); 401 1.1.1.2 christos while(w) { 402 1.1.1.2 christos n = w->write_wait_next; 403 1.1.1.2 christos w->write_wait_queued = 0; 404 1.1.1.2 christos w->write_wait_next = (struct waiting_tcp*)1; /* In purpose faux value */ 405 1.1.1.2 christos w->write_wait_prev = (struct waiting_tcp*)1; /* In purpose faux value */ 406 1.1.1.2 christos w = n; 407 1.1.1.2 christos } 408 1.1.1.2 christos reuse->write_wait_first = NULL; 409 1.1.1.2 christos reuse->write_wait_last = NULL; 410 1.1.1.2 christos } 411 1.1.1.2 christos 412 1.1.1.2 christos /** check removal of the reuse_write_wait element on the given position of total 413 1.1.1.2 christos * elements */ 414 1.1.1.2 christos static void check_reuse_write_wait_removal(int is_pop, 415 1.1.1.2 christos struct reuse_tcp* reuse, struct waiting_tcp* store, 416 1.1.1.2 christos size_t position, size_t total) 417 1.1.1.2 christos { 418 1.1.1.2 christos size_t i; 419 1.1.1.2 christos struct waiting_tcp* w; 420 1.1.1.2 christos reuse_write_wait_clear(reuse); 421 1.1.1.2 christos for(i=0; i<total; i++) { 422 1.1.1.2 christos reuse_write_wait_push_back(reuse, &store[i]); 423 1.1.1.2 christos } 424 1.1.1.2 christos check_reuse_write_wait(reuse, &store[0], &store[total-1], total); 425 1.1.1.2 christos 426 1.1.1.2 christos if(is_pop) { 427 1.1.1.2 christos w = reuse_write_wait_pop(reuse); 428 1.1.1.2 christos } else { 429 1.1.1.2 christos w = reuse->write_wait_first; 430 1.1.1.2 christos for(i=0; i<position; i++) w = w->write_wait_next; 431 1.1.1.2 christos reuse_write_wait_remove(reuse, w); 432 1.1.1.2 christos } 433 1.1.1.2 christos unit_assert(!(w->write_wait_queued || w->write_wait_next || w->write_wait_prev)); 434 1.1.1.2 christos 435 1.1.1.2 christos if(position == 0 && total == 1) { 436 1.1.1.2 christos /* the list should be empty */ 437 1.1.1.2 christos check_reuse_write_wait(reuse, NULL, NULL, total-1); 438 1.1.1.2 christos } else if(position == 0) { 439 1.1.1.2 christos /* first element should be gone */ 440 1.1.1.2 christos check_reuse_write_wait(reuse, &store[1], &store[total-1], total-1); 441 1.1.1.2 christos } else if(position == total - 1) { 442 1.1.1.2 christos /* last element should be gone */ 443 1.1.1.2 christos check_reuse_write_wait(reuse, &store[0], &store[total-2], total-1); 444 1.1.1.2 christos } else { 445 1.1.1.2 christos /* an element should be gone */ 446 1.1.1.2 christos check_reuse_write_wait(reuse, &store[0], &store[total-1], total-1); 447 1.1.1.2 christos } 448 1.1.1.2 christos } 449 1.1.1.2 christos 450 1.1.1.2 christos static void reuse_write_wait_test(void) 451 1.1.1.2 christos { 452 1.1.1.2 christos size_t i; 453 1.1.1.2 christos struct reuse_tcp reuse; 454 1.1.1.2 christos struct waiting_tcp store[MAX_TCP_WAITING_NODES]; 455 1.1.1.2 christos struct waiting_tcp* w; 456 1.1.1.2 christos memset(&reuse, 0, sizeof(reuse)); 457 1.1.1.2 christos memset(&store, 0, sizeof(store)); 458 1.1.1.2 christos 459 1.1.1.2 christos /* Check adding */ 460 1.1.1.2 christos unit_show_func("services/outside_network.c", "reuse_write_wait_push_back"); 461 1.1.1.2 christos for(i=0; i<MAX_TCP_WAITING_NODES; i++) { 462 1.1.1.2 christos w = &store[i]; 463 1.1.1.2 christos reuse_write_wait_push_back(&reuse, w); 464 1.1.1.2 christos } 465 1.1.1.2 christos check_reuse_write_wait(&reuse, &store[0], w, MAX_TCP_WAITING_NODES); 466 1.1.1.2 christos 467 1.1.1.2 christos /* Check removal */ 468 1.1.1.2 christos unit_show_func("services/outside_network.c", "reuse_write_wait_remove"); 469 1.1.1.2 christos check_reuse_write_wait_removal(0, &reuse, store, 2, 5); 470 1.1.1.2 christos check_reuse_write_wait_removal(0, &reuse, store, 1, 3); 471 1.1.1.2 christos check_reuse_write_wait_removal(0, &reuse, store, 0, 2); 472 1.1.1.2 christos check_reuse_write_wait_removal(0, &reuse, store, 1, 2); 473 1.1.1.2 christos check_reuse_write_wait_removal(0, &reuse, store, 0, 1); 474 1.1.1.2 christos 475 1.1.1.2 christos /* Check pop */ 476 1.1.1.2 christos unit_show_func("services/outside_network.c", "reuse_write_wait_pop"); 477 1.1.1.2 christos check_reuse_write_wait_removal(1, &reuse, store, 0, 3); 478 1.1.1.2 christos check_reuse_write_wait_removal(1, &reuse, store, 0, 2); 479 1.1.1.2 christos check_reuse_write_wait_removal(1, &reuse, store, 0, 1); 480 1.1.1.2 christos } 481 1.1.1.2 christos 482 1.1 christos void tcpreuse_test(void) 483 1.1 christos { 484 1.1 christos unit_show_feature("tcp_reuse"); 485 1.1 christos tcpid_test(); 486 1.1 christos tcp_reuse_tree_list_test(); 487 1.1.1.2 christos waiting_tcp_list_test(); 488 1.1.1.2 christos reuse_write_wait_test(); 489 1.1 christos } 490