From 2d3e182b5a54fb8a8c0e70f0406fcd873366d7bd Mon Sep 17 00:00:00 2001 From: luwenpeng Date: Tue, 12 Dec 2023 18:41:53 +0800 Subject: [PATCH] Add session timer --- deps/timeout/CMakeLists.txt | 5 +- .../{timeout-bitops.c => timeout-bitops.cpp} | 79 ++-- deps/timeout/{timeout.c => timeout.cpp} | 364 ++++++++++-------- deps/timeout/timeout.h | 67 ++-- src/session/CMakeLists.txt | 13 +- src/session/gtest_session_timer.cpp | 160 ++++++++ src/session/session.cpp | 41 +- src/session/session.h | 11 + src/session/session_private.h | 11 +- src/session/session_timer.cpp | 69 ++++ src/session/session_timer.h | 26 ++ 11 files changed, 612 insertions(+), 234 deletions(-) rename deps/timeout/{timeout-bitops.c => timeout-bitops.cpp} (75%) rename deps/timeout/{timeout.c => timeout.cpp} (74%) create mode 100644 src/session/gtest_session_timer.cpp create mode 100644 src/session/session_timer.cpp create mode 100644 src/session/session_timer.h diff --git a/deps/timeout/CMakeLists.txt b/deps/timeout/CMakeLists.txt index e000160..c105544 100644 --- a/deps/timeout/CMakeLists.txt +++ b/deps/timeout/CMakeLists.txt @@ -1,3 +1,2 @@ -set(CMAKE_C_FLAGS "-std=c99") -add_definitions(-fPIC) -add_library(timeout STATIC timeout.c timeout-bitops.c) \ No newline at end of file +add_library(timeout STATIC timeout.cpp timeout-bitops.cpp) +target_include_directories(timeout PUBLIC ${CMAKE_CURRENT_LIST_DIR}) \ No newline at end of file diff --git a/deps/timeout/timeout-bitops.c b/deps/timeout/timeout-bitops.cpp similarity index 75% rename from deps/timeout/timeout-bitops.c rename to deps/timeout/timeout-bitops.cpp index d8325db..6fb057f 100644 --- a/deps/timeout/timeout-bitops.c +++ b/deps/timeout/timeout-bitops.cpp @@ -1,6 +1,6 @@ #include #ifdef _MSC_VER -#include /* _BitScanForward, _BitScanReverse */ +#include /* _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 - diff --git a/deps/timeout/timeout.c b/deps/timeout/timeout.cpp similarity index 74% rename from deps/timeout/timeout.c rename to deps/timeout/timeout.cpp index e78f57d..a2d57b6 100644 --- a/deps/timeout/timeout.c +++ b/deps/timeout/timeout.cpp @@ -23,17 +23,17 @@ * USE OR OTHER DEALINGS IN THE SOFTWARE. * ========================================================================== */ -#include /* CHAR_BIT */ +#include /* CHAR_BIT */ -#include /* NULL */ -#include /* malloc(3) free(3) */ -#include /* FILE fprintf(3) */ +#include /* NULL */ +#include /* malloc(3) free(3) */ +#include /* FILE fprintf(3) */ -#include /* UINT64_C uint64_t */ +#include /* UINT64_C uint64_t */ -#include /* memset(3) */ +#include /* memset(3) */ -#include /* errno */ +#include /* errno */ #include /* 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() */ - diff --git a/deps/timeout/timeout.h b/deps/timeout/timeout.h index 3ef76e9..2c7fccb 100644 --- a/deps/timeout/timeout.h +++ b/deps/timeout/timeout.h @@ -26,13 +26,12 @@ #ifndef TIMEOUT_H #define TIMEOUT_H -#include /* bool */ -#include /* FILE */ +#include /* bool */ +#include /* FILE */ -#include /* PRIu64 PRIx64 PRIX64 uint64_t */ - -#include /* TAILQ(3) */ +#include /* PRIu64 PRIx64 PRIX64 uint64_t */ +#include /* 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 * diff --git a/src/session/CMakeLists.txt b/src/session/CMakeLists.txt index b5f30b0..48aae7e 100644 --- a/src/session/CMakeLists.txt +++ b/src/session/CMakeLists.txt @@ -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) \ No newline at end of file +gtest_discover_tests(gtest_session_table) +gtest_discover_tests(gtest_session_timer) \ No newline at end of file diff --git a/src/session/gtest_session_timer.cpp b/src/session/gtest_session_timer.cpp new file mode 100644 index 0000000..25acbc2 --- /dev/null +++ b/src/session/gtest_session_timer.cpp @@ -0,0 +1,160 @@ +#include + +#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(); +} diff --git a/src/session/session.cpp b/src/session/session.cpp index cddc603..6dcf6e3 100644 --- a/src/session/session.cpp +++ b/src/session/session.cpp @@ -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); + } +} \ No newline at end of file diff --git a/src/session/session.h b/src/session/session.h index 0525fbd..a43acd3 100644 --- a/src/session/session.h +++ b/src/session/session.h @@ -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 diff --git a/src/session/session_private.h b/src/session/session_private.h index 8043913..5138e3f 100644 --- a/src/session/session_private.h +++ b/src/session/session_private.h @@ -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 diff --git a/src/session/session_timer.cpp b/src/session/session_timer.cpp new file mode 100644 index 0000000..4938894 --- /dev/null +++ b/src/session/session_timer.cpp @@ -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; +} diff --git a/src/session/session_timer.h b/src/session/session_timer.h new file mode 100644 index 0000000..93690c5 --- /dev/null +++ b/src/session/session_timer.h @@ -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