diff options
Diffstat (limited to 'libglusterfs/src/timer.c')
| -rw-r--r-- | libglusterfs/src/timer.c | 400 |
1 files changed, 214 insertions, 186 deletions
diff --git a/libglusterfs/src/timer.c b/libglusterfs/src/timer.c index d8766d38ba3..66c861b04cd 100644 --- a/libglusterfs/src/timer.c +++ b/libglusterfs/src/timer.c @@ -1,228 +1,256 @@ /* - Copyright (c) 2007-2010 Gluster, Inc. <http://www.gluster.com> + Copyright (c) 2008-2012 Red Hat, Inc. <http://www.redhat.com> This file is part of GlusterFS. - GlusterFS is free software; you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation; either version 3 of the License, - or (at your option) any later version. - - GlusterFS is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see - <http://www.gnu.org/licenses/>. + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. */ -#ifndef _CONFIG_H -#define _CONFIG_H -#include "config.h" -#endif - -#include "timer.h" -#include "logging.h" -#include "common-utils.h" -#include "globals.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" -#define TS(tv) ((((unsigned long long) tv.tv_sec) * 1000000) + (tv.tv_usec)) +/* fwd decl */ +static gf_timer_registry_t * +gf_timer_registry_init(glusterfs_ctx_t *); gf_timer_t * -gf_timer_call_after (glusterfs_ctx_t *ctx, - struct timeval 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; - unsigned long long at = 0L; - - if (ctx == NULL) - { - gf_log_callingfn ("timer", GF_LOG_ERROR, "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_log_callingfn ("timer", GF_LOG_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; - } - gettimeofday (&event->at, NULL); - event->at.tv_usec = ((event->at.tv_usec + delta.tv_usec) % 1000000); - event->at.tv_sec += ((event->at.tv_usec + delta.tv_usec) / 1000000); - event->at.tv_sec += delta.tv_sec; - at = TS (event->at); - event->callbk = callbk; - event->data = data; - event->xl = THIS; - pthread_mutex_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) { - trav = reg->active.prev; - while (trav != ®->active) { - if (TS (trav->at) < at) - break; - trav = trav->prev; - } - event->prev = trav; - event->next = event->prev->next; - event->prev->next = event; - event->next->prev = event; + if (TS(trav->at) < at) + break; + } + list_add(&event->list, &trav->list); + if (&trav->list == ®->active) { + pthread_cond_signal(®->cond); } - pthread_mutex_unlock (®->lock); - return event; + } + pthread_mutex_unlock(®->lock); + return event; } int32_t -gf_timer_call_stale (gf_timer_registry_t *reg, - gf_timer_t *event) +gf_timer_call_cancel(glusterfs_ctx_t *ctx, gf_timer_t *event) { - if (reg == NULL || event == NULL) - { - gf_log_callingfn ("timer", GF_LOG_ERROR, "invalid argument"); - return 0; - } - - event->next->prev = event->prev; - event->prev->next = event->next; - event->next = ®->stale; - event->prev = event->next->prev; - event->next->prev = event; - event->prev->next = 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 -1; + } + + 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: + pthread_mutex_unlock(®->lock); + + if (!fired) { + GF_FREE(event); return 0; + } + return -1; } -int32_t -gf_timer_call_cancel (glusterfs_ctx_t *ctx, - gf_timer_t *event) +static void * +gf_timer_proc(void *data) { - gf_timer_registry_t *reg = NULL; + 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; + } + event->callbk(event->data); + GF_FREE(event); + if (old_THIS) { + THIS = old_THIS; + } - if (ctx == NULL || event == NULL) - { - gf_log_callingfn ("timer", GF_LOG_ERROR, "invalid argument"); - return 0; + 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; +} - reg = gf_timer_registry_init (ctx); - if (!reg) { - gf_log ("timer", GF_LOG_ERROR, "!reg"); - GF_FREE (event); - return 0; +static gf_timer_registry_t * +gf_timer_registry_init(glusterfs_ctx_t *ctx) +{ + 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; } - - pthread_mutex_lock (®->lock); - { - event->next->prev = event->prev; - event->prev->next = event->next; + reg = GF_CALLOC(1, sizeof(*reg), gf_common_mt_gf_timer_registry_t); + if (!reg) { + UNLOCK(&ctx->lock); + goto out; } - pthread_mutex_unlock (®->lock); + 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"); + } - GF_FREE (event); - return 0; +out: + return reg; } -void * -gf_timer_proc (void *ctx) +void +gf_timer_registry_destroy(glusterfs_ctx_t *ctx) { - gf_timer_registry_t *reg = NULL; + pthread_t thr_id; + gf_timer_registry_t *reg = NULL; - if (ctx == NULL) - { - gf_log_callingfn ("timer", GF_LOG_ERROR, "invalid argument"); - return NULL; - } + if (ctx == NULL) + return; - reg = gf_timer_registry_init (ctx); - if (!reg) { - gf_log ("timer", GF_LOG_ERROR, "!reg"); - return NULL; - } + LOCK(&ctx->lock); + { + reg = ctx->timer; + ctx->timer = NULL; + } + UNLOCK(&ctx->lock); - while (!reg->fin) { - unsigned long long now; - struct timeval now_tv; - gf_timer_t *event = NULL; - - gettimeofday (&now_tv, NULL); - now = TS (now_tv); - while (1) { - unsigned long long at; - char need_cbk = 0; - - pthread_mutex_lock (®->lock); - { - event = reg->active.next; - at = TS (event->at); - if (event != ®->active && now >= at) { - need_cbk = 1; - gf_timer_call_stale (reg, event); - } - } - pthread_mutex_unlock (®->lock); - if (event->xl) - THIS = event->xl; - if (need_cbk) - event->callbk (event->data); - - else - break; - } - usleep (1000000); - } + if (!reg) + return; - pthread_mutex_lock (®->lock); - { - while (reg->active.next != ®->active) { - gf_timer_call_cancel (ctx, reg->active.next); - } + thr_id = reg->th; - while (reg->stale.next != ®->stale) { - gf_timer_call_cancel (ctx, reg->stale.next); - } - } - pthread_mutex_unlock (®->lock); - pthread_mutex_destroy (®->lock); - GF_FREE (((glusterfs_ctx_t *)ctx)->timer); + pthread_mutex_lock(®->lock); - return NULL; -} + reg->fin = 1; + pthread_cond_signal(®->cond); -gf_timer_registry_t * -gf_timer_registry_init (glusterfs_ctx_t *ctx) -{ - if (ctx == NULL) { - gf_log_callingfn ("timer", GF_LOG_ERROR, "invalid argument"); - return NULL; - } - - if (!ctx->timer) { - gf_timer_registry_t *reg = NULL; + pthread_mutex_unlock(®->lock); - reg = GF_CALLOC (1, sizeof (*reg), - gf_common_mt_gf_timer_registry_t); - if (!reg) - goto out; + pthread_join(thr_id, NULL); - pthread_mutex_init (®->lock, NULL); - reg->active.next = ®->active; - reg->active.prev = ®->active; - reg->stale.next = ®->stale; - reg->stale.prev = ®->stale; + pthread_cond_destroy(®->cond); + pthread_mutex_destroy(®->lock); - ctx->timer = reg; - pthread_create (®->th, NULL, gf_timer_proc, ctx); - } -out: - return ctx->timer; + GF_FREE(reg); } |
