lsquic_attq.c revision 229fce07
1/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */ 2/* 3 * lsquic_attq.c -- Advisory Tick Time Queue 4 * 5 * This is a collection of connections kept in a binary heap, the top 6 * element having the minimum advsory time. To speed up removal, each 7 * element has an index it has in the heap array. The index is updated 8 * as elements are moved around in the array when heap is updated. 9 */ 10 11#include <assert.h> 12#include <stdlib.h> 13#ifdef WIN32 14#include <vc_compat.h> 15#endif 16 17#include "lsquic.h" 18#include "lsquic_types.h" 19#include "lsquic_int_types.h" 20#include "lsquic_attq.h" 21#include "lsquic_malo.h" 22#include "lsquic_conn.h" 23 24 25struct attq 26{ 27 struct malo *aq_elem_malo; 28 struct attq_elem **aq_heap; 29 unsigned aq_nelem; 30 unsigned aq_nalloc; 31}; 32 33 34struct attq * 35attq_create (void) 36{ 37 struct attq *q; 38 struct malo *malo; 39 40 malo = lsquic_malo_create(sizeof(struct attq_elem)); 41 if (!malo) 42 return NULL; 43 44 q = calloc(1, sizeof(*q)); 45 if (!q) 46 { 47 lsquic_malo_destroy(malo); 48 return NULL; 49 } 50 51 q->aq_elem_malo = malo; 52 return q; 53} 54 55 56void 57attq_destroy (struct attq *q) 58{ 59 lsquic_malo_destroy(q->aq_elem_malo); 60 free(q->aq_heap); 61 free(q); 62} 63 64 65 66#define AE_PARENT(i) ((i - 1) / 2) 67#define AE_LCHILD(i) (2 * i + 1) 68#define AE_RCHILD(i) (2 * i + 2) 69 70 71#ifndef NDEBUG 72static void 73attq_verify (struct attq *q) 74{ 75 unsigned i; 76 77 for (i = 0; i < q->aq_nelem; ++i) 78 { 79 assert(q->aq_heap[i]->ae_heap_idx == i); 80 if (AE_LCHILD(i) < q->aq_nelem) 81 assert(q->aq_heap[i]->ae_adv_time <= 82 q->aq_heap[AE_LCHILD(i)]->ae_adv_time); 83 if (AE_RCHILD(i) < q->aq_nelem) 84 assert(q->aq_heap[i]->ae_adv_time <= 85 q->aq_heap[AE_RCHILD(i)]->ae_adv_time); 86 } 87} 88#else 89#define attq_verify(q) 90#endif 91 92 93static void 94attq_swap (struct attq *q, unsigned a, unsigned b) 95{ 96 struct attq_elem *el; 97 98 el = q->aq_heap[ a ]; 99 q->aq_heap[ a ] = q->aq_heap[ b ]; 100 q->aq_heap[ b ] = el; 101 q->aq_heap[ a ]->ae_heap_idx = a; 102 q->aq_heap[ b ]->ae_heap_idx = b; 103} 104 105 106int 107attq_add (struct attq *q, struct lsquic_conn *conn, 108 lsquic_time_t advisory_time) 109{ 110 struct attq_elem *el, **heap; 111 unsigned n, i; 112 113 if (q->aq_nelem >= q->aq_nalloc) 114 { 115 if (q->aq_nalloc > 0) 116 n = q->aq_nalloc * 2; 117 else 118 n = 8; 119 heap = realloc(q->aq_heap, n * sizeof(q->aq_heap[0])); 120 if (!heap) 121 return -1; 122 q->aq_heap = heap; 123 q->aq_nalloc = n; 124 } 125 126 el = lsquic_malo_get(q->aq_elem_malo); 127 if (!el) 128 return -1; 129 el->ae_adv_time = advisory_time; 130 131 /* The only place linkage between conn and attq_elem occurs: */ 132 el->ae_conn = conn; 133 conn->cn_attq_elem = el; 134 135 el->ae_heap_idx = q->aq_nelem; 136 q->aq_heap[ q->aq_nelem++ ] = el; 137 138 i = q->aq_nelem - 1; 139 while (i > 0 && q->aq_heap[ AE_PARENT(i) ]->ae_adv_time >= 140 q->aq_heap[ i ]->ae_adv_time) 141 { 142 attq_swap(q, i, AE_PARENT(i)); 143 i = AE_PARENT(i); 144 } 145 146 attq_verify(q); 147 148 return 0; 149} 150 151 152struct lsquic_conn * 153attq_pop (struct attq *q, lsquic_time_t cutoff) 154{ 155 struct lsquic_conn *conn; 156 struct attq_elem *el; 157 158 if (q->aq_nelem == 0) 159 return NULL; 160 161 el = q->aq_heap[0]; 162 if (el->ae_adv_time >= cutoff) 163 return NULL; 164 165 conn = el->ae_conn; 166 attq_remove(q, conn); 167 return conn; 168} 169 170 171static void 172attq_heapify (struct attq *q, unsigned i) 173{ 174 unsigned smallest; 175 176 assert(i < q->aq_nelem); 177 178 if (AE_LCHILD(i) < q->aq_nelem) 179 { 180 if (q->aq_heap[ AE_LCHILD(i) ]->ae_adv_time < 181 q->aq_heap[ i ]->ae_adv_time) 182 smallest = AE_LCHILD(i); 183 else 184 smallest = i; 185 if (AE_RCHILD(i) < q->aq_nelem && 186 q->aq_heap[ AE_RCHILD(i) ]->ae_adv_time < 187 q->aq_heap[ smallest ]->ae_adv_time) 188 smallest = AE_RCHILD(i); 189 } 190 else 191 smallest = i; 192 193 if (smallest != i) 194 { 195 attq_swap(q, smallest, i); 196 attq_heapify(q, smallest); 197 } 198} 199 200 201void 202attq_remove (struct attq *q, struct lsquic_conn *conn) 203{ 204 struct attq_elem *el; 205 unsigned idx; 206 207 el = conn->cn_attq_elem; 208 idx = el->ae_heap_idx; 209 210 assert(q->aq_nelem > 0); 211 assert(q->aq_heap[idx] == el); 212 assert(conn->cn_attq_elem == el); 213 214 conn->cn_attq_elem = NULL; 215 lsquic_malo_put(el); 216 217 q->aq_heap[ idx ] = q->aq_heap[ --q->aq_nelem ]; 218 q->aq_heap[ idx ]->ae_heap_idx = idx; 219 if (idx > 0 && q->aq_heap[ idx ]->ae_adv_time < 220 q->aq_heap[ AE_PARENT(idx) ]->ae_adv_time) 221 { 222 do 223 { 224 attq_swap(q, idx, AE_PARENT(idx)); 225 idx = AE_PARENT(idx); 226 } 227 while (idx > 0 && q->aq_heap[ idx ]->ae_adv_time < 228 q->aq_heap[ AE_PARENT(idx) ]->ae_adv_time); 229 } 230 else if (q->aq_nelem > 1 && idx < q->aq_nelem) 231 attq_heapify(q, idx); 232 attq_verify(q); 233} 234 235 236unsigned 237attq_count_before (struct attq *q, lsquic_time_t cutoff) 238{ 239 unsigned level, total_count, level_count, i, level_max; 240 241 total_count = 0; 242 for (i = 0, level = 0;; ++level) 243 { 244 level_count = 0; 245 level_max = i + (1U << level); 246 for ( ; i < level_max && i < q->aq_nelem; ++i) 247 level_count += q->aq_heap[i]->ae_adv_time < cutoff; 248 total_count += level_count; 249 if (level_count < (1U << level)) 250 return total_count; 251 } 252 assert(0); 253} 254 255 256const lsquic_time_t * 257attq_next_time (struct attq *q) 258{ 259 if (q->aq_nelem > 0) 260 return &q->aq_heap[0]->ae_adv_time; 261 else 262 return NULL; 263} 264