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