timer.c

00001 /*
00002  * Copyright (c) 2005-2012 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     pid_t owner;                /* process that set the alarm   */
00036 };
00037 
00038 struct timerm_s
00039 {
00040     talarm_list_t alist;        /* alarm list                   */
00041     time_t next;                /* next timestamp               */
00042 #ifdef OS_WIN
00043     CRITICAL_SECTION cs;
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     time_t n = time(0) + timeout;
00055 
00056     if(timeout && (timer->next == 0 || n < timer->next))
00057     {
00058         timer->next = n;
00059 #ifdef OS_UNIX
00060         alarm(timeout);
00061 #endif
00062     } 
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(U_MAX(1, al->expire - now));
00074 
00075     return 0;
00076 }
00077 
00078 void timerm_sigalrm(int sigalrm)
00079 {
00080     talarm_t *al = NULL;
00081     pid_t pid = getpid();
00082     time_t now = time(0);
00083 
00084     u_unused_args(sigalrm);
00085 
00086     dbg_err_if(timer == NULL);
00087 
00088     timer->next = 0;
00089 
00090     for(;;)
00091     {
00092         /* get the topmost item and remove it from the list */
00093         al = TAILQ_FIRST(&timer->alist);
00094         nop_err_if(al == NULL);
00095 
00096         if(al->owner != pid)
00097         {
00098             /* this alert has been inherited from the parent, we cannot
00099              * timerm_del() it because the user may have a reference to it
00100              * somewhere so we just remove it from the list of timers */
00101             TAILQ_REMOVE(&timer->alist, al, np);
00102             continue;
00103         }
00104 
00105         if(al->expire > now)
00106             break;
00107         
00108         TAILQ_REMOVE(&timer->alist, al, np);
00109 
00110         /* call the callback function */
00111         al->cb(al, al->arg);
00112     }
00113 
00114     /* prepare for the next alarm */
00115     if(TAILQ_FIRST(&timer->alist))
00116         timerm_set_next();
00117 
00118 err:
00119     return;
00120 }
00121 
00122 static int timerm_block_alarms(void)
00123 {
00124 #ifdef OS_UNIX
00125     dbg_err_if(u_sig_block(SIGALRM));
00126 #endif
00127 
00128 #ifdef OS_WIN
00129     EnterCriticalSection(&timer->cs);
00130 #endif
00131 
00132     return 0;
00133 err:
00134     return ~0;
00135 }
00136 
00137 static int timerm_unblock_alarms(void)
00138 {
00139 #ifdef OS_UNIX
00140     dbg_err_if(u_sig_unblock(SIGALRM));
00141 #endif
00142 
00143 #ifdef OS_WIN
00144     LeaveCriticalSection(&timer->cs);
00145 #endif
00146 
00147     return 0;
00148 err:
00149     return ~0;
00150 }
00151 
00152 static int timerm_free(timerm_t *t)
00153 {
00154     talarm_t *a = NULL;
00155 
00156     dbg_return_if (t == NULL, ~0);
00157     
00158     if(t)
00159     {
00160         while((a = TAILQ_FIRST(&t->alist)) != NULL)
00161             dbg_if(timerm_del(a));
00162 
00163         U_FREE(t);
00164     }
00165 
00166     return 0;
00167 }
00168 
00169 #ifdef OS_WIN
00170 static DWORD WINAPI thread_func(LPVOID param)
00171 {
00172     for(;;Sleep(250))
00173     {
00174         if(timer->next == NULL)
00175             continue;
00176 
00177         if((timer->next - time(0)) <= 0)
00178             timerm_sigalrm(0);  /* raise the alarm */
00179     }
00180 
00181     return 0;
00182 }
00183 #endif
00184 
00185 static int timerm_create(timerm_t **pt)
00186 {
00187     timerm_t *t = NULL;
00188 
00189     dbg_return_if (pt == NULL, ~0);
00190 
00191     t = u_zalloc(sizeof(timerm_t));
00192     dbg_err_if(t == NULL);
00193 
00194     TAILQ_INIT(&t->alist);
00195 
00196 #ifdef OS_WIN
00197     InitializeCriticalSection(&t->cs);
00198 
00199     dbg_err_if((t->hthread = CreateThread(NULL, 0, thread_func, NULL, 0, 
00200         &t->tid)) == NULL); 
00201 #endif
00202 
00203     *pt = t;
00204 
00205     return 0;
00206 err:
00207     if(t)
00208         timerm_free(t);
00209     return ~0;
00210 }
00211 
00212 int timerm_add(int secs, talarm_cb_t cb, void *arg, talarm_t **pa)
00213 {
00214     talarm_t *al = NULL;
00215     talarm_t *item = NULL;
00216     time_t now = time(0);
00217     pid_t pid = getpid();
00218 
00219     dbg_return_if (cb == NULL, ~0);
00220     dbg_return_if (pa == NULL, ~0);
00221 
00222     if(timer == NULL)
00223     {
00224         dbg_err_if(timerm_create(&timer));
00225         #ifdef OS_UNIX
00226         dbg_err_if(u_signal(SIGALRM, timerm_sigalrm));
00227         #endif
00228     }
00229 
00230     al = (talarm_t*)u_zalloc(sizeof(talarm_t));
00231     dbg_err_if(al == NULL);
00232 
00233     al->timer = timer;
00234     al->cb = cb;
00235     al->arg = arg;
00236     al->expire = now + secs;
00237     al->owner = pid;
00238 
00239     dbg_err_if(timerm_block_alarms());
00240 
00241     /* insert al ordered by the expire field (smaller first) */
00242     TAILQ_FOREACH(item, &timer->alist, np)
00243         if(al->expire < item->expire)
00244             break;
00245 
00246     if(item)
00247         TAILQ_INSERT_BEFORE(item, al, np);
00248     else
00249         TAILQ_INSERT_TAIL(&timer->alist, al, np);
00250 
00251     /* set the timer for the earliest alarm */
00252     timerm_set_next(); 
00253 
00254     dbg_err_if(timerm_unblock_alarms());
00255 
00256     *pa = al;
00257 
00258     return 0;
00259 err:
00260     u_dbg("[%lu] timerm_add error", (unsigned long) getpid());
00261     if(timer)
00262     {
00263         (void) timerm_free(timer);
00264         timer = NULL;
00265     }
00266     U_FREE(al);
00267 
00268     dbg_err_if(timerm_unblock_alarms());
00269 
00270     return ~0;
00271 }
00272 
00273 static int timerm_alarm_pending(talarm_t *a)
00274 {
00275     talarm_t *t;
00276 
00277     TAILQ_FOREACH(t, &timer->alist,np)
00278     {
00279         if(t == a)
00280             return 1;   /* found */
00281     }
00282     return 0;
00283 }
00284 
00285 int timerm_del(talarm_t *a)
00286 {
00287     dbg_return_if(a == NULL, ~0);
00288 
00289     dbg_err_if(timerm_block_alarms());
00290 
00291     /* if not expired remove it from the list */
00292     if(timerm_alarm_pending(a))
00293         TAILQ_REMOVE(&timer->alist, a, np);
00294 
00295     /* set the timer for the earliest alarm */
00296     timerm_set_next();
00297 
00298     dbg_err_if(timerm_unblock_alarms());
00299 
00300     U_FREE(a);
00301 
00302     return 0;
00303 err:
00304     dbg_err_if(timerm_unblock_alarms());
00305     return ~0;
00306 }

←Products
Copyright © 2005-2012 - KoanLogic S.r.l. - All rights reserved