diff options
Diffstat (limited to 'contrib/timer-wheel/timer-wheel.c')
| -rw-r--r-- | contrib/timer-wheel/timer-wheel.c | 121 |
1 files changed, 114 insertions, 7 deletions
diff --git a/contrib/timer-wheel/timer-wheel.c b/contrib/timer-wheel/timer-wheel.c index 77fbfbe9953..58e0607bf0c 100644 --- a/contrib/timer-wheel/timer-wheel.c +++ b/contrib/timer-wheel/timer-wheel.c @@ -1,4 +1,12 @@ /* + * linux/kernel/timer.c + * + * Kernel internal timers + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + */ +/* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or @@ -57,9 +65,17 @@ __gf_tw_add_timer (struct tvec_base *base, struct gf_tw_timer_list *timer) list_add_tail (&timer->entry, vec); } -/* optimized find_last_bit() */ unsigned long gf_tw_find_last_bit(const unsigned long *, unsigned long); +#if defined(__GNUC__) || defined(__clang__) +static inline unsigned long gf_tw_fls (unsigned long word) +{ + return BITS_PER_LONG - __builtin_clzl(word); +} +#else +extern unsigned long gf_tw_fls (unsigned long); +#endif + static inline unsigned long apply_slack(struct tvec_base *base, struct gf_tw_timer_list *timer) { @@ -77,7 +93,7 @@ apply_slack(struct tvec_base *base, struct gf_tw_timer_list *timer) if (mask == 0) return expires; - int bit = gf_tw_find_last_bit (&mask, BITS_PER_LONG); + int bit = gf_tw_fls (mask); mask = (1UL << bit) - 1; expires_limit = expires_limit & ~(mask); @@ -85,11 +101,12 @@ apply_slack(struct tvec_base *base, struct gf_tw_timer_list *timer) } static inline void -gf_tw_detach_timer (struct gf_tw_timer_list *timer) +__gf_tw_detach_timer (struct gf_tw_timer_list *timer) { struct list_head *entry = &timer->entry; list_del (entry); + entry->next = NULL; } static inline int @@ -141,8 +158,15 @@ run_timers (struct tvec_base *base) fn = timer->function; data = timer->data; - gf_tw_detach_timer (timer); - fn (timer, data, call_time); + __gf_tw_detach_timer (timer); + pthread_spin_unlock(&base->lock); + { + /* It is required to run the actual function outside + of the locked zone, so we don't bother about + locked operations inside that function */ + fn(timer, data, call_time); + } + pthread_spin_lock(&base->lock); } } pthread_spin_unlock (&base->lock); @@ -166,6 +190,38 @@ void *runner (void *arg) } +static inline int timer_pending (struct gf_tw_timer_list *timer) +{ + struct list_head *entry = &timer->entry; + + return (entry->next != NULL); +} + +static inline int __detach_if_pending (struct gf_tw_timer_list *timer) +{ + if (!timer_pending (timer)) + return 0; + + __gf_tw_detach_timer (timer); + return 1; +} + +static inline int __mod_timer (struct tvec_base *base, + struct gf_tw_timer_list *timer, int pending_only) +{ + int ret = 0; + + ret = __detach_if_pending (timer); + if (!ret && pending_only) + goto done; + + ret = 1; + __gf_tw_add_timer (base, timer); + + done: + return ret; +} + /* interface */ /** @@ -185,9 +241,60 @@ void gf_tw_add_timer (struct tvec_base *base, struct gf_tw_timer_list *timer) /** * Remove a timer from the timer wheel */ -void gf_tw_del_timer (struct gf_tw_timer_list *timer) +int gf_tw_del_timer (struct tvec_base *base, struct gf_tw_timer_list *timer) +{ + int ret = 0; + + pthread_spin_lock (&base->lock); + { + if (timer_pending (timer)) { + ret = 1; + __gf_tw_detach_timer (timer); + } + } + pthread_spin_unlock (&base->lock); + + return ret; +} + +int gf_tw_mod_timer_pending (struct tvec_base *base, + struct gf_tw_timer_list *timer, + unsigned long expires) +{ + int ret = 1; + + pthread_spin_lock (&base->lock); + { + timer->expires = expires + base->timer_sec; + timer->expires = apply_slack (base, timer); + + ret = __mod_timer (base, timer, 1); + } + pthread_spin_unlock (&base->lock); + + return ret; +} + +int gf_tw_mod_timer (struct tvec_base *base, + struct gf_tw_timer_list *timer, unsigned long expires) { - gf_tw_detach_timer (timer); + int ret = 1; + + pthread_spin_lock (&base->lock); + { + /* fast path optimization */ + if (timer_pending (timer) && timer->expires == expires) + goto unblock; + + timer->expires = expires + base->timer_sec; + timer->expires = apply_slack (base, timer); + + ret = __mod_timer (base, timer, 0); + } + unblock: + pthread_spin_unlock (&base->lock); + + return ret; } int gf_tw_cleanup_timers (struct tvec_base *base) |
