Linux Multi-timer implementation of the second
In this paper, the implementation of the multi timer in the industrial level, the realization of the second-level timer, time complexity of approximately o (1). Has the following characteristics: The time complexity of the new timer drop is approximately O (1). According to the timer timeout, the new timer is hashed into the hash bucket . The time complexity of the timer is approximately O (1) , which can be used in the C language code of multiple timers for multithreaded environments:
/*********************************************************************** * Copyright (c) 2018 Pepstack, pepstack.com * This software are provided ' as-is ', without any express or implied * warranty.
In no event would the authors be held liable to any damages * arising from the ' use of ' this software. * Permission is granted to anyone to the use of this software for all purpose, * including commercial, and to alter It and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must is not misrepresented; You are must not * claim this you wrote the original software. If you use this software * to a product, an acknowledgment in the product documentation would being * appreciated but is
Not required. * * 2.
Altered source versions must is plainly marked as such, and must not * is misrepresented as being the original. * * 3.
This notice may is removed or altered from any source distribution. ************************************//** * Mul_wheel_timer.h * Multi wheel timer for Linux * * Second-level Multi-timer industrial-level implementation * Refer: * http://blog.csdn.net/zhangxinrun/article/details/5914191 * https://linux.die.net/man/2/setitimer * * * AUT
hor:master@pepstack.com * * create:2018-02-03 11:00 * update:2018-02-03 22:09/#ifndef mul_wheel_timer_h_included #define MUL_WHEEL_TIMER_H_INCLUDED #if defined (__cplusplus) extern "C" {#endif #include "dhlist.h" #include <pthre ad.h> #include <unistd.h> #include <time.h> #include <sys/time.h>/** * Whether to use a double linked list: If you do not need to traverse the timer in order do not enable it * * mul_wheel_timer_has_dlist = 1 * Use Dlist to traversing by sequence * * mul_wheel_timer_has_dlist = 0 * DONOT use dlist since we need not traversing by sequence/#define MUL_WHEEL_TIMER_HAS_DLIST 0/** * Mul_wheel_tim Er_hashlen_max * * Defines the size of the hash bucket (2^n-1) = [255, 1023, 4095, 8191] * can be specified at compile time as needed.
The larger the bucket, the faster the lookup rate. * * #ifndef Mul_wheel_timer_hashlen_max # define Mul_wheel_timer_hashlen_max 1023 #endif/** * mul_wheel_timer_timeunit_sec * * Defines the time unit for multiple timers: that is, the minimum time interval. The system will trigger the timer callback function according to the time interval: Sigalarm_handler/#ifndef mul_wheel_timer_timeunit_sec # define Mul_wheel_timer_timeunit_s
EC 1 #endif #define MUL_WHEEL_TIMER_TIMEUNIT_DEFAULT ( -1) typedef int64_t mul_eventid_t;
typedef mul_eventid_t * MUL_EVENT_HANDLE;
typedef struct MUL_TIMER_EVENT_T {mul_eventid_t EventID;
/* Reference count: 0 Delete, 1 reserved */int REFC;
void *eventarg;
Int (*TIMER_EVENT_CB) (Mul_event_handle eventhdl, void *eventarg);
int64_t On_counter;
/* Specify the timer first excitation time and after each interval excitation time * * struct itimerval value;
unsigned int timeout;
int hash;
/** dhlist node */#if Mul_wheel_timer_has_dlist = = 1 struct list_head i_list;
#endif struct Hlist_node i_hash;
} mul_timer_event_t;
typedef struct MUL_WHEEL_TIMER_T {pthread_mutex_t lock;
/** * Timer Status: * 1: Start * 0: Suspend */int status; VolatileMul_eventid_t EventID;
Volatile int64_t counter;
void (* old_sigalarm) (int);
void (* new_sigalarm) (int);
unsigned int timeunit;
struct Itimerval value, ovalue;
/** Dhlist for Timer entry */#if mul_wheel_timer_has_dlist = = 1 struct list_head;
#endif struct Hlist_head Hlist[mul_wheel_timer_hashlen_max + 1];
} mul_wheel_timer_t;
/* Global timer variable for one Linux process/__attribute__ (used) static struct mul_wheel_timer_t mulwheeltimer;
__ATTRIBUTE__ ((used)) static void Sigalarm_handler (int signo); __ATTRIBUTE__ ((used)) static inline void free_timer_event (struct mul_timer_event_t * event) {printf ("\033[32m-delete
Event_%llu\033[0m\n ", (unsigned long) event->eventid);
Free (event); } __attribute__ ((used)) static inline mul_timer_event_t * Mul_handle_cast_event (mul_event_handle eventhdl) {struct
mul_timer_event_t * Event = container_of (EVENTHDL, struct mul_timer_event_t, EventID);
return event; } __atTRIBUTE__ ((used)) static int64_t Timer_on_counter_hash (mul_wheel_timer_t * mwtr, unsigned int tv_sec, int *hash) {in
t64_t On_counter = (int64_t) (Tv_sec/mwtr->timeunit + mwtr->counter);
*hash = (On_counter & Mul_wheel_timer_hashlen_max);
printf ("* \033[36mon_counter=%lld, hash=%d\033[0m\n", (Long Long) On_counter, (*hash));
return on_counter; } __attribute__ ((used)) static int Timer_select_sleep (int sec, int ms) {if (sec | | ms) {struct Timeval TV
= {0};
Tv.tv_sec = sec;
Tv.tv_usec =ms*1000;
Return Select (0, NULL, NULL, NULL, &TV);
else {return 0; }/** * Mul_wheel_timer_create * * Initializes multiple timers based on the given value.
This procedure does not provide the timer below the second, if need to ask the user to expand. * * Params: * start-1: Start timer immediately; 0: Do not start timer * * 1 multiple timers that initialize the minimum time unit (= mul_wheel_timer_timeunit_sec) Second: * * Mul_wheel_timer_create (mul_wheel_timer_t
Imeunit_default, 0); * * 2 Initializes a multiple timer for the minimum time unit (= 10) seconds, and starts after 60 seconds to activate 1 times every 10 seconds: * Mul_wheel_timer_create (10, 60);
* * Returns: * 0:success * -1:failed.
* Use Strerror (errno) for the error message. */__attribute__ ((used)) static int mul_wheel_timer_create (long timeunit_sec, long timedelay_sec, int start) {int I,
Err
Bzero (&mulwheeltimer, sizeof (struct mul_wheel_timer_t));
Mulwheeltimer.eventid = 1;
#if mul_wheel_timer_has_dlist = = 1 Init_list_head (&mulwheeltimer.dlist);
#endif for (i = 0; I <= mul_wheel_timer_hashlen_max i++) {init_hlist_head (&mulwheeltimer.hlist[i]);
Err = Pthread_mutex_init (&mulwheeltimer.lock, 0);
if (err) {/* Nerver run to this! */return (-1);
Err = Pthread_mutex_lock (&mulwheeltimer.lock);
if (err) {printf ("[MWt] Pthread_mutex_lock error (%d):%s\n", err, strerror (err));
Return (-1); } if ((Mulwheeltimer.old_sigalarm = Signal (SIGALRM, sigalarm_handler)) = = Sig_err) {Pthread_mutex_destroy (&mulwheeltimer.lock);
Return (-1);
} mulwheeltimer.new_sigalarm = Sigalarm_handler;
/** * Set time interval for the it_interval timer. * It_interval should be set to be the minimum time interval for late-added timers. Default is seconds: * mul_wheel_timer_timeunit_sec */if (timeunit_sec = = mul_wheel_timer_timeunit_default) {mu
Lwheeltimer.value.it_interval.tv_sec = mul_wheel_timer_timeunit_sec;
mulwheeltimer.value.it_interval.tv_usec = 0;
else {mulwheeltimer.value.it_interval.tv_sec = timeunit_sec;
mulwheeltimer.value.it_interval.tv_usec = 0; /** set time interval, in seconds: This program does not provide the timer below the seconds, if necessary, please expand their own users.
* * Mulwheeltimer.timeunit = mulwheeltimer.value.it_interval.tv_sec; /** set the timer with the expiration time of It_value, once the time reaches It_value, the kernel uses the It_interval restart Timer/if (timedelay_sec <= 0) {Mulwheeltimer
. value.it_value.tv_sec = mulwheeltimer.value.it_interval.tv_sec;
mulwheeltimer.value.it_value.tv_usec = 0; else {mulwheeltimer.value.it_value.tv_sec = Timedelay_seC
mulwheeltimer.value.it_value.tv_usec = 0;
} if (start) {err = Setitimer (Itimer_real, &mulwheeltimer.value, &mulwheeltimer.ovalue);
if (! Err) {/** Timer was successfully created and started/mulwheeltimer.status = 1;
Pthread_mutex_unlock (&mulwheeltimer.lock);
printf ("[MWt] Create success.\n");
return 0;
else {/** timer created but failed to start/mulwheeltimer.status = 0;
Pthread_mutex_destroy (&mulwheeltimer.lock);
printf ("[MWt] Create failed.\n");
Return (-1);
} else {/** timer successfully created, but does not require start/mulwheeltimer.status = 0;
Pthread_mutex_unlock (&mulwheeltimer.lock);
printf ("[MWT] Create success with no start.\n");
return 0; }/** * Mul_wheel_timer_start * Start Timer * * Returns: * 0:success successfully started * 1:sfalse has started * -1:error Start Failure/__attribute__ ((used)) static int mul_wheel_Timer_start (void) {int err;
Err = Pthread_mutex_trylock (&mulwheeltimer.lock);
if (err) {/** multithreaded lock failed/printf ("[MWt] Pthread_mutex_trylock error (%d):%s\n", err, strerror (err));
Return (-1);
} if (mulwheeltimer.status = = 1) {/** has started */Pthread_mutex_unlock (&mulwheeltimer.lock);
printf ("[MWt] already start.\n");
return 1;
Err = Setitimer (Itimer_real, &mulwheeltimer.value, &mulwheeltimer.ovalue);
if (! Err) {/** timer successfully started/mulwheeltimer.status = 1;
Pthread_mutex_unlock (&mulwheeltimer.lock);
printf ("[MWt] Start success.\n");
return 0;
else {/** timer failed to start/mulwheeltimer.status = 0;
Pthread_mutex_unlock (&mulwheeltimer.lock);
printf ("[MWt] Start error.\n");
Return (-1);
}/** * Mul_wheel_timer_destroy * * Returns: * 0:success * -1:failed. * Use Strerror (errno) For the error message.
*/__attribute__ ((used)) static int Mul_wheel_timer_destroy (void) {int err;
Err = Pthread_mutex_lock (&mulwheeltimer.lock);
if (err) {printf ("[MWt] Pthread_mutex_lock error (%d):%s\n", err, strerror (err));
Return (-1); } if ((Signal (SIGALRM, mulwheeltimer.new_sigalarm)) = = Sig_err) {Pthread_mutex_unlock (&mulwheeltimer.loc
k); printf ("[MWt] Destroy failed."
Signal error.\n ");
Return (-1);
/** recovery Process The original timer */err = Setitimer (Itimer_real, &mulwheeltimer.ovalue, &mulwheeltimer.value);
if (Err < 0) {Pthread_mutex_unlock (&mulwheeltimer.lock); printf ("[MWt] Destroy failed."
Setitimer error (%d):%s.\n ", errno, Strerror (errno));
Return (-1);
/** Empty Timer list/#if mul_wheel_timer_has_dlist = = 1 do {struct list_head *list, *node; List_for_each_safe (list, node, &mulwheeltimer.dlist) {struct mul_timer_event_t * Event = list_entry (list, struct mul_timer_event_t, i_list);
Hlist_del (&event->i_hash);
List_del (&event->i_list);
Free_timer_event (event);
} while (0);
#else do {int hash;
struct Hlist_node *hp, *hn; for (hash = 0; hash <= mul_wheel_timer_hashlen_max; hash++) {Hlist_for_each_safe (HP, HN, &mulwheeltime
R.hlist[hash]) {struct mul_timer_event_t * event = hlist_entry (HP, struct mul_timer_event_t, i_hash);
Hlist_del (&event->i_hash);
Free_timer_event (event);
}} while (0);
#endif Pthread_mutex_destroy (&mulwheeltimer.lock);
Bzero (&mulwheeltimer, sizeof (struct mul_wheel_timer_t));
printf ("[MWt] Destroy success.\n");
return (0); /** * Mul_wheel_timer_set_event * Set Event Timer * * Params: * timedelay_sec-Specifies the time of the first fire: how many seconds after the current timer first starts to fire E
Vent * 0: Immediate excitation
* > 0: Delay Time (SEC) * timeinterval_sec-How many seconds after the first event is fired. * 0: does not excite * > 0: Interval How many seconds time fires * Returns: * > 0:mul_eventid_t, Success * < 0:failed/__a TTRIBUTE__ ((used)) static mul_eventid_t mul_wheel_timer_set_event (unsigned int timedelay_sec, unsigned int
timeinterval_sec, Int (*ON_EVENT_CB) (Mul_event_handle eventhdl, void *eventarg), void *eventarg) {int err, hash;
int64_t On_counter;
mul_timer_event_t *new_event;
if (timedelay_sec = = 1 | | timeinterval_sec = = 1) {/** Invalid timer */return (-2);
Err = Pthread_mutex_trylock (&mulwheeltimer.lock);
if (err) {/** multithreaded lock failed/printf ("[MWt] Pthread_mutex_trylock error (%d):%s\n", err, strerror (err));
Return (-1); /** when mulwheeltimer.counter = = On_counter is fired * therefore set to On_counter for the hash key save Event */On_counter = Tim
Er_on_counter_hash (&mulwheeltimer, timedelay_sec, &hash); New_event= (mul_timer_event_t *) malloc (sizeof (mul_timer_event_t));
if (! new_event) {/** out of memory */Pthread_mutex_unlock (&mulwheeltimer.lock);
Return (-4);
} bzero (New_event, sizeof (mul_timer_event_t));
New_event->eventid = mulwheeltimer.eventid++;
New_event->on_counter = On_counter;
/** Current Time * * new_event->value.it_value.tv_sec = timedelay_sec;
/** Next time * * new_event->value.it_interval.tv_sec = timeinterval_sec;
New_event->hash = hash;
/** set callback parameters and functions, the callback function is implemented by the user himself/new_event->eventarg = EventArg;
NEW_EVENT->TIMER_EVENT_CB = ON_EVENT_CB; printf ("\033[31m+create event_%lld. (%d:%d) \033[0m\n ", (Long Long) New_event->eventid, (int) new_event->value.it_value.tv_sec, (int) new_event-&
GT;VALUE.IT_INTERVAL.TV_SEC);
#if mul_wheel_timer_has_dlist = = 1/** string into long string/List_add (&new_event->i_list, &mulwheeltimer.dlist); #endif/** Hash Short String */Hlist_adD_head (&new_event->i_hash, &mulwheeltimer.hlist[hash]);
/** set Reference count to 1 */NEW_EVENT->REFC = 1;
Demonstrates how to delete itself:////hlist_del (&new_event->i_hash);
List_del (&new_event->i_list);
Free_timer_event (new_event);
Pthread_mutex_unlock (&mulwheeltimer.lock);
Return new_event->eventid; /** * mul_wheel_timer_remove_event * Deletes events from multiple timers. The call simply marks the event to be deleted.
The behavior of the real deletion is determined by the system.
* * Returns: * 0:success * -1:failed. */__attribute__ ((used)) static int mul_wheel_timer_remove_event (Mul_event_handle eventhdl) {mul_timer_event_t * Eve
NT = mul_handle_cast_event (EVENTHDL);
printf ("Remove event-%lld\n", (Long Long) event->eventid);
/** set reference count of 0, automatically remove/__sync_lock_release (&EVENT->REFC) When an event is triggered;
return 0; /** * Mul_wheel_timer_fire_event * finds event * * Params based on currently fired counter: * On_counter-Currently fired counter * * returns : */__attribute__ ((used)) Static mul_timer_event_t * Mul_wheel_timer_fiRe_event (int64_t fire_counter) {struct Hlist_node *hp;
struct Hlist_node *hn;
int hash = (int) (Fire_counter & Mul_wheel_timer_hashlen_max); Hlist_for_each_safe (HP, HN, &mulwheeltimer.hlist[hash]) {struct mul_timer_event_t * event = Hlist_entry (HP, S
Truct mul_timer_event_t, I_hash);
if (Event->on_counter = = fire_counter) {/** First removes itself from the list */Hlist_del (&event->i_hash);
#if mul_wheel_timer_has_dlist = = 1 List_del (&event->i_list);
#endif if (__sync_lock_test_and_set (&EVENT->REFC, 1) = = 0) {/** request to delete the event * *
Free_timer_event (event); else {/** Fire event callback function/EVENT->TIMER_EVENT_CB (&event->eventid, Event->eventa
RG); if (event->value.it_interval.tv_sec = = 0) {/* Use only once, next time no longer fires, delete event/Free_timer
_event (event); else {event->on_counter = Timer_on_counter_hash (&mulwheeltimer, event->value.it_i
Nterval.tv_sec, &event->hash); #if mul_wheel_timer_has_dlist = = 1/** string into long string/List_add (&event->i_list, &am
P;mulwheeltimer.dlist); #endif/** Hash Short string/Hlist_add_head (&event->i_hash, &MULWHEELTIMER.HL
Ist[event->hash]);
return 0;
} __attribute__ ((used)) static void Sigalarm_handler (int signo) {int err;
int64_t On_counter;
Err = Pthread_mutex_trylock (&mulwheeltimer.lock); if (err) {/** multithreaded lock failed/printf ("[mwt]\033[33m Lock Error:%llu\033[0m\n", (unsigned long long) mulwheelt
Imer.counter);
Return
/** the current fired counter/on_counter = mulwheeltimer.counter++; /** * Fire Event: This event invokes a user-supplied callback function in a locked state: * On_timer_eveNT * *!!
Therefore, it is not possible to perform a long operation in On_timer_event!!
* * Mul_wheel_timer_fire_event (On_counter);
Pthread_mutex_unlock (&mulwheeltimer.lock);
printf ("* Alarm:%llu\n", (unsigned long) on_counter);
#if defined (__cplusplus)} #endif #endif/* mul_wheel_timer_h_included * *
C code for double-linked lists:
/** * dhlist.h *-deque list and hash list from Linux Kernel * * to Linux Kernel * for Windows and Linux * * mo Dified by Cheungmine * 2013-4, 2018-02-03/#ifndef _dh_list_h #define _DH_LIST_H #if defined (__cplusplus) extern "C" {#endif #include <assert.h> #include <stdio.h> #include <string.h> #include <memory.h> #include <malloc.h>/** * BKDR Hash Function * http://www.cnblogs.com/-clq/archive/2012/05/31/2528153.html/static
inline unsigned int bkdrhash (char *str) {/* seed:31 131 1313 13131 131313 etc. */unsigned int seed = 131;
unsigned int hash = 0;
while (*STR) {hash = hash * seed + (*str++);
Return (hash & 0x7fffffff); static inline unsigned int BKDRHash2 (char *str, int hashlen) {/* seed:31 131 1313 13131-131313 etc. * * uns
igned int seed = 131;
unsigned int hash = 0;
while (*STR) {hash = hash * seed + (*str++); Return (Hash & 0x7FFFFFFF) & Hashlen); #ifdef Config_illegal_pointer_value # define Poison_pointer_delta _ac (Config_illegal_pointer_value, UL) #else # def Ine Poison_pointer_delta 0 #endif/** * are non-null pointers that'll result in page faults * under normal CIR
Cumstances, used to verify that nobody uses * non-initialized list entries. * * #define LIST_POISON1 ((void *) 0x00100100 + poison_pointer_delta) #define List_poison2 (void *) 0x00200200 + poison_ Pointer_delta) #ifdef typeof static inline void prefetch (const void *x) {;} static inline void Prefetchw (const void *x)
{;} /** * filename:linux-2.6.7/include/linux/stddef.h/#define OFFSET_OF (Type, member) ((size_t) & ((type *) 0)->mem BER) #define CONTAINER_OF (PTR, type, member) ({\ Const typeof ((type *) 0)->member) *__mptr = (ptr); \ (Typ E *) ((char *) __mptr-offset_of (type,member)); #else static inline int prefetch (const void *x) {; return 1;} static inline int prefetchw (const void *x) {; return 1;} #define OFFSET_OF (Type, field) (unsigned int) (uintptr_t) (void*) & ((type *) 0)->field) #define CONTAINER_OF (AD
Dress, type, field) \ ((Type *) ((char *) (address)-offset_of (Type, field)) #endif/* typeof/* struct List_head {
struct List_head *next, *prev;
}; #define LIST_HEAD_INIT (name) {& (name), & (name)} #define LIST_HEAD (name) \ struct List_head name = List_head _init (name) #define INIT_LIST_HEAD (PTR) do {(PTR)->next = (PTR); (PTR)->prev = (PTR);
\} while (0)/* Insert A new entry between two known consecutive entries.
* * This is a for internal list manipulation where we know * the Prev/next entries already! */static inline void __list_add (struct list_head *_new, struct list_head,