/* Copyright (c) 2017 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 #include "lsquic.h" #include "lsquic_types.h" #include "lsquic_int_types.h" #include "lsquic_attq.h" #include "lsquic_malo.h" #include "lsquic_conn.h" struct attq { struct malo *aq_elem_malo; struct attq_elem **aq_heap; lsquic_time_t aq_min; unsigned aq_nelem; unsigned aq_nalloc; }; struct attq * 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 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) #ifndef 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 int attq_maybe_add (struct attq *q, struct lsquic_conn *conn, lsquic_time_t advisory_time) { if (advisory_time < q->aq_min) return 1; else return attq_add(q, conn, advisory_time); } 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 attq_add (struct attq *q, struct lsquic_conn *conn, lsquic_time_t advisory_time) { 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; /* 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 * 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; 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 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; lsquic_malo_put(el); 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); attq_verify(q); } unsigned 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); } const lsquic_time_t * attq_next_time (struct attq *q) { if (q->aq_nelem > 0) return &q->aq_heap[0]->ae_adv_time; else return NULL; } lsquic_time_t attq_set_min (struct attq *q, lsquic_time_t new_min) { lsquic_time_t prev_value; prev_value = q->aq_min; q->aq_min = new_min; return prev_value; }