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