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

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