283 lines
6.1 KiB
C
283 lines
6.1 KiB
C
/* 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 <assert.h>
|
|
#include <stdlib.h>
|
|
|
|
#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;
|
|
}
|