1/*
2 * Copyright (c)1996-2002 by Hartmut Brandt
3 *	All rights reserved.
4 *
5 * Author: Hartmut Brandt
6 *
7 * Redistribution of this software and documentation and use in source and
8 * binary forms, with or without modification, are permitted provided that
9 * the following conditions are met:
10 *
11 * 1. Redistributions of source code or documentation must retain the above
12 *   copyright notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *   notice, this list of conditions and the following disclaimer in the
15 *   documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE AUTHOR
18 * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
19 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
21 * THE AUTHOR OR ITS CONTRIBUTORS  BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
24 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29/*
30 * These functions try to hide the poll/select/setitimer interface from the
31 * user. You associate callback functions with file descriptors and timers.
32 *
33 * $Begemot: libbegemot/rpoll.c,v 1.14 2004/09/21 15:59:00 brandt Exp $
34 */
35# include <stdio.h>
36# include <stdlib.h>
37# include <stddef.h>
38# include <stdarg.h>
39# include <signal.h>
40# include <string.h>
41# include <errno.h>
42# include <time.h>
43# include <assert.h>
44# include <unistd.h>
45# include <sys/time.h>
46
47# include "rpoll.h"
48
49/*
50# define DEBUG
51*/
52
53# ifdef USE_POLL
54#  ifdef NEED_POLL_XOPEN_TWIDDLE
55#   define __USE_XOPEN
56#  endif
57#  include <poll.h>
58#  ifdef NEED_POLL_XOPEN_TWIDDLE
59#   undef __USE_XOPEN
60#  endif
61#  include <stropts.h>
62# endif
63
64/*
65 * the second define is for Linux, which sometimes fails to
66 * declare INFTIM.
67 */
68# if defined(USE_SELECT) || !defined(INFTIM)
69#  define INFTIM (-1)
70# endif
71
72# if defined(SIGPOLL)
73#  define SIGNAL	SIGPOLL
74# else
75#  if defined(SIGIO)
76#   define SIGNAL	SIGIO
77#  endif
78# endif
79
80# ifdef USE_POLL
81#  define poll_in	(POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)
82#  define poll_out	(POLLOUT | POLLWRNORM | POLLWRBAND)
83#  define poll_except	(POLLERR | POLLHUP)
84# endif
85
86# ifdef BROKEN_SELECT_PROTO
87#  define SELECT_CAST(P)	(int *)P
88# else
89#  define SELECT_CAST(P)	P
90# endif
91
92
93typedef int64_t tval_t;
94
95static inline tval_t GETUSECS(void);
96
97static inline tval_t
98GETUSECS(void) {
99	struct timeval tval;
100
101	(void)gettimeofday(&tval, NULL);
102	return (tval_t)tval.tv_sec * 1000000 + tval.tv_usec;
103}
104
105/*
106 * Simple fatal exit.
107 */
108static void
109_panic(const char *fmt, ...)
110{
111	va_list ap;
112
113	va_start(ap, fmt);
114	fprintf(stderr, "panic: ");
115	vfprintf(stderr, fmt, ap);
116	fprintf(stderr, "\n");
117	va_end(ap);
118
119	exit(1);
120}
121
122static void *
123_xrealloc(void *p, size_t s)
124{
125	void *ptr;
126
127	if(p == NULL) {
128		if((ptr=malloc(s)) == NULL && (s!=0 || (ptr=malloc(1)) == NULL))
129			_panic("out of memory: xrealloc(%lx, %lu)",
130				(unsigned long)p, (unsigned long)s);
131	} else if(s == 0) {
132		free(p);
133		if((ptr=malloc(s)) == NULL && (ptr=malloc(1)) == NULL)
134			_panic("out of memory: xrealloc(%lx, %lu)",
135				(unsigned long)p, (unsigned long)s);
136	} else {
137		if((ptr = realloc(p, s)) == NULL)
138			_panic("out of memory: xrealloc(%lx, %lu)",
139				(unsigned long)p, (unsigned long)s);
140	}
141
142	return ptr;
143}
144
145/*
146 * This structure holds one registration record for files
147 */
148typedef struct {
149	int	fd;		/* file descriptor (-1 if struct unused) */
150	int	mask;		/* event flags */
151	void *	arg;		/* client arg */
152	poll_f	func;		/* handler */
153# ifdef USE_POLL
154	struct pollfd *pfd;	/* pointer to corresponding poll() structure */
155# endif
156} PollReg_t;
157
158/*
159 * Now for timers
160 */
161typedef struct {
162	uint64_t usecs;		/* microsecond value of the timer */
163	int	repeat;		/* one shot or repeat? */
164	void	*arg;		/* client arg */
165	timer_f	func;		/* handler, 0 means disfunct */
166	tval_t	when;		/* next time to trigger in usecs! */
167} PollTim_t;
168
169/* how many records should our table grow at once? */
170# define POLL_REG_GROW	100
171
172# ifdef USE_POLL
173static struct pollfd *	pfd;		/* fd list for poll() */
174# endif
175
176# ifdef USE_SELECT
177static fd_set rset, wset, xset;		/* file descriptor sets for select() */
178static int maxfd;			/* maximum fd number */
179# endif
180
181static int		in_dispatch;
182
183static PollReg_t *	regs;		/* registration records */
184static u_int		regs_alloc;	/* how many are allocated */
185static u_int		regs_used;	/* upper used limit */
186static sigset_t		bset;		/* blocked signals */
187static int 		rebuild;	/* rebuild table on next dispatch() */
188
189static int *		tfd;		/* sorted entries */
190static u_int		tfd_alloc;	/* number of entries allocated */
191static u_int		tfd_used;	/* number of entries used */
192static PollTim_t *	tims;		/* timer registration records */
193static u_int		tims_alloc;	/* how many are allocated */
194static u_int		tims_used;	/* how many are used */
195static int		resort;		/* resort on next dispatch */
196
197int	rpoll_trace;
198int	rpoll_policy;	/* if 0 start sched callbacks from 0 else try round robin */
199
200static void poll_build(void);
201static void poll_blocksig(void);
202static void poll_unblocksig(void);
203static void sort_timers(void);
204
205
206/*
207 * Private function to block SIGPOLL or SIGIO for a short time.
208 * Don't forget to call poll_unblock before return from the calling function.
209 * Don't change the mask between this calls (your changes will be lost).
210 */
211static void
212poll_blocksig(void)
213{
214	sigset_t set;
215
216	sigemptyset(&set);
217	sigaddset(&set, SIGNAL);
218
219	if(sigprocmask(SIG_BLOCK, &set, &bset))
220		_panic("sigprocmask(SIG_BLOCK): %s", strerror(errno));
221}
222
223/*
224 * unblock the previously blocked signal
225 */
226static void
227poll_unblocksig(void)
228{
229	if(sigprocmask(SIG_SETMASK, &bset, NULL))
230		_panic("sigprocmask(SIG_SETMASK): %s", strerror(errno));
231}
232
233/*
234 * Register the file descriptor fd. If the event corresponding to
235 * mask arrives func is called with arg.
236 * If fd is already registered with that func and arg, only the mask
237 * is changed.
238 * We block the IO-signal, so the dispatch function can be called from
239 * within the signal handler.
240 */
241int
242poll_register(int fd, poll_f func, void *arg, int mask)
243{
244	PollReg_t * p;
245
246	poll_blocksig();
247
248	/* already registered? */
249	for(p = regs; p < &regs[regs_alloc]; p++)
250		if(p->fd == fd && p->func == func && p->arg == arg) {
251			p->mask = mask;
252			break;
253		}
254
255	if(p == &regs[regs_alloc]) {
256		/* no - register */
257
258		/* find a free slot */
259		for(p = regs; p < &regs[regs_alloc]; p++)
260			if(p->fd == -1)
261				break;
262
263		if(p == &regs[regs_alloc]) {
264			size_t newsize = regs_alloc + POLL_REG_GROW;
265			regs = _xrealloc(regs, sizeof(regs[0]) * newsize);
266			for(p = &regs[regs_alloc]; p < &regs[newsize]; p++) {
267				p->fd = -1;
268# ifdef USE_POLL
269				p->pfd = NULL;
270# endif
271			}
272			p = &regs[regs_alloc];
273			regs_alloc = newsize;
274		}
275
276		p->fd = fd;
277		p->arg = arg;
278		p->mask = mask;
279		p->func = func;
280
281		regs_used++;
282		rebuild = 1;
283	}
284
285	poll_unblocksig();
286
287	if(rpoll_trace)
288		fprintf(stderr, "poll_register(%d, %p, %p, %#x)->%tu",
289			fd, (void *)func, (void *)arg, mask, p - regs);
290	return p - regs;
291}
292
293/*
294 * remove registration
295 */
296void
297poll_unregister(int handle)
298{
299	if(rpoll_trace)
300		fprintf(stderr, "poll_unregister(%d)", handle);
301
302	poll_blocksig();
303
304	regs[handle].fd = -1;
305# ifdef USE_POLL
306	regs[handle].pfd = NULL;
307# endif
308	rebuild = 1;
309	regs_used--;
310
311	poll_unblocksig();
312}
313
314/*
315 * Build the structures used by poll() or select()
316 */
317static void
318poll_build(void)
319{
320	PollReg_t * p;
321
322# ifdef USE_POLL
323	struct pollfd * f;
324
325	f = pfd = _xrealloc(pfd, sizeof(pfd[0]) * regs_used);
326
327	for(p = regs; p < &regs[regs_alloc]; p++)
328		if(p->fd >= 0) {
329			f->fd = p->fd;
330			f->events = 0;
331			if(p->mask & RPOLL_IN)
332				f->events |= poll_in;
333			if(p->mask & RPOLL_OUT)
334				f->events |= poll_out;
335			if(p->mask & RPOLL_EXCEPT)
336				f->events |= poll_except;
337			f->revents = 0;
338			p->pfd = f++;
339		}
340	assert(f == &pfd[regs_used]);
341# endif
342
343# ifdef USE_SELECT
344	FD_ZERO(&rset);
345	FD_ZERO(&wset);
346	FD_ZERO(&xset);
347	maxfd = -1;
348	for(p = regs; p < &regs[regs_alloc]; p++)
349		if(p->fd >= 0) {
350			if(p->fd > maxfd)
351				maxfd = p->fd;
352			if(p->mask & RPOLL_IN)
353				FD_SET(p->fd, &rset);
354			if(p->mask & RPOLL_OUT)
355				FD_SET(p->fd, &wset);
356			if(p->mask & RPOLL_EXCEPT)
357				FD_SET(p->fd, &xset);
358		}
359# endif
360}
361
362int
363poll_start_timer(u_int msecs, int repeat, timer_f func, void *arg)
364{
365	return (poll_start_utimer((unsigned long long)msecs * 1000,
366	    repeat, func, arg));
367}
368
369int
370poll_start_utimer(unsigned long long usecs, int repeat, timer_f func, void *arg)
371{
372	PollTim_t *p;
373
374	/* find unused entry */
375	for(p = tims; p < &tims[tims_alloc]; p++)
376		if(p->func == NULL)
377			break;
378
379	if(p == &tims[tims_alloc]) {
380		if(tims_alloc == tims_used) {
381			size_t newsize = tims_alloc + POLL_REG_GROW;
382			tims = _xrealloc(tims, sizeof(tims[0]) * newsize);
383			for(p = &tims[tims_alloc]; p < &tims[newsize]; p++)
384				p->func = NULL;
385			p = &tims[tims_alloc];
386			tims_alloc = newsize;
387		}
388	}
389
390	/* create entry */
391	p->usecs = usecs;
392	p->repeat = repeat;
393	p->arg = arg;
394	p->func = func;
395	p->when = GETUSECS() + usecs;
396
397	tims_used++;
398
399	resort = 1;
400
401	if(rpoll_trace)
402		fprintf(stderr, "poll_start_utimer(%llu, %d, %p, %p)->%tu",
403			usecs, repeat, (void *)func, (void *)arg, p - tims);
404
405	return p - tims;
406}
407
408/*
409 * Here we have to look into the sorted table, whether any entry there points
410 * into the registration table for the deleted entry. This is needed,
411 * because a unregistration can occure while we are scanning through the
412 * table in dispatch(). Do this only, if we are really there - resorting
413 * will sort out things if we are called from outside the loop.
414 */
415void
416poll_stop_timer(int handle)
417{
418	u_int i;
419
420	if(rpoll_trace)
421		fprintf(stderr, "poll_stop_timer(%d)", handle);
422
423	tims[handle].func = NULL;
424	tims_used--;
425
426	resort = 1;
427
428	if(!in_dispatch)
429		return;
430
431	for(i = 0; i < tfd_used; i++)
432		if(tfd[i] == handle) {
433			tfd[i] = -1;
434			break;
435		}
436}
437
438/*
439 * Squeeze and sort timer table.
440 * Should perhaps use a custom sort.
441 */
442static int
443tim_cmp(const void *p1, const void *p2)
444{
445	int t1 = *(const int *)p1;
446	int t2 = *(const int *)p2;
447
448	return tims[t1].when < tims[t2].when ? -1
449	     : tims[t1].when > tims[t2].when ? +1
450	     :                        		  0;
451}
452
453/*
454 * Reconstruct the tfd-array. This will be an sorted array of indexes
455 * to the used entries in tims. The next timer to expire will be infront
456 * of the array. tfd_used is the number of used entries. The array is
457 * re-allocated if needed.
458 */
459static void
460sort_timers(void)
461{
462	int *pp;
463	u_int i;
464
465	if(tims_used > tfd_alloc) {
466		tfd_alloc = tims_used;
467		tfd  = _xrealloc(tfd, sizeof(int *) * tfd_alloc);
468	}
469
470	pp = tfd;
471
472	for(i = 0; i < tims_alloc; i++)
473		if(tims[i].func)
474			*pp++ = i;
475	assert(pp - tfd == (ptrdiff_t)tims_used);
476
477	tfd_used = tims_used;
478	if(tfd_used > 1)
479		qsort(tfd, tfd_used, sizeof(int), tim_cmp);
480}
481
482/*
483 * Poll the file descriptors and dispatch to the right function
484 * If wait is true the poll blocks until somewhat happens.
485 * Don't use a pointer here, because the called function may cause
486 * a reallocation! The check for pfd != NULL is required, because
487 * a sequence of unregister/register could make the wrong callback
488 * to be called. So we clear pfd in unregister and check here.
489 */
490void
491poll_dispatch(int wait)
492{
493	u_int i, idx;
494	int ret;
495	tval_t now;
496	tval_t tout;
497	static u_int last_index;
498
499# ifdef USE_SELECT
500	fd_set nrset, nwset, nxset;
501	struct timeval tv;
502# endif
503
504	in_dispatch = 1;
505
506	if(rebuild) {
507		rebuild = 0;
508		poll_build();
509	}
510	if(resort) {
511		resort = 0;
512		sort_timers();
513	}
514
515	/* in wait mode - compute the timeout */
516	if(wait) {
517		if(tfd_used) {
518			now = GETUSECS();
519# ifdef DEBUG
520			{
521				fprintf(stderr, "now=%llu", now);
522				for(i = 0; i < tims_used; i++)
523					fprintf(stderr, "timers[%2d] = %lld",
524					    i, tfd[i]->when - now);
525			}
526# endif
527			if((tout = tims[tfd[0]].when - now) < 0)
528				tout = 0;
529		} else
530			tout = INFTIM;
531	} else
532		tout = 0;
533
534# ifdef DEBUG
535	fprintf(stderr, "rpoll -- selecting with tout=%u", tout);
536# endif
537
538# ifdef USE_POLL
539	ret = poll(pfd, regs_used, tout == INFTIM ? INFTIM : (tout / 1000));
540# endif
541
542# ifdef USE_SELECT
543	nrset = rset;
544	nwset = wset;
545	nxset = xset;
546	if(tout != INFTIM) {
547		tv.tv_sec = tout / 1000000;
548		tv.tv_usec = tout % 1000000;
549	}
550	ret = select(maxfd+1,
551		SELECT_CAST(&nrset),
552		SELECT_CAST(&nwset),
553		SELECT_CAST(&nxset), (tout==INFTIM) ? NULL : &tv);
554# endif
555
556	if(ret == -1) {
557		if(errno == EINTR)
558			return;
559		_panic("poll/select: %s", strerror(errno));
560	}
561
562	/* dispatch files */
563	if(ret > 0) {
564		for(i = 0; i < regs_alloc; i++) {
565			idx = rpoll_policy ? ((last_index+i) % regs_alloc) : i;
566
567			assert(idx < regs_alloc);
568
569			if(regs[idx].fd >= 0) {
570				int mask = 0;
571
572# ifdef USE_POLL
573				if(regs[idx].pfd) {
574					if ((regs[idx].mask & RPOLL_IN) &&
575					    (regs[idx].pfd->revents & poll_in))
576						mask |= RPOLL_IN;
577					if ((regs[idx].mask & RPOLL_OUT) &&
578					    (regs[idx].pfd->revents & poll_out))
579						mask |= RPOLL_OUT;
580					if((regs[idx].mask & RPOLL_EXCEPT) &&
581					    (regs[idx].pfd->revents & poll_except))
582						mask |= RPOLL_EXCEPT;
583				}
584# endif
585# ifdef USE_SELECT
586				if ((regs[idx].mask & RPOLL_IN) &&
587				    FD_ISSET(regs[idx].fd, &nrset))
588					mask |= RPOLL_IN;
589				if ((regs[idx].mask & RPOLL_OUT) &&
590				    FD_ISSET(regs[idx].fd, &nwset))
591					mask |= RPOLL_OUT;
592				if ((regs[idx].mask & RPOLL_EXCEPT) &&
593				    FD_ISSET(regs[idx].fd, &nxset))
594					mask |= RPOLL_EXCEPT;
595# endif
596				assert(idx < regs_alloc);
597
598				if(mask) {
599					if(rpoll_trace)
600						fprintf(stderr, "poll_dispatch() -- "
601						    "file %d/%d %x",
602						    regs[idx].fd, idx, mask);
603					(*regs[idx].func)(regs[idx].fd, mask, regs[idx].arg);
604				}
605			}
606
607		}
608		last_index++;
609	}
610
611	/* dispatch timeouts */
612	if(tfd_used) {
613		now = GETUSECS();
614		for(i = 0; i < tfd_used; i++) {
615			if(tfd[i] < 0)
616				continue;
617			if(tims[tfd[i]].when > now)
618				break;
619			if(rpoll_trace)
620				fprintf(stderr, "rpoll_dispatch() -- timeout %d",tfd[i]);
621			(*tims[tfd[i]].func)(tfd[i], tims[tfd[i]].arg);
622			if(tfd[i] < 0)
623				continue;
624			if(tims[tfd[i]].repeat)
625				tims[tfd[i]].when = now + tims[tfd[i]].usecs;
626			else {
627				tims[tfd[i]].func = NULL;
628				tims_used--;
629				tfd[i] = -1;
630			}
631			resort = 1;
632		}
633	}
634	in_dispatch = 0;
635}
636
637
638# ifdef TESTME
639struct timeval start, now;
640int t0, t1;
641
642double elaps(void);
643void infunc(int fd, int mask, void *arg);
644
645double
646elaps(void)
647{
648	gettimeofday(&now, NULL);
649
650	return (double)(10 * now.tv_sec + now.tv_usec / 100000 -
651	    10 * start.tv_sec - start.tv_usec / 100000) / 10;
652}
653
654void
655infunc(int fd, int mask, void *arg)
656{
657	char buf[1024];
658	int ret;
659
660	mask = mask;
661	arg = arg;
662	if((ret = read(fd, buf, sizeof(buf))) < 0)
663		_panic("read: %s", strerror(errno));
664	write(1, "stdin:", 6);
665	write(1, buf, ret);
666}
667
668void tfunc0(int tid, void *arg);
669void tfunc1(int tid, void *arg);
670
671void
672tfunc0(int tid, void *arg)
673{
674	printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg);
675}
676void
677tfunc1(int tid, void *arg)
678{
679	printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg);
680}
681void
682tfunc2(int tid, void *arg)
683{
684	static u_int count = 0;
685
686	if (++count % 10000 == 0)
687		printf("%4.1f -- %d\n", elaps(), tid);
688}
689
690void first(int tid, void *arg);
691void second(int tid, void *arg);
692
693void
694second(int tid, void *arg)
695{
696	printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg);
697	poll_start_utimer(5500000, 0, first, "first");
698	poll_stop_timer(t1);
699	t0 = poll_start_timer(1000, 1, tfunc0, "1 second");
700}
701void
702first(int tid, void *arg)
703{
704	printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg);
705	poll_start_timer(3700, 0, second, "second");
706	poll_stop_timer(t0);
707	t1 = poll_start_timer(250, 1, tfunc1, "1/4 second");
708}
709
710int
711main(int argc, char *argv[])
712{
713	argv = argv;
714	gettimeofday(&start, NULL);
715	poll_register(0, infunc, NULL, RPOLL_IN);
716
717	if (argc < 2) {
718		t0 = poll_start_timer(1000, 1, tfunc0, "1 second");
719		poll_start_timer(2500, 0, first, "first");
720	} else {
721		t0 = poll_start_utimer(300, 1, tfunc2, NULL);
722	}
723
724	while(1)
725		poll_dispatch(1);
726
727	return 0;
728}
729# endif
730