timer.c
00001
00002
00003
00004
00005
00006
00007
00008
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;
00031 timerm_t *timer;
00032 time_t expire;
00033 talarm_cb_t cb;
00034 void *arg;
00035 pid_t owner;
00036 };
00037
00038 struct timerm_s
00039 {
00040 talarm_list_t alist;
00041 time_t next;
00042 int cb_running;
00043 #ifdef OS_WIN
00044 CRITICAL_SECTION cs;
00045 HANDLE hthread;
00046 DWORD tid;
00047 #endif
00048 };
00049
00050
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
00094 al = TAILQ_FIRST(&timer->alist);
00095 nop_err_if(al == NULL);
00096
00097 if(al->owner != pid)
00098 {
00099
00100
00101
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
00112 timer->cb_running = 1;
00113
00114
00115 al->cb(al, al->arg);
00116
00117 timer->cb_running = 0;
00118 }
00119
00120
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);
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
00223
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
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
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;
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
00320 if(timerm_alarm_pending(a))
00321 TAILQ_REMOVE(&timer->alist, a, np);
00322
00323
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 }