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 #ifdef OS_WIN
00043 CRITICAL_SECTION cs;
00044 HANDLE hthread;
00045 DWORD tid;
00046 #endif
00047 };
00048
00049
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
00093 al = TAILQ_FIRST(&timer->alist);
00094 nop_err_if(al == NULL);
00095
00096 if(al->owner != pid)
00097 {
00098
00099
00100
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
00111 al->cb(al, al->arg);
00112 }
00113
00114
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);
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
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
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;
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
00292 if(timerm_alarm_pending(a))
00293 TAILQ_REMOVE(&timer->alist, a, np);
00294
00295
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 }