/* Copyright (c) 2017 - 2022 LiteSpeed Technologies Inc. See LICENSE. */ /* * lsquic_attq.c -- Advisory Tick Time Queue * * This is a collection of connections kept in a binary heap, the top * element having the minimum advsory time. To speed up removal, each * element has an index it has in the heap array. The index is updated * as elements are moved around in the array when heap is updated. */ #include #include #ifdef WIN32 #include #endif #include #include "lsquic.h" #include "lsquic_types.h" #include "lsquic_int_types.h" #include "lsquic_attq.h" #include "lsquic_packet_common.h" #include "lsquic_alarmset.h" #include "lsquic_malo.h" #include "lsquic_hash.h" #include "lsquic_conn.h" struct attq { struct malo *aq_elem_malo; struct attq_elem **aq_heap; unsigned aq_nelem; unsigned aq_nalloc; }; struct attq * lsquic_attq_create (void) { struct attq *q; struct malo *malo; malo = lsquic_malo_create(sizeof(struct attq_elem)); if (!malo) return NULL; q = calloc(1, sizeof(*q)); if (!q) { lsquic_malo_destroy(malo); return NULL; } q->aq_elem_malo = malo; return q; } void lsquic_attq_destroy (struct attq *q) { lsquic_malo_destroy(q->aq_elem_malo); free(q->aq_heap); free(q); } #define AE_PARENT(i) ((i - 1) / 2) #define AE_LCHILD(i) (2 * i + 1) #define AE_RCHILD(i) (2 * i + 2) #if LSQUIC_EXTRA_CHECKS && !defined(NDEBUG) static void attq_verify (struct attq *q) { unsigned i; for (i = 0; i < q->aq_nelem; ++i) { assert(q->aq_heap[i]->ae_heap_idx == i); if (AE_LCHILD(i) < q->aq_nelem) assert(q->aq_heap[i]->ae_adv_time <= q->aq_heap[AE_LCHILD(i)]->ae_adv_time); if (AE_RCHILD(i) < q->aq_nelem) assert(q->aq_heap[i]->ae_adv_time <= q->aq_heap[AE_RCHILD(i)]->ae_adv_time); } } #else #define attq_verify(q) #endif static void attq_swap (struct attq *q, unsigned a, unsigned b) { struct attq_elem *el; el = q->aq_heap[ a ]; q->aq_heap[ a ] = q->aq_heap[ b ]; q->aq_heap[ b ] = el; q->aq_heap[ a ]->ae_heap_idx = a; q->aq_heap[ b ]->ae_heap_idx = b; } int lsquic_attq_add (struct attq *q, struct lsquic_conn *conn, lsquic_time_t advisory_time, enum ae_why why) { struct attq_elem *el, **heap; unsigned n, i; if (q->aq_nelem >= q->aq_nalloc) { if (q->aq_nalloc > 0) n = q->aq_nalloc * 2; else n = 8; heap = realloc(q->aq_heap, n * sizeof(q->aq_heap[0])); if (!heap) return -1; q->aq_heap = heap; q->aq_nalloc = n; } el = lsquic_malo_get(q->aq_elem_malo); if (!el) return -1; el->ae_adv_time = advisory_time; el->ae_why = why; /* The only place linkage between conn and attq_elem occurs: */ el->ae_conn = conn; conn->cn_attq_elem = el; el->ae_heap_idx = q->aq_nelem; q->aq_heap[ q->aq_nelem++ ] = el; i = q->aq_nelem - 1; while (i > 0 && q->aq_heap[ AE_PARENT(i) ]->ae_adv_time >= q->aq_heap[ i ]->ae_adv_time) { attq_swap(q, i, AE_PARENT(i)); i = AE_PARENT(i); } attq_verify(q); return 0; } struct lsquic_conn * lsquic_attq_pop (struct attq *q, lsquic_time_t cutoff) { struct lsquic_conn *conn; struct attq_elem *el; if (q->aq_nelem == 0) return NULL; el = q->aq_heap[0]; if (el->ae_adv_time >= cutoff) return NULL; conn = el->ae_conn; lsquic_attq_remove(q, conn); return conn; } static void attq_heapify (struct attq *q, unsigned i) { unsigned smallest; assert(i < q->aq_nelem); if (AE_LCHILD(i) < q->aq_nelem) { if (q->aq_heap[ AE_LCHILD(i) ]->ae_adv_time < q->aq_heap[ i ]->ae_adv_time) smallest = AE_LCHILD(i); else smallest = i; if (AE_RCHILD(i) < q->aq_nelem && q->aq_heap[ AE_RCHILD(i) ]->ae_adv_time < q->aq_heap[ smallest ]->ae_adv_time) smallest = AE_RCHILD(i); } else smallest = i; if (smallest != i) { attq_swap(q, smallest, i); attq_heapify(q, smallest); } } void lsquic_attq_remove (struct attq *q, struct lsquic_conn *conn) { struct attq_elem *el; unsigned idx; el = conn->cn_attq_elem; idx = el->ae_heap_idx; assert(q->aq_nelem > 0); assert(q->aq_heap[idx] == el); assert(conn->cn_attq_elem == el); conn->cn_attq_elem = NULL; q->aq_heap[ idx ] = q->aq_heap[ --q->aq_nelem ]; q->aq_heap[ idx ]->ae_heap_idx = idx; if (idx > 0 && q->aq_heap[ idx ]->ae_adv_time < q->aq_heap[ AE_PARENT(idx) ]->ae_adv_time) { do { attq_swap(q, idx, AE_PARENT(idx)); idx = AE_PARENT(idx); } while (idx > 0 && q->aq_heap[ idx ]->ae_adv_time < q->aq_heap[ AE_PARENT(idx) ]->ae_adv_time); } else if (q->aq_nelem > 1 && idx < q->aq_nelem) attq_heapify(q, idx); lsquic_malo_put(el); attq_verify(q); } unsigned lsquic_attq_count_before (struct attq *q, lsquic_time_t cutoff) { unsigned level, total_count, level_count, i, level_max; total_count = 0; for (i = 0, level = 0;; ++level) { level_count = 0; level_max = i + (1U << level); for ( ; i < level_max && i < q->aq_nelem; ++i) level_count += q->aq_heap[i]->ae_adv_time < cutoff; total_count += level_count; if (level_count < (1U << level)) return total_count; } assert(0); return total_count; } const struct attq_elem * lsquic_attq_next (struct attq *q) { if (q->aq_nelem > 0) return q->aq_heap[0]; else return NULL; } const char * lsquic_attq_why2str (enum ae_why why) { switch (why) { case AEW_PACER: return "PACER"; case AEW_MINI_EXPIRE: return "MINI-EXPIRE"; default: why -= N_AEWS; if ((unsigned) why < (unsigned) MAX_LSQUIC_ALARMS) return lsquic_alid2str[why]; return "UNKNOWN"; } }