diff options
Diffstat (limited to 'libglusterfs/src/timer.c')
| -rw-r--r-- | libglusterfs/src/timer.c | 399 |
1 files changed, 203 insertions, 196 deletions
diff --git a/libglusterfs/src/timer.c b/libglusterfs/src/timer.c index 3d69a9f7160..66c861b04cd 100644 --- a/libglusterfs/src/timer.c +++ b/libglusterfs/src/timer.c @@ -8,242 +8,249 @@ cases as published by the Free Software Foundation. */ -#include "timer.h" -#include "logging.h" -#include "common-utils.h" -#include "globals.h" -#include "timespec.h" -#include "libglusterfs-messages.h" +#include "glusterfs/timer.h" +#include "glusterfs/logging.h" +#include "glusterfs/common-utils.h" +#include "glusterfs/globals.h" +#include "glusterfs/timespec.h" +#include "glusterfs/libglusterfs-messages.h" /* fwd decl */ static gf_timer_registry_t * -gf_timer_registry_init (glusterfs_ctx_t *); +gf_timer_registry_init(glusterfs_ctx_t *); gf_timer_t * -gf_timer_call_after (glusterfs_ctx_t *ctx, - struct timespec delta, - gf_timer_cbk_t callbk, - void *data) +gf_timer_call_after(glusterfs_ctx_t *ctx, struct timespec delta, + gf_timer_cbk_t callbk, void *data) { - gf_timer_registry_t *reg = NULL; - gf_timer_t *event = NULL; - gf_timer_t *trav = NULL; - uint64_t at = 0; - - if (ctx == NULL) - { - gf_msg_callingfn ("timer", GF_LOG_ERROR, EINVAL, - LG_MSG_INVALID_ARG, "invalid argument"); - return NULL; - } + gf_timer_registry_t *reg = NULL; + gf_timer_t *event = NULL; + gf_timer_t *trav = NULL; + uint64_t at = 0; + + if ((ctx == NULL) || (ctx->cleanup_started)) { + gf_msg_callingfn("timer", GF_LOG_ERROR, EINVAL, LG_MSG_INVALID_ARG, + "Either ctx is NULL or" + " ctx cleanup started"); + return NULL; + } - reg = gf_timer_registry_init (ctx); + reg = gf_timer_registry_init(ctx); - if (!reg) { - gf_msg_callingfn ("timer", GF_LOG_ERROR, 0, - LG_MSG_TIMER_REGISTER_ERROR, "!reg"); - return NULL; - } + if (!reg) { + gf_msg_callingfn("timer", GF_LOG_ERROR, 0, LG_MSG_TIMER_REGISTER_ERROR, + "!reg"); + return NULL; + } - event = GF_CALLOC (1, sizeof (*event), gf_common_mt_gf_timer_t); - if (!event) { - return NULL; - } - timespec_now (&event->at); - timespec_adjust_delta (&event->at, delta); - at = TS (event->at); - event->callbk = callbk; - event->data = data; - event->xl = THIS; - LOCK (®->lock); + event = GF_CALLOC(1, sizeof(*event), gf_common_mt_gf_timer_t); + if (!event) { + return NULL; + } + timespec_now(&event->at); + timespec_adjust_delta(&event->at, delta); + at = TS(event->at); + event->callbk = callbk; + event->data = data; + event->xl = THIS; + pthread_mutex_lock(®->lock); + { + list_for_each_entry_reverse(trav, ®->active, list) { - list_for_each_entry_reverse (trav, ®->active, list) { - if (TS (trav->at) < at) - break; - } - list_add (&event->list, &trav->list); + if (TS(trav->at) < at) + break; } - UNLOCK (®->lock); - return event; + list_add(&event->list, &trav->list); + if (&trav->list == ®->active) { + pthread_cond_signal(®->cond); + } + } + pthread_mutex_unlock(®->lock); + return event; } - int32_t -gf_timer_call_cancel (glusterfs_ctx_t *ctx, - gf_timer_t *event) +gf_timer_call_cancel(glusterfs_ctx_t *ctx, gf_timer_t *event) { - gf_timer_registry_t *reg = NULL; - gf_boolean_t fired = _gf_false; - - if (ctx == NULL || event == NULL) - { - gf_msg_callingfn ("timer", GF_LOG_ERROR, EINVAL, - LG_MSG_INVALID_ARG, "invalid argument"); - return 0; - } + gf_timer_registry_t *reg = NULL; + gf_boolean_t fired = _gf_false; - LOCK (&ctx->lock); - { - reg = ctx->timer; - } - UNLOCK (&ctx->lock); - - if (!reg) { - gf_msg ("timer", GF_LOG_ERROR, 0, LG_MSG_INIT_TIMER_FAILED, - "!reg"); - GF_FREE (event); - return 0; - } + if (ctx == NULL || event == NULL) { + gf_msg_callingfn("timer", GF_LOG_ERROR, EINVAL, LG_MSG_INVALID_ARG, + "invalid argument"); + return -1; + } - LOCK (®->lock); - { - fired = event->fired; - if (fired) - goto unlock; - list_del (&event->list); - } + if (ctx->cleanup_started) { + gf_msg_callingfn("timer", GF_LOG_INFO, 0, LG_MSG_CTX_CLEANUP_STARTED, + "ctx cleanup started"); + return -1; + } + + LOCK(&ctx->lock); + { + reg = ctx->timer; + } + UNLOCK(&ctx->lock); + + if (!reg) { + /* This can happen when cleanup may have just started and + * gf_timer_registry_destroy() sets ctx->timer to NULL. + * gf_timer_proc() takes care of cleaning up the events. + */ + return -1; + } + + pthread_mutex_lock(®->lock); + { + fired = event->fired; + if (fired) + goto unlock; + list_del(&event->list); + } unlock: - UNLOCK (®->lock); + pthread_mutex_unlock(®->lock); - if (!fired) { - GF_FREE (event); - return 0; - } - return -1; + if (!fired) { + GF_FREE(event); + return 0; + } + return -1; } - static void * -gf_timer_proc (void *data) +gf_timer_proc(void *data) { - gf_timer_registry_t *reg = data; - const struct timespec sleepts = {.tv_sec = 1, .tv_nsec = 0, }; - gf_timer_t *event = NULL; - gf_timer_t *tmp = NULL; - xlator_t *old_THIS = NULL; - - while (!reg->fin) { - uint64_t now; - struct timespec now_ts; - - timespec_now (&now_ts); - now = TS (now_ts); - while (1) { - uint64_t at; - char need_cbk = 0; - - LOCK (®->lock); - { - list_for_each_entry_safe (event, - tmp, ®->active, list) { - at = TS (event->at); - if (now >= at) { - need_cbk = 1; - event->fired = _gf_true; - list_del (&event->list); - break; - } - } - } - UNLOCK (®->lock); - if (need_cbk) { - old_THIS = NULL; - if (event->xl) { - old_THIS = THIS; - THIS = event->xl; - } - event->callbk (event->data); - GF_FREE (event); - if (old_THIS) { - THIS = old_THIS; - } - } else { - break; - } + gf_timer_registry_t *reg = data; + gf_timer_t *event = NULL; + gf_timer_t *tmp = NULL; + xlator_t *old_THIS = NULL; + + pthread_mutex_lock(®->lock); + + while (!reg->fin) { + if (list_empty(®->active)) { + pthread_cond_wait(®->cond, ®->lock); + } else { + struct timespec now; + + timespec_now(&now); + event = list_first_entry(®->active, gf_timer_t, list); + if (TS(now) < TS(event->at)) { + now = event->at; + pthread_cond_timedwait(®->cond, ®->lock, &now); + } else { + event->fired = _gf_true; + list_del_init(&event->list); + + pthread_mutex_unlock(®->lock); + + old_THIS = NULL; + if (event->xl) { + old_THIS = THIS; + THIS = event->xl; } - nanosleep (&sleepts, NULL); - } - - LOCK (®->lock); - { - /* Do not call gf_timer_call_cancel(), - * it will lead to deadlock - */ - list_for_each_entry_safe (event, tmp, ®->active, list) { - list_del (&event->list); - GF_FREE (event); + event->callbk(event->data); + GF_FREE(event); + if (old_THIS) { + THIS = old_THIS; } - } - UNLOCK (®->lock); - LOCK_DESTROY (®->lock); - return NULL; + pthread_mutex_lock(®->lock); + } + } + } + + /* Do not call gf_timer_call_cancel(), + * it will lead to deadlock + */ + list_for_each_entry_safe(event, tmp, ®->active, list) + { + list_del(&event->list); + /* TODO Possible resource leak + * Before freeing the event, we need to call the respective + * event functions and free any resources. + * For example, In case of rpc_clnt_reconnect, we need to + * unref rpc object which was taken when added to timer + * wheel. + */ + GF_FREE(event); + } + + pthread_mutex_unlock(®->lock); + + return NULL; } - static gf_timer_registry_t * -gf_timer_registry_init (glusterfs_ctx_t *ctx) +gf_timer_registry_init(glusterfs_ctx_t *ctx) { - gf_timer_registry_t *reg = NULL; - - if (ctx == NULL) { - gf_msg_callingfn ("timer", GF_LOG_ERROR, EINVAL, - LG_MSG_INVALID_ARG, "invalid argument"); - return NULL; + gf_timer_registry_t *reg = NULL; + int ret = -1; + pthread_condattr_t attr; + + LOCK(&ctx->lock); + { + reg = ctx->timer; + if (reg) { + UNLOCK(&ctx->lock); + goto out; } - - if (ctx->cleanup_started) { - gf_msg_callingfn ("timer", GF_LOG_INFO, 0, - LG_MSG_CTX_CLEANUP_STARTED, - "ctx cleanup started"); - return NULL; + reg = GF_CALLOC(1, sizeof(*reg), gf_common_mt_gf_timer_registry_t); + if (!reg) { + UNLOCK(&ctx->lock); + goto out; } + ctx->timer = reg; + pthread_mutex_init(®->lock, NULL); + pthread_condattr_init(&attr); + pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); + pthread_cond_init(®->cond, &attr); + INIT_LIST_HEAD(®->active); + } + UNLOCK(&ctx->lock); + ret = gf_thread_create(®->th, NULL, gf_timer_proc, reg, "timer"); + if (ret) { + gf_msg(THIS->name, GF_LOG_ERROR, ret, LG_MSG_PTHREAD_FAILED, + "Thread creation failed"); + } - LOCK (&ctx->lock); - { - reg = ctx->timer; - if (reg) { - UNLOCK (&ctx->lock); - goto out; - } - reg = GF_CALLOC (1, sizeof (*reg), - gf_common_mt_gf_timer_registry_t); - if (!reg) { - UNLOCK (&ctx->lock); - goto out; - } - ctx->timer = reg; - LOCK_INIT (®->lock); - INIT_LIST_HEAD (®->active); - } - UNLOCK (&ctx->lock); - gf_thread_create (®->th, NULL, gf_timer_proc, reg, "timer"); out: - return reg; + return reg; } - void -gf_timer_registry_destroy (glusterfs_ctx_t *ctx) +gf_timer_registry_destroy(glusterfs_ctx_t *ctx) { - pthread_t thr_id; - gf_timer_registry_t *reg = NULL; + pthread_t thr_id; + gf_timer_registry_t *reg = NULL; - if (ctx == NULL) - return; + if (ctx == NULL) + return; - LOCK (&ctx->lock); - { - reg = ctx->timer; - ctx->timer = NULL; - } - UNLOCK (&ctx->lock); + LOCK(&ctx->lock); + { + reg = ctx->timer; + ctx->timer = NULL; + } + UNLOCK(&ctx->lock); + + if (!reg) + return; + + thr_id = reg->th; + + pthread_mutex_lock(®->lock); + + reg->fin = 1; + pthread_cond_signal(®->cond); + + pthread_mutex_unlock(®->lock); + + pthread_join(thr_id, NULL); - if (!reg) - return; + pthread_cond_destroy(®->cond); + pthread_mutex_destroy(®->lock); - thr_id = reg->th; - reg->fin = 1; - pthread_join (thr_id, NULL); - GF_FREE (reg); + GF_FREE(reg); } |
