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