Main Page | Modules | Data Structures | Directories | File List | Data Fields | Globals

timer.c

00001 /*
00002  * Copyright (c) 2005, 2006 by KoanLogic s.r.l. <http://www.koanlogic.com>
00003  * All rights reserved.
00004  *
00005  * This file is part of KLone, and as such it is subject to the license stated
00006  * in the LICENSE file which you have received as part of this distribution.
00007  *
00008  * $Id: timer.c,v 1.16 2007/10/26 10:14:52 tat Exp $
00009  */
00010 
00011 #include "klone_conf.h"
00012 #include <time.h>
00013 #include <unistd.h>
00014 #include <signal.h>
00015 #include <klone/timer.h>
00016 #include <klone/utils.h>
00017 #include <u/libu.h>
00018 
00019 #ifdef OS_WIN
00020 #include <windows.h>
00021 #endif
00022 
00023 TAILQ_HEAD(talarm_list_s, talarm_s);
00024 typedef struct talarm_list_s talarm_list_t;
00025 
00026 typedef void (*timerm_cb_t)(int);
00027 
00028 struct talarm_s
00029 {
00030     TAILQ_ENTRY(talarm_s) np;   /* next & prev pointers         */
00031     timerm_t *timer;            /* timerm_t that owns the alarm */
00032     time_t expire;              /* when to fire the alarm       */
00033     talarm_cb_t cb;             /* alarm callback               */
00034     void *arg;                  /* cb opaque argument           */
00035 };
00036 
00037 struct timerm_s
00038 {
00039     talarm_list_t alist;         /* alarm list                   */
00040 
00041 #ifdef OS_WIN
00042     CRITICAL_SECTION cs;
00043     time_t next;                /* next timestamp               */
00044     HANDLE hthread;             /* thread handle                */
00045     DWORD tid;                  /* thread id                    */
00046 #endif
00047 };
00048 
00049 /* this must be a singleton */
00050 static timerm_t *timer = NULL;
00051 
00052 static int timerm_set_alarm(int timeout)
00053 {
00054 #ifdef OS_UNIX
00055     /* if timeout == 0 disable the alarm */
00056     alarm(timeout);
00057 #else
00058     if(timeout > 0)
00059         timer->next = time(0) + timeout;
00060     else
00061         timer->next = NULL;
00062 #endif
00063 
00064     return 0;
00065 }
00066 
00067 static int timerm_set_next(void)
00068 {
00069     talarm_t *al = NULL;
00070     time_t now = time(0);
00071 
00072     if((al = TAILQ_FIRST(&timer->alist)) == NULL)
00073         timerm_set_alarm(0);   /* disable the alarm */
00074     else
00075         timerm_set_alarm(MAX(1, al->expire - now));
00076 
00077     return 0;
00078 }
00079 
00080 void timerm_sigalrm(int sigalrm)
00081 {
00082     talarm_t *al = NULL, *next = NULL;
00083     int expire;
00084 
00085     u_unused_args(sigalrm);
00086 
00087     dbg_err_if(timer == NULL);
00088 
00089     for(;;)
00090     {
00091         /* get the topmost item and remove it from the list */
00092         al = TAILQ_FIRST(&timer->alist);
00093         nop_err_if(al == NULL);
00094 
00095         expire = al->expire;
00096 
00097         TAILQ_REMOVE(&timer->alist, al, np);
00098 
00099         /* call the callback function */
00100         al->cb(al, al->arg);
00101 
00102         /* handle alarms with the same expiration date */
00103         next = TAILQ_FIRST(&timer->alist);
00104         if(next && next->expire == expire)
00105             continue;
00106 
00107         break;
00108     }
00109 
00110     /* prepare for the next alarm */
00111     if(TAILQ_FIRST(&timer->alist))
00112         timerm_set_next();
00113 
00114 err:
00115     return;
00116 }
00117 
00118 static int timerm_block_alarms(void)
00119 {
00120 #ifdef OS_UNIX
00121     dbg_err_if(u_sig_block(SIGALRM));
00122 #endif
00123 
00124 #ifdef OS_WIN
00125     EnterCriticalSection(&timer->cs);
00126 #endif
00127 
00128     return 0;
00129 err:
00130     return ~0;
00131 }
00132 
00133 static int timerm_unblock_alarms(void)
00134 {
00135 #ifdef OS_UNIX
00136     dbg_err_if(u_sig_unblock(SIGALRM));
00137 #endif
00138 
00139 #ifdef OS_WIN
00140     LeaveCriticalSection(&timer->cs);
00141 #endif
00142 
00143     return 0;
00144 err:
00145     return ~0;
00146 }
00147 
00148 static int timerm_free(timerm_t *t)
00149 {
00150     talarm_t *a = NULL;
00151 
00152     dbg_return_if (t == NULL, ~0);
00153     
00154     if(t)
00155     {
00156         while((a = TAILQ_FIRST(&t->alist)) != NULL)
00157             dbg_if(timerm_del(a));
00158 
00159         U_FREE(t);
00160     }
00161 
00162     return 0;
00163 }
00164 
00165 #ifdef OS_WIN
00166 static DWORD WINAPI thread_func(LPVOID param)
00167 {
00168     for(;;Sleep(250))
00169     {
00170         if(timer->next == NULL)
00171             continue;
00172 
00173         if((timer->next - time(0)) <= 0)
00174             timerm_sigalrm(0);  /* raise the alarm */
00175     }
00176 
00177     return 0;
00178 }
00179 #endif
00180 
00181 static int timerm_create(timerm_t **pt)
00182 {
00183     timerm_t *t = NULL;
00184 
00185     dbg_return_if (pt == NULL, ~0);
00186 
00187     t = u_zalloc(sizeof(timerm_t));
00188     dbg_err_if(t == NULL);
00189 
00190     TAILQ_INIT(&t->alist);
00191 
00192 #ifdef OS_WIN
00193     InitializeCriticalSection(&t->cs);
00194 
00195     dbg_err_if((t->hthread = CreateThread(NULL, 0, thread_func, NULL, 0, 
00196         &t->tid)) == NULL); 
00197 #endif
00198 
00199     *pt = t;
00200 
00201     return 0;
00202 err:
00203     if(t)
00204         timerm_free(t);
00205     return ~0;
00206 }
00207 
00208 int timerm_add(int secs, talarm_cb_t cb, void *arg, talarm_t **pa)
00209 {
00210     talarm_t *al = NULL;
00211     talarm_t *item = NULL;
00212     time_t now = time(0);
00213 
00214     dbg_return_if (cb == NULL, ~0);
00215     dbg_return_if (pa == NULL, ~0);
00216 
00217     if(timer == NULL)
00218     {
00219         dbg_err_if(timerm_create(&timer));
00220         #ifdef OS_UNIX
00221         dbg_err_if(u_signal(SIGALRM, timerm_sigalrm));
00222         #endif
00223     }
00224 
00225     al = (talarm_t*)u_zalloc(sizeof(talarm_t));
00226     dbg_err_if(al == NULL);
00227 
00228     al->timer = timer;
00229     al->cb = cb;
00230     al->arg = arg;
00231     al->expire = now + secs;
00232 
00233     dbg_err_if(timerm_block_alarms());
00234 
00235     /* insert al ordered by the expire field (smaller first) */
00236     TAILQ_FOREACH(item, &timer->alist, np)
00237         if(al->expire <= item->expire)
00238             break;
00239 
00240     if(item)
00241         TAILQ_INSERT_BEFORE(item, al, np);
00242     else
00243         TAILQ_INSERT_TAIL(&timer->alist, al, np);
00244 
00245     /* set the timer for the earliest alarm */
00246     timerm_set_next();
00247 
00248     dbg_err_if(timerm_unblock_alarms());
00249 
00250     *pa = al;
00251 
00252     return 0;
00253 err:
00254     dbg("[%lu] timerm_add error", getpid());
00255     if(timer)
00256     {
00257         (void) timerm_free(timer);
00258         timer = NULL;
00259     }
00260     U_FREE(al);
00261 
00262     dbg_err_if(timerm_unblock_alarms());
00263 
00264     return ~0;
00265 }
00266 
00267 static int timerm_alarm_pending(talarm_t *a)
00268 {
00269     talarm_t *t;
00270 
00271     TAILQ_FOREACH(t, &timer->alist,np)
00272     {
00273         if(t == a)
00274             return 1;   /* found */
00275     }
00276     return 0;
00277 }
00278 
00279 int timerm_del(talarm_t *a)
00280 {
00281     dbg_return_if(a == NULL, ~0);
00282 
00283     dbg_err_if(timerm_block_alarms());
00284 
00285     /* if not expired remove it from the list */
00286     if(timerm_alarm_pending(a))
00287         TAILQ_REMOVE(&timer->alist, a, np);
00288 
00289     /* set the timer for the earliest alarm */
00290     timerm_set_next();
00291 
00292     dbg_err_if(timerm_unblock_alarms());
00293 
00294     U_FREE(a);
00295 
00296     return 0;
00297 err:
00298     dbg_err_if(timerm_unblock_alarms());
00299     return ~0;
00300 }