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