You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

223 lines
7.1 KiB

// twheel.c — Tickless hierarchical timing wheel (adapted from timeout.c)
// Original: Copyright (c) 2013-2014 William Ahern, MIT license.
#include <limits.h>
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include <sys/queue.h>
#include "twheel.h"
#include "mem.h"
#include "debug_config.h"
#define WHEEL_BIT 6
#define WHEEL_NUM 4
#define WHEEL_LEN (1U << WHEEL_BIT)
#define WHEEL_MAX (WHEEL_LEN - 1)
#define WHEEL_MASK (WHEEL_LEN - 1)
#include "twheel_bitops.c"
#define ctz(n) ctz64(n)
#define clz(n) clz64(n)
#define fls(n) ((int)(64 - clz64(n)))
typedef uint64_t wheel_t;
#define WHEEL_C(n) UINT64_C(n)
#define countof(a) (sizeof(a) / sizeof *(a))
#ifndef TAILQ_CONCAT
#define TAILQ_CONCAT(head1, head2, field) do { \
if (!TAILQ_EMPTY(head2)) { \
*(head1)->tqh_last = (head2)->tqh_first; \
(head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
(head1)->tqh_last = (head2)->tqh_last; \
TAILQ_INIT((head2)); \
} \
} while (0)
#endif
#ifndef TAILQ_FOREACH_SAFE
#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = TAILQ_FIRST(head); \
(var) && ((tvar) = TAILQ_NEXT(var, field), 1); \
(var) = (tvar))
#endif
#ifndef MAX
#define MAX(a,b) (((a)>(b))?(a):(b))
#endif
#ifndef MIN
#define MIN(a,b) (((a)<(b))?(a):(b))
#endif
static inline wheel_t rotl(const wheel_t v, int c) {
if (!(c &= (sizeof v * CHAR_BIT - 1))) return v;
return (v << c) | (v >> (sizeof v * CHAR_BIT - c));
}
static inline wheel_t rotr(const wheel_t v, int c) {
if (!(c &= (sizeof v * CHAR_BIT - 1))) return v;
return (v >> c) | (v << (sizeof v * CHAR_BIT - c));
}
static struct twheels *twheels_init(struct twheels *T, twheel_t hz) {
for (unsigned i = 0; i < WHEEL_NUM; i++) {
for (unsigned j = 0; j < WHEEL_LEN; j++) {
TAILQ_INIT(&T->wheel[i][j]);
}
}
TAILQ_INIT(&T->expired);
for (unsigned i = 0; i < WHEEL_NUM; i++) T->pending[i] = 0;
T->curtime = 0;
T->hertz = hz ? hz : TWHEEL_mHZ;
return T;
}
struct twheels *twheels_open(twheel_t hz) {
struct twheels *T = u_malloc(sizeof *T);
if (!T) { DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "twheels_open: malloc failed"); return NULL; }
return twheels_init(T, hz);
}
void twheels_close(struct twheels *T) {
if (!T) return;
struct twheel_list reset;
TAILQ_INIT(&reset);
for (unsigned i = 0; i < WHEEL_NUM; i++)
for (unsigned j = 0; j < WHEEL_LEN; j++)
TAILQ_CONCAT(&reset, &T->wheel[i][j], tqe);
TAILQ_CONCAT(&reset, &T->expired, tqe);
struct twheel *to;
TAILQ_FOREACH(to, &reset, tqe) to->pending = NULL;
u_free(T);
}
twheel_t twheels_hz(struct twheels *T) { return T->hertz; }
void twheels_del(struct twheels *T, struct twheel *to) {
if (to->pending) {
TAILQ_REMOVE(to->pending, to, tqe);
if (to->pending != &T->expired && TAILQ_EMPTY(to->pending)) {
ptrdiff_t index = to->pending - &T->wheel[0][0];
int w = index / WHEEL_LEN, slot = index % WHEEL_LEN;
T->pending[w] &= ~(WHEEL_C(1) << slot);
}
to->pending = NULL;
}
}
static inline int timeout_wheel(twheel_t timeout) {
return (fls(MIN(timeout, TWHEEL_MAX)) - 1) / WHEEL_BIT;
}
static inline int timeout_slot(int wheel, twheel_t expires) {
return WHEEL_MASK & ((expires >> (wheel * WHEEL_BIT)) - !!wheel);
}
static void twheels_sched(struct twheels *T, struct twheel *to, twheel_t expires) {
twheels_del(T, to);
to->expires = expires;
if (expires > T->curtime) {
twheel_t rem = expires - T->curtime;
int wheel = timeout_wheel(rem);
int slot = timeout_slot(wheel, to->expires);
to->pending = &T->wheel[wheel][slot];
TAILQ_INSERT_TAIL(to->pending, to, tqe);
T->pending[wheel] |= WHEEL_C(1) << slot;
} else {
to->pending = &T->expired;
TAILQ_INSERT_TAIL(to->pending, to, tqe);
}
}
void twheels_add(struct twheels *T, struct twheel *to, twheel_t timeout) {
if (to->flags & TWHEEL_INT) to->interval = MAX(1, timeout);
if (to->flags & TWHEEL_ABS) twheels_sched(T, to, timeout);
else twheels_sched(T, to, T->curtime + timeout);
}
void twheels_update(struct twheels *T, twheel_t curtime) {
twheel_t elapsed = curtime - T->curtime;
struct twheel_list todo;
TAILQ_INIT(&todo);
for (int wheel = 0; wheel < WHEEL_NUM; wheel++) {
wheel_t pending;
if ((elapsed >> (wheel * WHEEL_BIT)) > WHEEL_MAX) {
pending = (wheel_t)~WHEEL_C(0);
} else {
wheel_t _elapsed = WHEEL_MASK & (elapsed >> (wheel * WHEEL_BIT));
int oslot = WHEEL_MASK & (T->curtime >> (wheel * WHEEL_BIT));
pending = rotl(((UINT64_C(1) << _elapsed) - 1), oslot);
int nslot = WHEEL_MASK & (curtime >> (wheel * WHEEL_BIT));
pending |= rotr(rotl(((WHEEL_C(1) << _elapsed) - 1), nslot), _elapsed);
pending |= WHEEL_C(1) << nslot;
}
while (pending & T->pending[wheel]) {
int slot = ctz(pending & T->pending[wheel]);
TAILQ_CONCAT(&todo, &T->wheel[wheel][slot], tqe);
T->pending[wheel] &= ~(UINT64_C(1) << slot);
}
if (!(0x1 & pending)) break;
elapsed = MAX(elapsed, (WHEEL_LEN << (wheel * WHEEL_BIT)));
}
T->curtime = curtime;
struct twheel *to;
while (!TAILQ_EMPTY(&todo)) {
to = TAILQ_FIRST(&todo);
TAILQ_REMOVE(&todo, to, tqe);
to->pending = NULL;
twheels_sched(T, to, to->expires);
}
}
bool twheels_pending(struct twheels *T) {
wheel_t pending = 0;
for (int wheel = 0; wheel < WHEEL_NUM; wheel++) pending |= T->pending[wheel];
return !!pending;
}
bool twheels_expired(struct twheels *T) { return !TAILQ_EMPTY(&T->expired); }
static twheel_t twheels_int(struct twheels *T) {
twheel_t timeout = ~TWHEEL_C(0), _timeout, relmask = 0;
for (int wheel = 0; wheel < WHEEL_NUM; wheel++) {
if (T->pending[wheel]) {
int slot = WHEEL_MASK & (T->curtime >> (wheel * WHEEL_BIT));
_timeout = (ctz(rotr(T->pending[wheel], slot)) + !!wheel) << (wheel * WHEEL_BIT);
_timeout -= relmask & T->curtime;
timeout = MIN(_timeout, timeout);
}
relmask <<= WHEEL_BIT;
relmask |= WHEEL_MASK;
}
return timeout;
}
twheel_t twheels_timeout(struct twheels *T) {
if (!TAILQ_EMPTY(&T->expired)) return 0;
return twheels_int(T);
}
struct twheel *twheels_get(struct twheels *T) {
if (!TAILQ_EMPTY(&T->expired)) {
struct twheel *to = TAILQ_FIRST(&T->expired);
TAILQ_REMOVE(&T->expired, to, tqe);
to->pending = NULL;
return to;
}
return NULL;
}
struct twheel *twheel_init(struct twheel *to, int flags) {
memset(to, 0, sizeof *to);
to->flags = flags;
return to;
}
bool twheel_pending(struct twheel *to) { return to->pending != NULL; }