Add session timer

This commit is contained in:
luwenpeng
2023-12-12 18:41:53 +08:00
parent 006315fb7c
commit 2d3e182b5a
11 changed files with 612 additions and 234 deletions

View File

@@ -1,3 +1,2 @@
set(CMAKE_C_FLAGS "-std=c99")
add_definitions(-fPIC)
add_library(timeout STATIC timeout.c timeout-bitops.c)
add_library(timeout STATIC timeout.cpp timeout-bitops.cpp)
target_include_directories(timeout PUBLIC ${CMAKE_CURRENT_LIST_DIR})

View File

@@ -1,6 +1,6 @@
#include <stdint.h>
#ifdef _MSC_VER
#include <intrin.h> /* _BitScanForward, _BitScanReverse */
#include <intrin.h> /* _BitScanForward, _BitScanReverse */
#endif
/* First define ctz and clz functions; these are compiler-dependent if
@@ -54,14 +54,14 @@ static __inline int clz64(uint64_t val)
#else
static __inline int ctz64(uint64_t val)
{
uint32_t lo = (uint32_t) val;
uint32_t hi = (uint32_t) (val >> 32);
uint32_t lo = (uint32_t)val;
uint32_t hi = (uint32_t)(val >> 32);
return lo ? ctz32(lo) : 32 + ctz32(hi);
}
static __inline int clz64(uint64_t val)
{
uint32_t lo = (uint32_t) val;
uint32_t hi = (uint32_t) (val >> 32);
uint32_t lo = (uint32_t)val;
uint32_t hi = (uint32_t)(val >> 32);
return hi ? clz32(hi) : 32 + clz32(lo);
}
#endif
@@ -72,9 +72,12 @@ static __inline int clz64(uint64_t val)
/* TODO: There are more clever ways to do this in the generic case. */
#define process_(one, cz_bits, bits) \
if (x < ( one << (cz_bits - bits))) { rv += bits; x <<= bits; }
#define process_(one, cz_bits, bits) \
if (x < (one << (cz_bits - bits))) \
{ \
rv += bits; \
x <<= bits; \
}
#define process64(bits) process_((UINT64_C(1)), 64, (bits))
static inline int clz64(uint64_t x)
@@ -105,8 +108,12 @@ static inline int clz32(uint32_t x)
#undef process_
#undef process32
#undef process64
#define process_(one, bits) \
if ((x & ((one << (bits))-1)) == 0) { rv += bits; x >>= bits; }
#define process_(one, bits) \
if ((x & ((one << (bits)) - 1)) == 0) \
{ \
rv += bits; \
x >>= bits; \
}
#define process64(bits) process_((UINT64_C(1)), bits)
static inline int ctz64(uint64_t x)
@@ -152,15 +159,15 @@ static uint64_t testcases[] = {
100,
385789752,
82574,
(((uint64_t)1)<<63) + (((uint64_t)1)<<31) + 10101
};
(((uint64_t)1) << 63) + (((uint64_t)1) << 31) + 10101};
static int
naive_clz(int bits, uint64_t v)
{
int r = 0;
uint64_t bit = ((uint64_t)1) << (bits-1);
while (bit && 0 == (v & bit)) {
uint64_t bit = ((uint64_t)1) << (bits - 1);
while (bit && 0 == (v & bit))
{
r++;
bit >>= 1;
}
@@ -173,7 +180,8 @@ naive_ctz(int bits, uint64_t v)
{
int r = 0;
uint64_t bit = 1;
while (bit && 0 == (v & bit)) {
while (bit && 0 == (v & bit))
{
r++;
bit <<= 1;
if (r == bits)
@@ -186,46 +194,50 @@ naive_ctz(int bits, uint64_t v)
static int
check(uint64_t vv)
{
uint32_t v32 = (uint32_t) vv;
uint32_t v32 = (uint32_t)vv;
if (vv == 0)
return 1; /* c[tl]z64(0) is undefined. */
if (ctz64(vv) != naive_ctz(64, vv)) {
if (ctz64(vv) != naive_ctz(64, vv))
{
printf("mismatch with ctz64: %d\n", ctz64(vv));
exit(1);
exit(1);
return 0;
}
if (clz64(vv) != naive_clz(64, vv)) {
if (clz64(vv) != naive_clz(64, vv))
{
printf("mismatch with clz64: %d\n", clz64(vv));
exit(1);
exit(1);
return 0;
}
if (v32 == 0)
return 1; /* c[lt]z(0) is undefined. */
if (ctz32(v32) != naive_ctz(32, v32)) {
if (ctz32(v32) != naive_ctz(32, v32))
{
printf("mismatch with ctz32: %d\n", ctz32(v32));
exit(1);
return 0;
}
if (clz32(v32) != naive_clz(32, v32)) {
if (clz32(v32) != naive_clz(32, v32))
{
printf("mismatch with clz32: %d\n", clz32(v32));
exit(1);
exit(1);
return 0;
}
return 1;
}
int
main(int c, char **v)
int main(int c, char **v)
{
unsigned int i;
const unsigned int n = sizeof(testcases)/sizeof(testcases[0]);
const unsigned int n = sizeof(testcases) / sizeof(testcases[0]);
int result = 0;
for (i = 0; i <= 63; ++i) {
for (i = 0; i <= 63; ++i)
{
uint64_t x = 1 << i;
if (!check(x))
result = 1;
@@ -234,16 +246,19 @@ main(int c, char **v)
result = 1;
}
for (i = 0; i < n; ++i) {
if (! check(testcases[i]))
for (i = 0; i < n; ++i)
{
if (!check(testcases[i]))
result = 1;
}
if (result) {
if (result)
{
puts("FAIL");
} else {
}
else
{
puts("OK");
}
return result;
}
#endif

View File

@@ -23,17 +23,17 @@
* USE OR OTHER DEALINGS IN THE SOFTWARE.
* ==========================================================================
*/
#include <limits.h> /* CHAR_BIT */
#include <limits.h> /* CHAR_BIT */
#include <stddef.h> /* NULL */
#include <stdlib.h> /* malloc(3) free(3) */
#include <stdio.h> /* FILE fprintf(3) */
#include <stddef.h> /* NULL */
#include <stdlib.h> /* malloc(3) free(3) */
#include <stdio.h> /* FILE fprintf(3) */
#include <inttypes.h> /* UINT64_C uint64_t */
#include <inttypes.h> /* UINT64_C uint64_t */
#include <string.h> /* memset(3) */
#include <string.h> /* memset(3) */
#include <errno.h> /* errno */
#include <errno.h> /* errno */
#include <sys/queue.h> /* TAILQ(3) */
@@ -58,7 +58,7 @@
#define reltime_t timeout_t /* "" */
#if !defined countof
#define countof(a) (sizeof (a) / sizeof *(a))
#define countof(a) (sizeof(a) / sizeof *(a))
#endif
#if !defined endof
@@ -66,32 +66,34 @@
#endif
#if !defined MIN
#define MIN(a, b) (((a) < (b))? (a) : (b))
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#endif
#if !defined MAX
#define MAX(a, b) (((a) > (b))? (a) : (b))
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#endif
#if !defined 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)
#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
#if !defined 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))
#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = TAILQ_FIRST(head); \
(var) && ((tvar) = TAILQ_NEXT(var, field), 1); \
(var) = (tvar))
#endif
/*
* B I T M A N I P U L A T I O N R O U T I N E S
*
@@ -134,7 +136,7 @@
#define WHEEL_MASK (WHEEL_LEN - 1)
#define TIMEOUT_MAX ((TIMEOUT_C(1) << (WHEEL_BIT * WHEEL_NUM)) - 1)
#include "timeout-bitops.c"
#include "timeout-bitops.cpp"
#if WHEEL_BIT == 6
#define ctz(n) ctz64(n)
@@ -181,23 +183,22 @@ typedef uint8_t wheel_t;
#error invalid WHEEL_BIT value
#endif
static inline wheel_t rotl(const wheel_t v, int c) {
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));
} /* rotl() */
static inline wheel_t rotr(const wheel_t v, int 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));
} /* rotr() */
/*
* T I M E R R O U T I N E S
*
@@ -205,7 +206,8 @@ static inline wheel_t rotr(const wheel_t v, int c) {
TAILQ_HEAD(timeout_list, timeout);
struct timeouts {
struct timeouts
{
struct timeout_list wheel[WHEEL_NUM][WHEEL_LEN], expired;
wheel_t pending[WHEEL_NUM];
@@ -214,33 +216,36 @@ struct timeouts {
timeout_t hertz;
}; /* struct timeouts */
static struct timeouts *timeouts_init(struct timeouts *T, timeout_t hz) {
static struct timeouts *timeouts_init(struct timeouts *T, timeout_t hz)
{
unsigned i, j;
for (i = 0; i < countof(T->wheel); i++) {
for (j = 0; j < countof(T->wheel[i]); j++) {
for (i = 0; i < countof(T->wheel); i++)
{
for (j = 0; j < countof(T->wheel[i]); j++)
{
TAILQ_INIT(&T->wheel[i][j]);
}
}
TAILQ_INIT(&T->expired);
for (i = 0; i < countof(T->pending); i++) {
for (i = 0; i < countof(T->pending); i++)
{
T->pending[i] = 0;
}
T->curtime = 0;
T->hertz = (hz)? hz : TIMEOUT_mHZ;
T->hertz = (hz) ? hz : TIMEOUT_mHZ;
return T;
} /* timeouts_init() */
TIMEOUT_PUBLIC struct timeouts *timeouts_open(timeout_t hz, int *error) {
TIMEOUT_PUBLIC struct timeouts *timeouts_open(timeout_t hz, int *error)
{
struct timeouts *T;
if ((T = malloc(sizeof *T)))
if ((T = (struct timeouts *)malloc(sizeof *T)))
return timeouts_init(T, hz);
*error = errno;
@@ -248,30 +253,33 @@ TIMEOUT_PUBLIC struct timeouts *timeouts_open(timeout_t hz, int *error) {
return NULL;
} /* timeouts_open() */
static void timeouts_reset(struct timeouts *T) {
static void timeouts_reset(struct timeouts *T)
{
struct timeout_list reset;
struct timeout *to;
unsigned i, j;
TAILQ_INIT(&reset);
for (i = 0; i < countof(T->wheel); i++) {
for (j = 0; j < countof(T->wheel[i]); j++) {
for (i = 0; i < countof(T->wheel); i++)
{
for (j = 0; j < countof(T->wheel[i]); j++)
{
TAILQ_CONCAT(&reset, &T->wheel[i][j], tqe);
}
}
TAILQ_CONCAT(&reset, &T->expired, tqe);
TAILQ_FOREACH(to, &reset, tqe) {
TAILQ_FOREACH(to, &reset, tqe)
{
to->pending = NULL;
TO_SET_TIMEOUTS(to, NULL);
}
} /* timeouts_reset() */
TIMEOUT_PUBLIC void timeouts_close(struct timeouts *T) {
TIMEOUT_PUBLIC void timeouts_close(struct timeouts *T)
{
/*
* NOTE: Delete installed timeouts so timeout_pending() and
* timeout_expired() worked as expected.
@@ -281,17 +289,19 @@ TIMEOUT_PUBLIC void timeouts_close(struct timeouts *T) {
free(T);
} /* timeouts_close() */
TIMEOUT_PUBLIC timeout_t timeouts_hz(struct timeouts *T) {
TIMEOUT_PUBLIC timeout_t timeouts_hz(struct timeouts *T)
{
return T->hertz;
} /* timeouts_hz() */
TIMEOUT_PUBLIC void timeouts_del(struct timeouts *T, struct timeout *to) {
if (to->pending) {
TIMEOUT_PUBLIC void timeouts_del(struct timeouts *T, struct timeout *to)
{
if (to->pending)
{
TAILQ_REMOVE(to->pending, to, tqe);
if (to->pending != &T->expired && TAILQ_EMPTY(to->pending)) {
if (to->pending != &T->expired && TAILQ_EMPTY(to->pending))
{
ptrdiff_t index = to->pending - &T->wheel[0][0];
int wheel = index / WHEEL_LEN;
int slot = index % WHEEL_LEN;
@@ -304,24 +314,24 @@ TIMEOUT_PUBLIC void timeouts_del(struct timeouts *T, struct timeout *to) {
}
} /* timeouts_del() */
static inline reltime_t timeout_rem(struct timeouts *T, struct timeout *to) {
static inline reltime_t timeout_rem(struct timeouts *T, struct timeout *to)
{
return to->expires - T->curtime;
} /* timeout_rem() */
static inline int timeout_wheel(timeout_t timeout) {
static inline int timeout_wheel(timeout_t timeout)
{
/* must be called with timeout != 0, so fls input is nonzero */
return (fls(MIN(timeout, TIMEOUT_MAX)) - 1) / WHEEL_BIT;
} /* timeout_wheel() */
static inline int timeout_slot(int wheel, timeout_t expires) {
static inline int timeout_slot(int wheel, timeout_t expires)
{
return WHEEL_MASK & ((expires >> (wheel * WHEEL_BIT)) - !!wheel);
} /* timeout_slot() */
static void timeouts_sched(struct timeouts *T, struct timeout *to, timeout_t expires) {
static void timeouts_sched(struct timeouts *T, struct timeout *to, timeout_t expires)
{
timeout_t rem;
int wheel, slot;
@@ -331,7 +341,8 @@ static void timeouts_sched(struct timeouts *T, struct timeout *to, timeout_t exp
TO_SET_TIMEOUTS(to, T);
if (expires > T->curtime) {
if (expires > T->curtime)
{
rem = timeout_rem(T, to);
/* rem is nonzero since:
@@ -346,18 +357,21 @@ static void timeouts_sched(struct timeouts *T, struct timeout *to, timeout_t exp
TAILQ_INSERT_TAIL(to->pending, to, tqe);
T->pending[wheel] |= WHEEL_C(1) << slot;
} else {
}
else
{
to->pending = &T->expired;
TAILQ_INSERT_TAIL(to->pending, to, tqe);
}
} /* timeouts_sched() */
#ifndef TIMEOUT_DISABLE_INTERVALS
static void timeouts_readd(struct timeouts *T, struct timeout *to) {
static void timeouts_readd(struct timeouts *T, struct timeout *to)
{
to->expires += to->interval;
if (to->expires <= T->curtime) {
if (to->expires <= T->curtime)
{
/* If we've missed the next firing of this timeout, reschedule
* it to occur at the next multiple of its interval after
* the last time that it fired.
@@ -371,8 +385,8 @@ static void timeouts_readd(struct timeouts *T, struct timeout *to) {
} /* timeouts_readd() */
#endif
TIMEOUT_PUBLIC void timeouts_add(struct timeouts *T, struct timeout *to, timeout_t timeout) {
TIMEOUT_PUBLIC void timeouts_add(struct timeouts *T, struct timeout *to, timeout_t timeout)
{
#ifndef TIMEOUT_DISABLE_INTERVALS
if (to->flags & TIMEOUT_INT)
to->interval = MAX(1, timeout);
@@ -384,8 +398,8 @@ TIMEOUT_PUBLIC void timeouts_add(struct timeouts *T, struct timeout *to, timeout
timeouts_sched(T, to, T->curtime + timeout);
} /* timeouts_add() */
TIMEOUT_PUBLIC void timeouts_update(struct timeouts *T, abstime_t curtime) {
TIMEOUT_PUBLIC void timeouts_update(struct timeouts *T, abstime_t curtime)
{
timeout_t elapsed = curtime - T->curtime;
struct timeout_list todo;
int wheel;
@@ -396,7 +410,8 @@ TIMEOUT_PUBLIC void timeouts_update(struct timeouts *T, abstime_t curtime) {
* There's no avoiding looping over every wheel. It's best to keep
* WHEEL_NUM smallish.
*/
for (wheel = 0; wheel < WHEEL_NUM; wheel++) {
for (wheel = 0; wheel < WHEEL_NUM; wheel++)
{
wheel_t pending;
/*
@@ -413,9 +428,12 @@ TIMEOUT_PUBLIC void timeouts_update(struct timeouts *T, abstime_t curtime) {
* If a wheel rolls over, force a tick of the next higher
* wheel.
*/
if ((elapsed >> (wheel * WHEEL_BIT)) > WHEEL_MAX) {
if ((elapsed >> (wheel * WHEEL_BIT)) > WHEEL_MAX)
{
pending = (wheel_t)~WHEEL_C(0);
} else {
}
else
{
wheel_t _elapsed = WHEEL_MASK & (elapsed >> (wheel * WHEEL_BIT));
int oslot, nslot;
@@ -432,7 +450,8 @@ TIMEOUT_PUBLIC void timeouts_update(struct timeouts *T, abstime_t curtime) {
pending |= WHEEL_C(1) << nslot;
}
while (pending & T->pending[wheel]) {
while (pending & T->pending[wheel])
{
/* ctz input cannot be zero: loop condition. */
int slot = ctz(pending & T->pending[wheel]);
TAILQ_CONCAT(&todo, &T->wheel[wheel][slot], tqe);
@@ -448,7 +467,8 @@ TIMEOUT_PUBLIC void timeouts_update(struct timeouts *T, abstime_t curtime) {
T->curtime = curtime;
while (!TAILQ_EMPTY(&todo)) {
while (!TAILQ_EMPTY(&todo))
{
struct timeout *to = TAILQ_FIRST(&todo);
TAILQ_REMOVE(&todo, to, tqe);
@@ -460,29 +480,29 @@ TIMEOUT_PUBLIC void timeouts_update(struct timeouts *T, abstime_t curtime) {
return;
} /* timeouts_update() */
TIMEOUT_PUBLIC void timeouts_step(struct timeouts *T, reltime_t elapsed) {
TIMEOUT_PUBLIC void timeouts_step(struct timeouts *T, reltime_t elapsed)
{
timeouts_update(T, T->curtime + elapsed);
} /* timeouts_step() */
TIMEOUT_PUBLIC bool timeouts_pending(struct timeouts *T) {
TIMEOUT_PUBLIC bool timeouts_pending(struct timeouts *T)
{
wheel_t pending = 0;
int wheel;
for (wheel = 0; wheel < WHEEL_NUM; wheel++) {
for (wheel = 0; wheel < WHEEL_NUM; wheel++)
{
pending |= T->pending[wheel];
}
return !!pending;
} /* timeouts_pending() */
TIMEOUT_PUBLIC bool timeouts_expired(struct timeouts *T) {
TIMEOUT_PUBLIC bool timeouts_expired(struct timeouts *T)
{
return !TAILQ_EMPTY(&T->expired);
} /* timeouts_expired() */
/*
* Calculate the interval before needing to process any timeouts pending on
* any wheel.
@@ -499,15 +519,18 @@ TIMEOUT_PUBLIC bool timeouts_expired(struct timeouts *T) {
*
* We should never return a timeout larger than the lowest actual timeout.
*/
static timeout_t timeouts_int(struct timeouts *T) {
static timeout_t timeouts_int(struct timeouts *T)
{
timeout_t timeout = ~TIMEOUT_C(0), _timeout;
timeout_t relmask;
int wheel, slot;
relmask = 0;
for (wheel = 0; wheel < WHEEL_NUM; wheel++) {
if (T->pending[wheel]) {
for (wheel = 0; wheel < WHEEL_NUM; wheel++)
{
if (T->pending[wheel])
{
slot = WHEEL_MASK & (T->curtime >> (wheel * WHEEL_BIT));
/* ctz input cannot be zero: T->pending[wheel] is
@@ -521,28 +544,29 @@ static timeout_t timeouts_int(struct timeouts *T) {
timeout = MIN(_timeout, timeout);
}
relmask <<= WHEEL_BIT;
relmask <<= WHEEL_BIT;
relmask |= WHEEL_MASK;
}
return timeout;
} /* timeouts_int() */
/*
* Calculate the interval our caller can wait before needing to process
* events.
*/
TIMEOUT_PUBLIC timeout_t timeouts_timeout(struct timeouts *T) {
TIMEOUT_PUBLIC timeout_t timeouts_timeout(struct timeouts *T)
{
if (!TAILQ_EMPTY(&T->expired))
return 0;
return timeouts_int(T);
} /* timeouts_timeout() */
TIMEOUT_PUBLIC struct timeout *timeouts_get(struct timeouts *T) {
if (!TAILQ_EMPTY(&T->expired)) {
TIMEOUT_PUBLIC struct timeout *timeouts_get(struct timeouts *T)
{
if (!TAILQ_EMPTY(&T->expired))
{
struct timeout *to = TAILQ_FIRST(&T->expired);
TAILQ_REMOVE(&T->expired, to, tqe);
@@ -555,23 +579,28 @@ TIMEOUT_PUBLIC struct timeout *timeouts_get(struct timeouts *T) {
#endif
return to;
} else {
}
else
{
return 0;
}
} /* timeouts_get() */
/*
* Use dumb looping to locate the earliest timeout pending on the wheel so
* our invariant assertions can check the result of our optimized code.
*/
static struct timeout *timeouts_min(struct timeouts *T) {
static struct timeout *timeouts_min(struct timeouts *T)
{
struct timeout *to, *min = NULL;
unsigned i, j;
for (i = 0; i < countof(T->wheel); i++) {
for (j = 0; j < countof(T->wheel[i]); j++) {
TAILQ_FOREACH(to, &T->wheel[i][j], tqe) {
for (i = 0; i < countof(T->wheel); i++)
{
for (j = 0; j < countof(T->wheel[i]); j++)
{
TAILQ_FOREACH(to, &T->wheel[i][j], tqe)
{
if (!min || to->expires < min->expires)
min = to;
}
@@ -581,28 +610,34 @@ static struct timeout *timeouts_min(struct timeouts *T) {
return min;
} /* timeouts_min() */
/*
* Check some basic algorithm invariants. If these invariants fail then
* something is definitely broken.
*/
#define report(...) do { \
if ((fp)) \
fprintf(fp, __VA_ARGS__); \
} while (0)
#define report(...) \
do \
{ \
if ((fp)) \
fprintf(fp, __VA_ARGS__); \
} while (0)
#define check(expr, ...) do { \
if (!(expr)) { \
report(__VA_ARGS__); \
return 0; \
} \
} while (0)
#define check(expr, ...) \
do \
{ \
if (!(expr)) \
{ \
report(__VA_ARGS__); \
return 0; \
} \
} while (0)
TIMEOUT_PUBLIC bool timeouts_check(struct timeouts *T, FILE *fp) {
TIMEOUT_PUBLIC bool timeouts_check(struct timeouts *T, FILE *fp)
{
timeout_t timeout;
struct timeout *to;
if ((to = timeouts_min(T))) {
if ((to = timeouts_min(T)))
{
check(to->expires > T->curtime, "missed timeout (expires:%" TIMEOUT_PRIu " <= curtime:%" TIMEOUT_PRIu ")\n", to->expires, T->curtime);
timeout = timeouts_int(T);
@@ -610,7 +645,9 @@ TIMEOUT_PUBLIC bool timeouts_check(struct timeouts *T, FILE *fp) {
timeout = timeouts_timeout(T);
check(timeout <= to->expires - T->curtime, "wrong soft timeout (soft:%" TIMEOUT_PRIu " > hard:%" TIMEOUT_PRIu ") (expires:%" TIMEOUT_PRIu "; curtime:%" TIMEOUT_PRIu ")\n", timeout, (to->expires - T->curtime), to->expires, T->curtime);
} else {
}
else
{
timeout = timeouts_timeout(T);
if (!TAILQ_EMPTY(&T->expired))
@@ -622,49 +659,65 @@ TIMEOUT_PUBLIC bool timeouts_check(struct timeouts *T, FILE *fp) {
return 1;
} /* timeouts_check() */
#define ENTER \
do \
{ \
static const int pc0 = __LINE__; \
switch (pc0 + it->pc) \
{ \
case __LINE__: \
(void)0
#define ENTER \
do { \
static const int pc0 = __LINE__; \
switch (pc0 + it->pc) { \
case __LINE__: (void)0
#define SAVE_AND_DO(do_statement) \
do { \
it->pc = __LINE__ - pc0; \
do_statement; \
case __LINE__: (void)0; \
#define SAVE_AND_DO(do_statement) \
do \
{ \
it->pc = __LINE__ - pc0; \
do_statement; \
case __LINE__: \
(void)0; \
} while (0)
#define YIELD(rv) \
#define YIELD(rv) \
SAVE_AND_DO(return (rv))
#define LEAVE \
SAVE_AND_DO(break); \
} \
} while (0)
#define LEAVE \
SAVE_AND_DO(break); \
} \
} \
while (0)
TIMEOUT_PUBLIC struct timeout *timeouts_next(struct timeouts *T, struct timeouts_it *it) {
TIMEOUT_PUBLIC struct timeout *timeouts_next(struct timeouts *T, struct timeouts_it *it)
{
struct timeout *to;
ENTER;
if (it->flags & TIMEOUTS_EXPIRED) {
if (it->flags & TIMEOUTS_CLEAR) {
while ((to = timeouts_get(T))) {
if (it->flags & TIMEOUTS_EXPIRED)
{
if (it->flags & TIMEOUTS_CLEAR)
{
while ((to = timeouts_get(T)))
{
YIELD(to);
}
} else {
TAILQ_FOREACH_SAFE(to, &T->expired, tqe, it->to) {
}
else
{
TAILQ_FOREACH_SAFE(to, &T->expired, tqe, it->to)
{
YIELD(to);
}
}
}
if (it->flags & TIMEOUTS_PENDING) {
for (it->i = 0; it->i < countof(T->wheel); it->i++) {
for (it->j = 0; it->j < countof(T->wheel[it->i]); it->j++) {
TAILQ_FOREACH_SAFE(to, &T->wheel[it->i][it->j], tqe, it->to) {
if (it->flags & TIMEOUTS_PENDING)
{
for (it->i = 0; it->i < countof(T->wheel); it->i++)
{
for (it->j = 0; it->j < countof(T->wheel[it->i]); it->j++)
{
TAILQ_FOREACH_SAFE(to, &T->wheel[it->i][it->j], tqe, it->to)
{
YIELD(to);
}
}
@@ -681,13 +734,13 @@ TIMEOUT_PUBLIC struct timeout *timeouts_next(struct timeouts *T, struct timeouts
#undef SAVE_AND_DO
#undef ENTER
/*
* T I M E O U T R O U T I N E S
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
TIMEOUT_PUBLIC struct timeout *timeout_init(struct timeout *to, int flags) {
TIMEOUT_PUBLIC struct timeout *timeout_init(struct timeout *to, int flags)
{
memset(to, 0, sizeof *to);
to->flags = flags;
@@ -695,50 +748,49 @@ TIMEOUT_PUBLIC struct timeout *timeout_init(struct timeout *to, int flags) {
return to;
} /* timeout_init() */
#ifndef TIMEOUT_DISABLE_RELATIVE_ACCESS
TIMEOUT_PUBLIC bool timeout_pending(struct timeout *to) {
TIMEOUT_PUBLIC bool timeout_pending(struct timeout *to)
{
return to->pending && to->pending != &to->timeouts->expired;
} /* timeout_pending() */
TIMEOUT_PUBLIC bool timeout_expired(struct timeout *to) {
TIMEOUT_PUBLIC bool timeout_expired(struct timeout *to)
{
return to->pending && to->pending == &to->timeouts->expired;
} /* timeout_expired() */
TIMEOUT_PUBLIC void timeout_del(struct timeout *to) {
TIMEOUT_PUBLIC void timeout_del(struct timeout *to)
{
timeouts_del(to->timeouts, to);
} /* timeout_del() */
#endif
/*
* V E R S I O N I N T E R F A C E S
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
TIMEOUT_PUBLIC int timeout_version(void) {
TIMEOUT_PUBLIC int timeout_version(void)
{
return TIMEOUT_VERSION;
} /* timeout_version() */
TIMEOUT_PUBLIC const char *timeout_vendor(void) {
TIMEOUT_PUBLIC const char *timeout_vendor(void)
{
return TIMEOUT_VENDOR;
} /* timeout_version() */
TIMEOUT_PUBLIC int timeout_v_rel(void) {
TIMEOUT_PUBLIC int timeout_v_rel(void)
{
return TIMEOUT_V_REL;
} /* timeout_version() */
TIMEOUT_PUBLIC int timeout_v_abi(void) {
TIMEOUT_PUBLIC int timeout_v_abi(void)
{
return TIMEOUT_V_ABI;
} /* timeout_version() */
TIMEOUT_PUBLIC int timeout_v_api(void) {
TIMEOUT_PUBLIC int timeout_v_api(void)
{
return TIMEOUT_V_API;
} /* timeout_version() */

View File

@@ -26,13 +26,12 @@
#ifndef TIMEOUT_H
#define TIMEOUT_H
#include <stdbool.h> /* bool */
#include <stdio.h> /* FILE */
#include <stdbool.h> /* bool */
#include <stdio.h> /* FILE */
#include <inttypes.h> /* PRIu64 PRIx64 PRIX64 uint64_t */
#include <sys/queue.h> /* TAILQ(3) */
#include <inttypes.h> /* PRIu64 PRIx64 PRIX64 uint64_t */
#include <sys/queue.h> /* TAILQ(3) */
/*
* V E R S I O N I N T E R F A C E S
@@ -44,7 +43,7 @@
#endif
#define TIMEOUT_VERSION TIMEOUT_V_REL
#define TIMEOUT_VENDOR "william@25thandClement.com"
#define TIMEOUT_VENDOR "william@25thandClement.com"
#define TIMEOUT_V_REL 0x20160226
#define TIMEOUT_V_ABI 0x20160224
@@ -60,7 +59,6 @@ TIMEOUT_PUBLIC int timeout_v_abi(void);
TIMEOUT_PUBLIC int timeout_v_api(void);
/*
* I N T E G E R T Y P E I N T E R F A C E S
*
@@ -79,7 +77,6 @@ typedef uint64_t timeout_t;
#define timeout_error_t int /* for documentation purposes */
/*
* C A L L B A C K I N T E R F A C E
*
@@ -89,7 +86,8 @@ typedef uint64_t timeout_t;
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef TIMEOUT_CB_OVERRIDE
struct timeout_cb {
struct timeout_cb
{
void (*fn)();
void *arg;
}; /* struct timeout_cb */
@@ -105,14 +103,20 @@ struct timeout_cb {
#endif
#define TIMEOUT_ABS 0x02 /* treat timeout values as absolute */
#define TIMEOUT_INITIALIZER(flags) { (flags) }
#define TIMEOUT_INITIALIZER(flags) \
{ \
(flags) \
}
#define timeout_setcb(to, fn, arg) do { \
(to)->callback.fn = (fn); \
(to)->callback.arg = (arg); \
} while (0)
#define timeout_setcb(to, _fn, _arg) \
do \
{ \
(to)->callback.fn = (_fn); \
(to)->callback.arg = (_arg); \
} while (0)
struct timeout {
struct timeout
{
int flags;
timeout_t expires;
@@ -121,7 +125,8 @@ struct timeout {
struct timeout_list *pending;
/* timeout list if pending on wheel or expiry queue */
TAILQ_ENTRY(timeout) tqe;
TAILQ_ENTRY(timeout)
tqe;
/* entry member for struct timeout_list lists */
#ifndef TIMEOUT_DISABLE_CALLBACKS
@@ -140,14 +145,13 @@ struct timeout {
#endif
}; /* struct timeout */
TIMEOUT_PUBLIC struct timeout *timeout_init(struct timeout *, int);
/* initialize timeout structure (same as TIMEOUT_INITIALIZER) */
#ifndef TIMEOUT_DISABLE_RELATIVE_ACCESS
TIMEOUT_PUBLIC bool timeout_pending(struct timeout *);
/* true if on timing wheel, false otherwise */
TIMEOUT_PUBLIC bool timeout_expired(struct timeout *);
/* true if on expired queue, false otherwise */
@@ -200,17 +204,23 @@ TIMEOUT_PUBLIC bool timeouts_check(struct timeouts *, FILE *);
#define TIMEOUTS_PENDING 0x10
#define TIMEOUTS_EXPIRED 0x20
#define TIMEOUTS_ALL (TIMEOUTS_PENDING|TIMEOUTS_EXPIRED)
#define TIMEOUTS_CLEAR 0x40
#define TIMEOUTS_ALL (TIMEOUTS_PENDING | TIMEOUTS_EXPIRED)
#define TIMEOUTS_CLEAR 0x40
#define TIMEOUTS_IT_INITIALIZER(flags) { (flags), 0, 0, 0, 0 }
#define TIMEOUTS_IT_INITIALIZER(flags) \
{ \
(flags), 0, 0, 0, 0 \
}
#define TIMEOUTS_IT_INIT(cur, _flags) do { \
(cur)->flags = (_flags); \
(cur)->pc = 0; \
} while (0)
#define TIMEOUTS_IT_INIT(cur, _flags) \
do \
{ \
(cur)->flags = (_flags); \
(cur)->pc = 0; \
} while (0)
struct timeouts_it {
struct timeouts_it
{
int flags;
unsigned pc, i, j;
struct timeout *to;
@@ -223,11 +233,10 @@ TIMEOUT_PUBLIC struct timeout *timeouts_next(struct timeouts *, struct timeouts_
* could invalidate cursor state and trigger a use-after-free.
*/
#define TIMEOUTS_FOREACH(var, T, flags) \
struct timeouts_it _it = TIMEOUTS_IT_INITIALIZER((flags)); \
#define TIMEOUTS_FOREACH(var, T, flags) \
struct timeouts_it _it = TIMEOUTS_IT_INITIALIZER((flags)); \
while (((var) = timeouts_next((T), &_it)))
/*
* B O N U S W H E E L I N T E R F A C E S
*

View File

@@ -6,11 +6,13 @@ add_library(session_manager
session.cpp
session_address.cpp
session_pool.cpp
session_table.cpp)
session_table.cpp
session_timer.cpp)
target_include_directories(session_manager PUBLIC ${CMAKE_SOURCE_DIR}/deps/uthash)
target_include_directories(session_manager PUBLIC ${CMAKE_SOURCE_DIR}/deps/timeout)
target_include_directories(session_manager PUBLIC ${CMAKE_SOURCE_DIR}/src/packet)
target_include_directories(session_manager PUBLIC ${CMAKE_SOURCE_DIR}/src/session)
target_link_libraries(session_manager)
target_link_libraries(session_manager timeout)
###############################################################################
# gtest
@@ -32,8 +34,13 @@ add_executable(gtest_session_table gtest_session_table.cpp)
target_include_directories(gtest_session_table PUBLIC ${CMAKE_CURRENT_LIST_DIR})
target_link_libraries(gtest_session_table session_manager gtest)
add_executable(gtest_session_timer gtest_session_timer.cpp)
target_include_directories(gtest_session_timer PUBLIC ${CMAKE_CURRENT_LIST_DIR})
target_link_libraries(gtest_session_timer session_manager gtest)
include(GoogleTest)
gtest_discover_tests(gtest_session)
gtest_discover_tests(gtest_session_address)
gtest_discover_tests(gtest_session_pool)
gtest_discover_tests(gtest_session_table)
gtest_discover_tests(gtest_session_table)
gtest_discover_tests(gtest_session_timer)

View File

@@ -0,0 +1,160 @@
#include <gtest/gtest.h>
#include "session_timer.h"
#include "session_private.h"
static void session_expire(struct session *sess)
{
printf("=== session %lu expired ===\n", session_get_id(sess));
}
TEST(SESSION_TIMER, ADD_DEL)
{
struct session sess;
struct session_timer *timer = session_timer_create();
EXPECT_TRUE(timer != NULL);
session_init(&sess);
session_set_id(&sess, 1);
session_set_expirecb(&sess, session_expire, 1000);
session_timer_add_session(timer, &sess);
session_timer_del_session(timer, &sess);
session_timer_destroy(timer);
}
TEST(SESSION_TIMER, EXPIRE)
{
struct session *sess = NULL;
struct session sess1;
struct session sess2;
struct session sess3;
struct session_timer *timer = session_timer_create();
EXPECT_TRUE(timer != NULL);
session_init(&sess1);
session_init(&sess2);
session_init(&sess3);
session_set_id(&sess1, 1);
session_set_id(&sess2, 2);
session_set_id(&sess3, 3);
session_set_expirecb(&sess1, session_expire, 5);
session_set_expirecb(&sess2, session_expire, 5);
session_set_expirecb(&sess3, session_expire, 10);
session_timer_add_session(timer, &sess1);
session_timer_add_session(timer, &sess2);
session_timer_add_session(timer, &sess3);
for (uint64_t abs_current_ts = 0; abs_current_ts < 15; abs_current_ts++)
{
printf("current timestamp %lu\n", abs_current_ts);
do
{
sess = session_timer_expire_session(timer, abs_current_ts);
if (sess != NULL)
{
session_run_expirecb(sess);
}
} while (sess);
}
session_timer_destroy(timer);
}
TEST(SESSION_TIMER, BEFORE_EXPIRE_DEL)
{
struct session *sess = NULL;
struct session sess1;
struct session sess2;
struct session sess3;
struct session_timer *timer = session_timer_create();
EXPECT_TRUE(timer != NULL);
session_init(&sess1);
session_init(&sess2);
session_init(&sess3);
session_set_id(&sess1, 1);
session_set_id(&sess2, 2);
session_set_id(&sess3, 3);
session_set_expirecb(&sess1, session_expire, 5);
session_set_expirecb(&sess2, session_expire, 5);
session_set_expirecb(&sess3, session_expire, 10);
session_timer_add_session(timer, &sess1);
session_timer_add_session(timer, &sess2);
session_timer_add_session(timer, &sess3);
for (uint64_t abs_current_ts = 0; abs_current_ts < 15; abs_current_ts++)
{
printf("current timestamp %lu\n", abs_current_ts);
if (abs_current_ts == 2)
{
printf("delete timer 2\n");
session_timer_del_session(timer, &sess2);
}
do
{
sess = session_timer_expire_session(timer, abs_current_ts);
if (sess != NULL)
{
session_run_expirecb(sess);
}
} while (sess);
}
session_timer_destroy(timer);
}
TEST(SESSION_TIMER, BEFORE_EXPIRE_UPDATE)
{
struct session *sess = NULL;
struct session sess1;
struct session sess2;
struct session sess3;
struct session_timer *timer = session_timer_create();
EXPECT_TRUE(timer != NULL);
session_init(&sess1);
session_init(&sess2);
session_init(&sess3);
session_set_id(&sess1, 1);
session_set_id(&sess2, 2);
session_set_id(&sess3, 3);
session_set_expirecb(&sess1, session_expire, 5);
session_set_expirecb(&sess2, session_expire, 5);
session_set_expirecb(&sess3, session_expire, 10);
session_timer_add_session(timer, &sess1);
session_timer_add_session(timer, &sess2);
session_timer_add_session(timer, &sess3);
for (uint64_t abs_current_ts = 0; abs_current_ts < 15; abs_current_ts++)
{
printf("current timestamp %lu\n", abs_current_ts);
if (abs_current_ts == 2)
{
printf("update timer 2\n");
session_timer_del_session(timer, &sess2);
session_set_expirecb(&sess2, session_expire, 8);
session_timer_add_session(timer, &sess2);
}
do
{
sess = session_timer_expire_session(timer, abs_current_ts);
if (sess != NULL)
{
session_run_expirecb(sess);
}
} while (sess);
}
session_timer_destroy(timer);
}
int main(int argc, char **argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@@ -184,11 +184,6 @@ void session_set_last_time(struct session *sess, uint64_t timestamp)
sess->last_time = timestamp;
}
void session_set_expire_time(struct session *sess, uint64_t timestamp)
{
sess->expire_time = timestamp;
}
uint64_t session_get_create_time(struct session *sess)
{
return sess->create_time;
@@ -199,11 +194,6 @@ uint64_t session_get_last_time(struct session *sess)
return sess->last_time;
}
uint64_t session_get_expire_time(struct session *sess)
{
return sess->expire_time;
}
// session event
bool session_push_event(struct session *sess, uint32_t event)
{
@@ -304,3 +294,34 @@ void session_free_ex_data(struct session *sess, uint8_t idx)
sess->ex_data[idx] = NULL;
}
/******************************************************************************
* session expire
******************************************************************************/
// session expire
void session_set_expirecb(struct session *sess, session_expire_cb fn, uint64_t abs_expire_ts)
{
struct timeout *timeout = &sess->timeout;
timeout_init(timeout, TIMEOUT_ABS);
timeout_setcb(timeout, fn, sess);
sess->abs_expire_ts = abs_expire_ts;
}
void session_del_expirecb(struct session *sess)
{
struct timeout *timeout = &sess->timeout;
timeout_init(timeout, 0);
sess->abs_expire_ts = 0;
}
void session_run_expirecb(struct session *sess)
{
struct timeout *timeout = &sess->timeout;
if (timeout->callback.fn)
{
timeout->callback.fn(timeout->callback.arg);
}
}

View File

@@ -123,6 +123,17 @@ void *session_get0_ex_data(struct session *sess, uint8_t idx);
*/
void session_free_ex_data(struct session *sess, uint8_t idx);
/******************************************************************************
* session expire
******************************************************************************/
typedef void (*session_expire_cb)(struct session *sess);
// session timer
void session_set_expirecb(struct session *sess, session_expire_cb fn, uint64_t abs_timeout_ts);
void session_del_expirecb(struct session *sess);
void session_run_expirecb(struct session *sess);
#ifdef __cpluscplus
}
#endif

View File

@@ -14,6 +14,14 @@ extern "C"
#include "session.h"
#include "session_address.h"
#define TIMEOUT_CB_OVERRIDE
struct timeout_cb
{
session_expire_cb fn;
struct session *arg;
};
#include "timeout.h"
#define EX_DATA_MAX_COUNT 128
#define SESSION_EVENT_QUEUE_SIZE 256
@@ -45,7 +53,6 @@ struct session
// session timestamp
uint64_t create_time;
uint64_t last_time;
uint64_t expire_time;
/******************************
* Session Ev Queue Zone
@@ -64,6 +71,8 @@ struct session
******************************/
// session timer
struct timeout timeout;
uint64_t abs_expire_ts;
/******************************
* Session Pool Zone

View File

@@ -0,0 +1,69 @@
#include "session_timer.h"
#include "session_private.h"
struct session_timer
{
struct timeouts *timeouts;
};
struct session_timer *session_timer_create()
{
timeout_error_t err;
struct session_timer *timer = (struct session_timer *)calloc(1, sizeof(struct session_timer));
if (timer == NULL)
{
return NULL;
}
timer->timeouts = timeouts_open(0, &err);
if (timer->timeouts == NULL)
{
goto error;
}
return timer;
error:
session_timer_destroy(timer);
return NULL;
}
void session_timer_destroy(struct session_timer *timer)
{
if (timer)
{
if (timer->timeouts)
{
timeouts_close(timer->timeouts);
}
free(timer);
timer = NULL;
}
}
void session_timer_add_session(struct session_timer *timer, struct session *sess)
{
struct timeout *timeout = &sess->timeout;
timeouts_add(timer->timeouts, timeout, sess->abs_expire_ts);
}
void session_timer_del_session(struct session_timer *timer, struct session *sess)
{
struct timeout *timeout = &sess->timeout;
timeouts_del(timer->timeouts, timeout);
}
struct session *session_timer_expire_session(struct session_timer *timer, uint64_t abs_current_ts)
{
timeouts_update(timer->timeouts, abs_current_ts);
struct timeout *timeout = timeouts_get(timer->timeouts);
if (timeout == NULL)
{
return NULL;
}
struct session *sess = (struct session *)timeout->callback.arg;
return sess;
}

View File

@@ -0,0 +1,26 @@
#ifndef _SESSION_TIMER_H
#define _SESSION_TIMER_H
#ifdef __cpluscplus
extern "C"
{
#endif
#include "session.h"
struct session_timer;
struct session_timer *session_timer_create();
void session_timer_destroy(struct session_timer *timer);
void session_timer_add_session(struct session_timer *timer, struct session *sess);
void session_timer_del_session(struct session_timer *timer, struct session *sess);
/*
* return one session which timeout, or NULL if no session timeout.
* if return session, the session will be removed from timer.
*/
struct session *session_timer_expire_session(struct session_timer *timer, uint64_t abs_current_ts);
#ifdef __cpluscplus
}
#endif
#endif