diff options
| author | Garrett D'Amore <garrett@damore.org> | 2017-03-03 20:36:47 -0800 |
|---|---|---|
| committer | Garrett D'Amore <garrett@damore.org> | 2017-03-03 20:36:47 -0800 |
| commit | c17a1dd3f5333da59355ecc3f8788a0396a8f72d (patch) | |
| tree | 77e6790df0fa45b57fafa749f3380fc4b8aa230e /src/core/timer.c | |
| parent | 97614393e450b6c6813021f0e733b864a6265872 (diff) | |
| download | nng-c17a1dd3f5333da59355ecc3f8788a0396a8f72d.tar.gz nng-c17a1dd3f5333da59355ecc3f8788a0396a8f72d.tar.bz2 nng-c17a1dd3f5333da59355ecc3f8788a0396a8f72d.zip | |
Timer implementation. Operations can timeout now?
Diffstat (limited to 'src/core/timer.c')
| -rw-r--r-- | src/core/timer.c | 161 |
1 files changed, 161 insertions, 0 deletions
diff --git a/src/core/timer.c b/src/core/timer.c new file mode 100644 index 00000000..59966822 --- /dev/null +++ b/src/core/timer.c @@ -0,0 +1,161 @@ +// +// Copyright 2017 Garrett D'Amore <garrett@damore.org> +// +// This software is supplied under the terms of the MIT License, a +// copy of which should be located in the distribution where this +// file was obtained (LICENSE.txt). A copy of the license may also be +// found online at https://opensource.org/licenses/MIT. +// + +#include "core/nng_impl.h" + +#include <stdlib.h> +#include <string.h> + +extern void nni_timer_schedule(nni_timer_node *); +extern void nni_timer_cancel(nni_timer_node *); +extern int nni_timer_init(void); +extern void nni_timer_fini(void); +static void nni_timer_loop(void *); + +struct nni_timer { + // We use two mutexes. One protects the list, and the other ensures + // that cancel is blocked if we are running a timeout calllback. + // The callback(s) are allowed to reschedule a timeout. The list + // mutex is *always* acquired before the run mutex. + nni_mtx t_list_mx; + nni_mtx t_run_mx; + nni_cv t_cv; + nni_list t_entries; + nni_thr t_thr; + int t_close; +}; + +typedef struct nni_timer nni_timer; + +static nni_timer nni_global_timer; + + +int +nni_timer_sys_init(void) +{ + int rv; + nni_timer *timer = &nni_global_timer; + + memset(timer, 0, sizeof (*timer)); + NNI_LIST_INIT(&timer->t_entries, nni_timer_node, t_node); + timer->t_close = 0; + + if (((rv = nni_mtx_init(&timer->t_list_mx)) != 0) || + ((rv = nni_mtx_init(&timer->t_run_mx)) != 0) || + ((rv = nni_cv_init(&timer->t_cv, &timer->t_list_mx)) != 0) || + ((rv = nni_thr_init(&timer->t_thr, nni_timer_loop, timer)) != 0)) { + nni_timer_sys_fini(); + return (rv); + } + nni_thr_run(&timer->t_thr); + return (0); +} + + +void +nni_timer_sys_fini(void) +{ + nni_timer *timer = &nni_global_timer; + + nni_mtx_lock(&timer->t_list_mx); + timer->t_close = 1; + nni_cv_wake(&timer->t_cv); + nni_mtx_unlock(&timer->t_list_mx); + + nni_thr_fini(&timer->t_thr); + nni_cv_fini(&timer->t_cv); + nni_mtx_fini(&timer->t_list_mx); + nni_mtx_fini(&timer->t_run_mx); +} + + +void +nni_timer_cancel(nni_timer_node *node) +{ + nni_timer *timer = &nni_global_timer; + + nni_mtx_lock(&timer->t_list_mx); + nni_mtx_lock(&timer->t_run_mx); + if (node->t_sched) { + nni_list_remove(&timer->t_entries, node); + node->t_sched = 0; + } + nni_mtx_unlock(&timer->t_run_mx); + nni_mtx_unlock(&timer->t_list_mx); +} + + +void +nni_timer_schedule(nni_timer_node *node) +{ + nni_timer *timer = &nni_global_timer; + nni_timer_node *srch; + int wake = 1; + + nni_mtx_lock(&timer->t_list_mx); + + srch = nni_list_first(&timer->t_entries); + while ((srch != NULL) && (srch->t_expire < node->t_expire)) { + srch = nni_list_next(&timer->t_entries, srch); + wake = 0; + } + if (srch != NULL) { + nni_list_insert_before(&timer->t_entries, node, srch); + } else { + nni_list_append(&timer->t_entries, node); + } + node->t_sched = 1; + if (wake) { + nni_cv_wake(&timer->t_cv); + } + nni_mtx_unlock(&timer->t_list_mx); +} + + +static void +nni_timer_loop(void *arg) +{ + nni_timer *timer = arg; + nni_time now; + nni_time expire; + nni_timer_node *node; + + for (;;) { + nni_mtx_lock(&timer->t_list_mx); + if (timer->t_close) { + nni_mtx_unlock(&timer->t_list_mx); + break; + } + + now = nni_clock(); + if ((node = nni_list_first(&timer->t_entries)) == NULL) { + nni_cv_wait(&timer->t_cv); + nni_mtx_unlock(&timer->t_list_mx); + continue; + } + if (now < node->t_expire) { + // End of run, we have to wait for next. + nni_cv_until(&timer->t_cv, node->t_expire); + nni_mtx_unlock(&timer->t_list_mx); + continue; + } + + nni_list_remove(&timer->t_entries, node); + node->t_sched = 0; + + // The lock ordering here is important. We acquire the run + // lock before dropping the list lock. One the run is done, + // we can drop the run lock too. The reason for the second + // lock is so that the callback can reschedule itself. + nni_mtx_lock(&timer->t_run_mx); + nni_mtx_unlock(&timer->t_list_mx); + node->t_cb(node->t_arg); + nni_mtx_unlock(&timer->t_run_mx); + } +} |
