1 /*
2 * $Id: phk_event.c,v 1.2 2007/11/10 19:12:19 phk Exp $
3 */
4
5 #include <assert.h>
6 #include <stdio.h>
7 #include <errno.h>
8 #include <poll.h>
9 #include <time.h>
10 #include <signal.h>
11 #include <string.h>
12 #include <stdlib.h>
13
14 #include "phk_event.h"
15 #include "phk_obj.h"
16 #include "phk_binheap.h"
17
18 /* INFTIM indicates an infinite timeout for poll(2) */
19 #ifndef INFTIM
20 #define INFTIM -1
21 #endif
22
23 struct evsig {
24 struct phkevbase *evb;
25 struct phkev *ev;
26 struct sigaction sigact;
27 unsigned char happened;
28 };
29
30 static struct evsig *ev_sigs;
31 static int ev_nsig;
32
33 struct phkevbase {
34 unsigned magic;
35 #define EVBASE_MAGIC 0x0cfd976f
36 TAILQ_HEAD(,phkev__) events;
37 struct pollfd *pfd;
38 unsigned npfd;
39 unsigned lpfd;
40 struct phkbinheap *binheap;
41 unsigned char compact_pfd;
42 unsigned char disturbed;
43 unsigned psig;
44 };
45
46 struct phkev__ {
47 unsigned magic;
48 #define PHKEV___MAGIC 0x3c518c90
49 struct phkev ev;
50 double when;
51 TAILQ_ENTRY(phkev__) list;
52 unsigned binheap_idx;
53 unsigned privflags;
54 struct phkevbase *evb;
55 unsigned poll_idx;
56 };
57
58 /*--------------------------------------------------------------------*/
59
60 static double
ev_now(void)61 ev_now(void)
62 {
63 double t;
64 struct timespec ts;
65
66 assert(clock_gettime(CLOCK_MONOTONIC, &ts) == 0);
67 t = ts.tv_sec + ts.tv_nsec * 1e-9;
68 return (t);
69 }
70
71 /*--------------------------------------------------------------------*/
72
73 static void
ev_bh_update(void * priv,void * a,unsigned u)74 ev_bh_update(void *priv, void *a, unsigned u)
75 {
76 struct phkevbase *evb;
77 struct phkev__ *e;
78
79 CAST_OBJ_NOTNULL(evb, priv, EVBASE_MAGIC);
80 CAST_OBJ_NOTNULL(e, a, PHKEV___MAGIC);
81 e->binheap_idx = u;
82 }
83
84 static int
ev_bh_cmp(void * priv,void * a,void * b)85 ev_bh_cmp(void *priv, void *a, void *b)
86 {
87 struct phkevbase *evb;
88 struct phkev__ *ea, *eb;
89
90 CAST_OBJ_NOTNULL(evb, priv, EVBASE_MAGIC);
91 CAST_OBJ_NOTNULL(ea, a, PHKEV___MAGIC);
92 CAST_OBJ_NOTNULL(eb, b, PHKEV___MAGIC);
93 return (ea->when < eb->when);
94 }
95
96 /*--------------------------------------------------------------------*/
97
98 static int
ev_get_pfd(struct phkevbase * evb)99 ev_get_pfd(struct phkevbase *evb)
100 {
101 unsigned u;
102 void *p;
103
104 if (evb->lpfd < evb->npfd)
105 return (0);
106
107 if (evb->npfd > 256)
108 u = evb->npfd + 256;
109 else if (evb->npfd < 8)
110 u = 8;
111 else
112 u = evb->npfd * 2;
113 p = realloc(evb->pfd, sizeof *evb->pfd * u);
114 if (p == NULL)
115 return (1);
116 evb->npfd = u;
117 evb->pfd = p;
118 return (0);
119 }
120
121 /*--------------------------------------------------------------------*/
122
123 static int
ev_get_sig(int sig)124 ev_get_sig(int sig)
125 {
126 struct evsig *os;
127
128 if (sig < ev_nsig)
129 return (0);
130
131 os = calloc(sizeof *os, (sig + 1));
132 if (os == NULL)
133 return (ENOMEM);
134
135 memcpy(os, ev_sigs, ev_nsig * sizeof *os);
136
137 free(ev_sigs);
138 ev_sigs = os;
139 ev_nsig = sig + 1;
140
141 return (0);
142 }
143
144 /*--------------------------------------------------------------------*/
145
146 static void
ev_sighandler(int sig)147 ev_sighandler(int sig)
148 {
149 struct evsig *es;
150
151 assert(sig < ev_nsig);
152 assert(ev_sigs != NULL);
153 es = &ev_sigs[sig];
154 if (!es->happened)
155 es->evb->psig++;
156 es->happened = 1;
157 }
158
159 /*--------------------------------------------------------------------*/
160
161 struct phkevbase *
PhkEvNewBase(void)162 PhkEvNewBase(void)
163 {
164 struct phkevbase *evb;
165
166 evb = calloc(sizeof *evb, 1);
167 if (evb == NULL)
168 return (evb);
169 if (ev_get_pfd(evb)) {
170 free(evb);
171 return (NULL);
172 }
173 evb->magic = EVBASE_MAGIC;
174 TAILQ_INIT(&evb->events);
175 evb->binheap = PhkBinHeapNew(evb, ev_bh_cmp, ev_bh_update);
176 return (evb);
177 }
178
179 /*--------------------------------------------------------------------*/
180
181 void
PhkEvDestroyBase(struct phkevbase * evb)182 PhkEvDestroyBase(struct phkevbase *evb)
183 {
184 CHECK_OBJ_NOTNULL(evb, EVBASE_MAGIC);
185 evb->magic = 0;
186 free(evb);
187 }
188
189 /*--------------------------------------------------------------------*/
190
191 struct phkev *
PhkEvNew(void)192 PhkEvNew(void)
193 {
194 struct phkev__ *e;
195
196 e = calloc(sizeof *e, 1);
197 if (e != NULL) {
198 e->magic = PHKEV___MAGIC;
199 e->ev.magic = PHKEV_MAGIC;
200 e->ev.fd = -1;
201 e->ev.real = e;
202 }
203 return (&e->ev);
204 }
205
206 /*--------------------------------------------------------------------*/
207
208 int
PhkEvAdd(struct phkevbase * evb,struct phkev * e)209 PhkEvAdd(struct phkevbase *evb, struct phkev *e)
210 {
211 struct evsig *es;
212 struct phkev__ *E;
213
214 CHECK_OBJ_NOTNULL(evb, EVBASE_MAGIC);
215 CHECK_OBJ_NOTNULL(e, PHKEV_MAGIC);
216 E = e->real;
217 CHECK_OBJ_NOTNULL(E, PHKEV___MAGIC);
218 assert(e->callback != NULL);
219 assert(e->sig >= 0);
220 assert(e->timeout >= 0.0);
221 assert(e->fd < 0 || e->fd_flags);
222
223 if (e->sig > 0 && ev_get_sig(e->sig))
224 return (ENOMEM);
225
226 if (e->fd >= 0 && ev_get_pfd(evb))
227 return (ENOMEM);
228
229 if (e->sig > 0) {
230 es = &ev_sigs[e->sig];
231 if (es->ev != NULL)
232 return (EBUSY);
233 assert(es->happened == 0);
234 es->ev = e;
235 es->evb = evb;
236 es->sigact.sa_flags = e->sig_flags;
237 es->sigact.sa_handler = ev_sighandler;
238 } else {
239 es = NULL;
240 }
241
242 if (e->fd >= 0) {
243 assert(evb->lpfd < evb->npfd);
244 evb->pfd[evb->lpfd].fd = e->fd;
245 evb->pfd[evb->lpfd].events =
246 e->fd_flags & (EV_RD|EV_WR|EV_ERR|EV_HUP);
247 E->poll_idx = evb->lpfd;
248 evb->lpfd++;
249 } else
250 E->poll_idx = -1;
251
252 e->magic = PHKEV_MAGIC; /* before binheap_insert() */
253
254 if (e->timeout != 0.0) {
255 E->when += ev_now() + e->timeout;
256 PhkBinHeapInsert(evb->binheap, E);
257 assert(E->binheap_idx > 0);
258 } else {
259 E->when = 0.0;
260 E->binheap_idx = 0;
261 }
262
263 E->evb = evb;
264 E->privflags = 0;
265 if (e->fd < 0)
266 TAILQ_INSERT_TAIL(&evb->events, E, list);
267 else
268 TAILQ_INSERT_HEAD(&evb->events, E, list);
269
270 if (e->sig > 0) {
271 assert(es != NULL);
272 assert(sigaction(e->sig, &es->sigact, NULL) == 0);
273 }
274
275 return (0);
276 }
277
278 /*--------------------------------------------------------------------*/
279
280 void
PhkEvDel(struct phkevbase * evb,struct phkev * e)281 PhkEvDel(struct phkevbase *evb, struct phkev *e)
282 {
283 struct evsig *es;
284 struct phkev__ *E;
285
286 CHECK_OBJ_NOTNULL(evb, EVBASE_MAGIC);
287 CHECK_OBJ_NOTNULL(e, PHKEV_MAGIC);
288 E = e->real;
289 CHECK_OBJ_NOTNULL(E, PHKEV___MAGIC);
290 assert(evb == E->evb);
291
292 if (E->binheap_idx != 0)
293 PhkBinHeapDelete(evb->binheap, E->binheap_idx);
294 assert(E->binheap_idx == 0);
295
296 if (e->fd >= 0) {
297 evb->pfd[E->poll_idx].fd = -1;
298 if (E->poll_idx == evb->lpfd - 1)
299 evb->lpfd--;
300 else
301 evb->compact_pfd++;
302 e->fd = -1;
303 }
304
305 if (e->sig > 0) {
306 assert(e->sig < ev_nsig);
307 es = &ev_sigs[e->sig];
308 assert(es->ev == e);
309 es->ev = NULL;
310 es->evb = NULL;
311 es->sigact.sa_flags = e->sig_flags;
312 es->sigact.sa_handler = SIG_DFL;
313 assert(sigaction(e->sig, &es->sigact, NULL) == 0);
314 es->happened = 0;
315 }
316
317 TAILQ_REMOVE(&evb->events, E, list);
318
319 E->evb = NULL;
320
321 evb->disturbed = 1;
322 }
323
324 /*--------------------------------------------------------------------*/
325
326 int
PhkEvSched(struct phkevbase * evb)327 PhkEvSched(struct phkevbase *evb)
328 {
329 int i;
330
331 CHECK_OBJ_NOTNULL(evb, EVBASE_MAGIC);
332 do
333 i = PhkEvSchedOne(evb);
334 while (i == 1);
335 return (i);
336 }
337
338 /*--------------------------------------------------------------------*/
339
340 static void
ev_compact_pfd(struct phkevbase * evb)341 ev_compact_pfd(struct phkevbase *evb)
342 {
343 unsigned u;
344 struct pollfd *p;
345 struct phkev__ *EP;
346
347 p = evb->pfd;
348 EP = TAILQ_FIRST(&evb->events);
349 for (u = 0; u < evb->lpfd; u++, p++) {
350 if (p->fd >= 0)
351 continue;
352 for(; EP != NULL; EP = TAILQ_NEXT(EP, list)) {
353 if (EP->ev.fd >= 0 && EP->poll_idx > u)
354 break;
355 }
356 if (EP == NULL)
357 break;
358 *p = evb->pfd[EP->poll_idx];
359 EP->poll_idx = u;
360 }
361 evb->lpfd = u;
362 evb->compact_pfd = 0;
363 }
364
365 /*--------------------------------------------------------------------*/
366
367 static int
ev_sched_timeout(struct phkevbase * evb,struct phkev__ * E,double t)368 ev_sched_timeout(struct phkevbase *evb, struct phkev__ *E, double t)
369 {
370 int i;
371
372 i = E->ev.callback(&E->ev, 0);
373 if (i) {
374 PhkEvDel(evb, &E->ev);
375 free(E);
376 } else {
377 E->when = t + E->ev.timeout;
378 PhkBinHeapDelete(evb->binheap, E->binheap_idx);
379 PhkBinHeapInsert(evb->binheap, E);
380 }
381 return (1);
382 }
383
384 static int
ev_sched_signal(struct phkevbase * evb)385 ev_sched_signal(struct phkevbase *evb)
386 {
387 int i, j;
388 struct evsig *es;
389 struct phkev *e;
390
391 es = ev_sigs;
392 for (j = 0; j < ev_nsig; j++, es++) {
393 if (!es->happened || es->evb != evb)
394 continue;
395 evb->psig--;
396 es->happened = 0;
397 e = es->ev;
398 assert(e != NULL);
399 i = e->callback(e, EV_SIG);
400 if (i) {
401 PhkEvDel(evb, e);
402 free(e);
403 }
404 }
405 return (1);
406 }
407
408 int
PhkEvSchedOne(struct phkevbase * evb)409 PhkEvSchedOne(struct phkevbase *evb)
410 {
411 double t;
412 struct phkev__ *E, *E2, *E3;
413 int i, j, tmo;
414 struct pollfd *pfd;
415
416 CHECK_OBJ_NOTNULL(evb, EVBASE_MAGIC);
417 E = PhkBinHeapRoot(evb->binheap);
418 if (E != NULL) {
419 CHECK_OBJ_NOTNULL(E, PHKEV___MAGIC);
420 assert(E->binheap_idx == 1);
421 t = ev_now();
422 if (E->when <= t)
423 return (ev_sched_timeout(evb, E, t));
424 tmo = (E->when - t) * 1e3;
425 if (tmo == 0)
426 tmo = 1;
427 } else
428 tmo = INFTIM;
429
430 if (evb->compact_pfd)
431 ev_compact_pfd(evb);
432
433 if (tmo == INFTIM && evb->lpfd == 0)
434 return (0);
435
436 if (evb->psig)
437 return (ev_sched_signal(evb));
438 assert(evb->lpfd < evb->npfd);
439 i = poll(evb->pfd, evb->lpfd, tmo);
440 if(i == -1 && errno == EINTR)
441 return (ev_sched_signal(evb));
442 if (i == 0) {
443 assert(E != NULL);
444 t = ev_now();
445 if (E->when <= t)
446 return (ev_sched_timeout(evb, E, t));
447 }
448 evb->disturbed = 0;
449 #ifdef __DragonFly__
450 TAILQ_FOREACH_MUTABLE(E, &evb->events, list, E2) {
451 #else
452 TAILQ_FOREACH_SAFE(E, &evb->events, list, E2) {
453 #endif
454 if (i == 0)
455 break;
456 if (E->ev.fd < 0)
457 continue;
458 assert(E->poll_idx < evb->lpfd);
459 pfd = &evb->pfd[E->poll_idx];
460 assert(pfd->fd == E->ev.fd);
461 if (!pfd->revents)
462 continue;
463 j = E->ev.callback(&E->ev, pfd->revents);
464 i--;
465 if (evb->disturbed) {
466 TAILQ_FOREACH(E3, &evb->events, list) {
467 if (E3 == E) {
468 E3 = TAILQ_NEXT(E, list);
469 break;
470 } else if (E3 == E2)
471 break;
472 }
473 E2 = E3;
474 evb->disturbed = 0;
475 }
476 if (j) {
477 PhkEvDel(evb, &E->ev);
478 evb->disturbed = 0;
479 free(E);
480 }
481 }
482 return (1);
483 }
484