xref: /freebsd/contrib/libbegemot/rpoll.c (revision 8e9b3e707151d136ec95e7f1d37556e39c1e228c)
1911a190fSTom Rhodes /*
2911a190fSTom Rhodes  * Copyright (c)1996-2002 by Hartmut Brandt
3911a190fSTom Rhodes  *	All rights reserved.
4911a190fSTom Rhodes  *
5911a190fSTom Rhodes  * Author: Hartmut Brandt
6911a190fSTom Rhodes  *
7911a190fSTom Rhodes  * Redistribution of this software and documentation and use in source and
8911a190fSTom Rhodes  * binary forms, with or without modification, are permitted provided that
9911a190fSTom Rhodes  * the following conditions are met:
10911a190fSTom Rhodes  *
11911a190fSTom Rhodes  * 1. Redistributions of source code or documentation must retain the above
12911a190fSTom Rhodes  *   copyright notice, this list of conditions and the following disclaimer.
13911a190fSTom Rhodes  * 2. Redistributions in binary form must reproduce the above copyright
14911a190fSTom Rhodes  *   notice, this list of conditions and the following disclaimer in the
15911a190fSTom Rhodes  *   documentation and/or other materials provided with the distribution.
16911a190fSTom Rhodes  *
17911a190fSTom Rhodes  * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE AUTHOR
18911a190fSTom Rhodes  * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
19911a190fSTom Rhodes  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20911a190fSTom Rhodes  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
21911a190fSTom Rhodes  * THE AUTHOR OR ITS CONTRIBUTORS  BE LIABLE FOR ANY DIRECT, INDIRECT,
22911a190fSTom Rhodes  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23911a190fSTom Rhodes  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
24911a190fSTom Rhodes  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25911a190fSTom Rhodes  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26911a190fSTom Rhodes  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27911a190fSTom Rhodes  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28911a190fSTom Rhodes  */
29911a190fSTom Rhodes /*
30911a190fSTom Rhodes  * These functions try to hide the poll/select/setitimer interface from the
31911a190fSTom Rhodes  * user. You associate callback functions with file descriptors and timers.
32911a190fSTom Rhodes  *
33911a190fSTom Rhodes  * $Begemot: libbegemot/rpoll.c,v 1.14 2004/09/21 15:59:00 brandt Exp $
34911a190fSTom Rhodes  */
35911a190fSTom Rhodes # include <stdio.h>
36911a190fSTom Rhodes # include <stdlib.h>
37911a190fSTom Rhodes # include <stddef.h>
38911a190fSTom Rhodes # include <stdarg.h>
39911a190fSTom Rhodes # include <signal.h>
40911a190fSTom Rhodes # include <string.h>
41911a190fSTom Rhodes # include <errno.h>
42911a190fSTom Rhodes # include <time.h>
43911a190fSTom Rhodes # include <assert.h>
44911a190fSTom Rhodes # include <unistd.h>
45911a190fSTom Rhodes # include <sys/time.h>
46911a190fSTom Rhodes 
47911a190fSTom Rhodes # include "rpoll.h"
48911a190fSTom Rhodes 
49911a190fSTom Rhodes /*
50911a190fSTom Rhodes # define DEBUG
51911a190fSTom Rhodes */
52911a190fSTom Rhodes 
53911a190fSTom Rhodes # ifdef USE_POLL
54911a190fSTom Rhodes #  ifdef NEED_POLL_XOPEN_TWIDDLE
55911a190fSTom Rhodes #   define __USE_XOPEN
56911a190fSTom Rhodes #  endif
57911a190fSTom Rhodes #  include <poll.h>
58911a190fSTom Rhodes #  ifdef NEED_POLL_XOPEN_TWIDDLE
59911a190fSTom Rhodes #   undef __USE_XOPEN
60911a190fSTom Rhodes #  endif
61911a190fSTom Rhodes #  include <stropts.h>
62911a190fSTom Rhodes # endif
63911a190fSTom Rhodes 
64911a190fSTom Rhodes /*
65911a190fSTom Rhodes  * the second define is for Linux, which sometimes fails to
66911a190fSTom Rhodes  * declare INFTIM.
67911a190fSTom Rhodes  */
68911a190fSTom Rhodes # if defined(USE_SELECT) || !defined(INFTIM)
69911a190fSTom Rhodes #  define INFTIM (-1)
70911a190fSTom Rhodes # endif
71911a190fSTom Rhodes 
72911a190fSTom Rhodes # if defined(SIGPOLL)
73911a190fSTom Rhodes #  define SIGNAL	SIGPOLL
74911a190fSTom Rhodes # else
75911a190fSTom Rhodes #  if defined(SIGIO)
76911a190fSTom Rhodes #   define SIGNAL	SIGIO
77911a190fSTom Rhodes #  endif
78911a190fSTom Rhodes # endif
79911a190fSTom Rhodes 
80911a190fSTom Rhodes # ifdef USE_POLL
81911a190fSTom Rhodes #  define poll_in	(POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)
82911a190fSTom Rhodes #  define poll_out	(POLLOUT | POLLWRNORM | POLLWRBAND)
83911a190fSTom Rhodes #  define poll_except	(POLLERR | POLLHUP)
84911a190fSTom Rhodes # endif
85911a190fSTom Rhodes 
86911a190fSTom Rhodes # ifdef BROKEN_SELECT_PROTO
87911a190fSTom Rhodes #  define SELECT_CAST(P)	(int *)P
88911a190fSTom Rhodes # else
89911a190fSTom Rhodes #  define SELECT_CAST(P)	P
90911a190fSTom Rhodes # endif
91911a190fSTom Rhodes 
92911a190fSTom Rhodes 
93ca77a23fSHartmut Brandt typedef int64_t tval_t;
94911a190fSTom Rhodes 
95ca77a23fSHartmut Brandt static inline tval_t GETUSECS(void);
96911a190fSTom Rhodes 
97911a190fSTom Rhodes static inline tval_t
GETUSECS(void)98ca77a23fSHartmut Brandt GETUSECS(void) {
99911a190fSTom Rhodes 	struct timeval tval;
100911a190fSTom Rhodes 
101911a190fSTom Rhodes 	(void)gettimeofday(&tval, NULL);
102ca77a23fSHartmut Brandt 	return (tval_t)tval.tv_sec * 1000000 + tval.tv_usec;
103911a190fSTom Rhodes }
104911a190fSTom Rhodes 
105911a190fSTom Rhodes /*
106911a190fSTom Rhodes  * Simple fatal exit.
107911a190fSTom Rhodes  */
108911a190fSTom Rhodes static void
_panic(const char * fmt,...)109911a190fSTom Rhodes _panic(const char *fmt, ...)
110911a190fSTom Rhodes {
111911a190fSTom Rhodes 	va_list ap;
112911a190fSTom Rhodes 
113911a190fSTom Rhodes 	va_start(ap, fmt);
114911a190fSTom Rhodes 	fprintf(stderr, "panic: ");
115911a190fSTom Rhodes 	vfprintf(stderr, fmt, ap);
116911a190fSTom Rhodes 	fprintf(stderr, "\n");
117911a190fSTom Rhodes 	va_end(ap);
118911a190fSTom Rhodes 
119911a190fSTom Rhodes 	exit(1);
120911a190fSTom Rhodes }
121911a190fSTom Rhodes 
122911a190fSTom Rhodes static void *
_xrealloc(void * p,size_t s)123911a190fSTom Rhodes _xrealloc(void *p, size_t s)
124911a190fSTom Rhodes {
125911a190fSTom Rhodes 	void *ptr;
126911a190fSTom Rhodes 
127911a190fSTom Rhodes 	if(p == NULL) {
128911a190fSTom Rhodes 		if((ptr=malloc(s)) == NULL && (s!=0 || (ptr=malloc(1)) == NULL))
129911a190fSTom Rhodes 			_panic("out of memory: xrealloc(%lx, %lu)",
130911a190fSTom Rhodes 				(unsigned long)p, (unsigned long)s);
131911a190fSTom Rhodes 	} else if(s == 0) {
132911a190fSTom Rhodes 		free(p);
133911a190fSTom Rhodes 		if((ptr=malloc(s)) == NULL && (ptr=malloc(1)) == NULL)
134911a190fSTom Rhodes 			_panic("out of memory: xrealloc(%lx, %lu)",
135911a190fSTom Rhodes 				(unsigned long)p, (unsigned long)s);
136911a190fSTom Rhodes 	} else {
137911a190fSTom Rhodes 		if((ptr = realloc(p, s)) == NULL)
138911a190fSTom Rhodes 			_panic("out of memory: xrealloc(%lx, %lu)",
139911a190fSTom Rhodes 				(unsigned long)p, (unsigned long)s);
140911a190fSTom Rhodes 	}
141911a190fSTom Rhodes 
142911a190fSTom Rhodes 	return ptr;
143911a190fSTom Rhodes }
144911a190fSTom Rhodes 
145911a190fSTom Rhodes /*
146911a190fSTom Rhodes  * This structure holds one registration record for files
147911a190fSTom Rhodes  */
148911a190fSTom Rhodes typedef struct {
149911a190fSTom Rhodes 	int	fd;		/* file descriptor (-1 if struct unused) */
150911a190fSTom Rhodes 	int	mask;		/* event flags */
151911a190fSTom Rhodes 	void *	arg;		/* client arg */
152911a190fSTom Rhodes 	poll_f	func;		/* handler */
153911a190fSTom Rhodes # ifdef USE_POLL
154911a190fSTom Rhodes 	struct pollfd *pfd;	/* pointer to corresponding poll() structure */
155911a190fSTom Rhodes # endif
156911a190fSTom Rhodes } PollReg_t;
157911a190fSTom Rhodes 
158911a190fSTom Rhodes /*
159911a190fSTom Rhodes  * Now for timers
160911a190fSTom Rhodes  */
161911a190fSTom Rhodes typedef struct {
162ca77a23fSHartmut Brandt 	uint64_t usecs;		/* microsecond value of the timer */
163911a190fSTom Rhodes 	int	repeat;		/* one shot or repeat? */
164911a190fSTom Rhodes 	void	*arg;		/* client arg */
165911a190fSTom Rhodes 	timer_f	func;		/* handler, 0 means disfunct */
166ca77a23fSHartmut Brandt 	tval_t	when;		/* next time to trigger in usecs! */
167911a190fSTom Rhodes } PollTim_t;
168911a190fSTom Rhodes 
169911a190fSTom Rhodes /* how many records should our table grow at once? */
170911a190fSTom Rhodes # define POLL_REG_GROW	100
171911a190fSTom Rhodes 
172911a190fSTom Rhodes # ifdef USE_POLL
173911a190fSTom Rhodes static struct pollfd *	pfd;		/* fd list for poll() */
174911a190fSTom Rhodes # endif
175911a190fSTom Rhodes 
176911a190fSTom Rhodes # ifdef USE_SELECT
177911a190fSTom Rhodes static fd_set rset, wset, xset;		/* file descriptor sets for select() */
178911a190fSTom Rhodes static int maxfd;			/* maximum fd number */
179911a190fSTom Rhodes # endif
180911a190fSTom Rhodes 
181911a190fSTom Rhodes static int		in_dispatch;
182911a190fSTom Rhodes 
183911a190fSTom Rhodes static PollReg_t *	regs;		/* registration records */
184911a190fSTom Rhodes static u_int		regs_alloc;	/* how many are allocated */
185911a190fSTom Rhodes static u_int		regs_used;	/* upper used limit */
186911a190fSTom Rhodes static sigset_t		bset;		/* blocked signals */
187911a190fSTom Rhodes static int 		rebuild;	/* rebuild table on next dispatch() */
188911a190fSTom Rhodes 
189911a190fSTom Rhodes static int *		tfd;		/* sorted entries */
190911a190fSTom Rhodes static u_int		tfd_alloc;	/* number of entries allocated */
191911a190fSTom Rhodes static u_int		tfd_used;	/* number of entries used */
192911a190fSTom Rhodes static PollTim_t *	tims;		/* timer registration records */
193911a190fSTom Rhodes static u_int		tims_alloc;	/* how many are allocated */
194911a190fSTom Rhodes static u_int		tims_used;	/* how many are used */
195911a190fSTom Rhodes static int		resort;		/* resort on next dispatch */
196911a190fSTom Rhodes 
197911a190fSTom Rhodes int	rpoll_trace;
198911a190fSTom Rhodes int	rpoll_policy;	/* if 0 start sched callbacks from 0 else try round robin */
199911a190fSTom Rhodes 
200911a190fSTom Rhodes static void poll_build(void);
201911a190fSTom Rhodes static void poll_blocksig(void);
202911a190fSTom Rhodes static void poll_unblocksig(void);
203911a190fSTom Rhodes static void sort_timers(void);
204911a190fSTom Rhodes 
205911a190fSTom Rhodes 
206911a190fSTom Rhodes /*
207911a190fSTom Rhodes  * Private function to block SIGPOLL or SIGIO for a short time.
208911a190fSTom Rhodes  * Don't forget to call poll_unblock before return from the calling function.
209911a190fSTom Rhodes  * Don't change the mask between this calls (your changes will be lost).
210911a190fSTom Rhodes  */
211911a190fSTom Rhodes static void
poll_blocksig(void)212911a190fSTom Rhodes poll_blocksig(void)
213911a190fSTom Rhodes {
214911a190fSTom Rhodes 	sigset_t set;
215911a190fSTom Rhodes 
216911a190fSTom Rhodes 	sigemptyset(&set);
217911a190fSTom Rhodes 	sigaddset(&set, SIGNAL);
218911a190fSTom Rhodes 
219911a190fSTom Rhodes 	if(sigprocmask(SIG_BLOCK, &set, &bset))
220911a190fSTom Rhodes 		_panic("sigprocmask(SIG_BLOCK): %s", strerror(errno));
221911a190fSTom Rhodes }
222911a190fSTom Rhodes 
223911a190fSTom Rhodes /*
224911a190fSTom Rhodes  * unblock the previously blocked signal
225911a190fSTom Rhodes  */
226911a190fSTom Rhodes static void
poll_unblocksig(void)227911a190fSTom Rhodes poll_unblocksig(void)
228911a190fSTom Rhodes {
229911a190fSTom Rhodes 	if(sigprocmask(SIG_SETMASK, &bset, NULL))
230911a190fSTom Rhodes 		_panic("sigprocmask(SIG_SETMASK): %s", strerror(errno));
231911a190fSTom Rhodes }
232911a190fSTom Rhodes 
233911a190fSTom Rhodes /*
234911a190fSTom Rhodes  * Register the file descriptor fd. If the event corresponding to
235911a190fSTom Rhodes  * mask arrives func is called with arg.
236911a190fSTom Rhodes  * If fd is already registered with that func and arg, only the mask
237911a190fSTom Rhodes  * is changed.
238911a190fSTom Rhodes  * We block the IO-signal, so the dispatch function can be called from
239911a190fSTom Rhodes  * within the signal handler.
240911a190fSTom Rhodes  */
241911a190fSTom Rhodes int
poll_register(int fd,poll_f func,void * arg,int mask)242911a190fSTom Rhodes poll_register(int fd, poll_f func, void *arg, int mask)
243911a190fSTom Rhodes {
244911a190fSTom Rhodes 	PollReg_t * p;
245911a190fSTom Rhodes 
246911a190fSTom Rhodes 	poll_blocksig();
247911a190fSTom Rhodes 
248911a190fSTom Rhodes 	/* already registered? */
249911a190fSTom Rhodes 	for(p = regs; p < &regs[regs_alloc]; p++)
250911a190fSTom Rhodes 		if(p->fd == fd && p->func == func && p->arg == arg) {
251911a190fSTom Rhodes 			p->mask = mask;
252911a190fSTom Rhodes 			break;
253911a190fSTom Rhodes 		}
254911a190fSTom Rhodes 
255911a190fSTom Rhodes 	if(p == &regs[regs_alloc]) {
256911a190fSTom Rhodes 		/* no - register */
257911a190fSTom Rhodes 
258911a190fSTom Rhodes 		/* find a free slot */
259911a190fSTom Rhodes 		for(p = regs; p < &regs[regs_alloc]; p++)
260911a190fSTom Rhodes 			if(p->fd == -1)
261911a190fSTom Rhodes 				break;
262911a190fSTom Rhodes 
263911a190fSTom Rhodes 		if(p == &regs[regs_alloc]) {
264911a190fSTom Rhodes 			size_t newsize = regs_alloc + POLL_REG_GROW;
265911a190fSTom Rhodes 			regs = _xrealloc(regs, sizeof(regs[0]) * newsize);
266911a190fSTom Rhodes 			for(p = &regs[regs_alloc]; p < &regs[newsize]; p++) {
267911a190fSTom Rhodes 				p->fd = -1;
268911a190fSTom Rhodes # ifdef USE_POLL
269911a190fSTom Rhodes 				p->pfd = NULL;
270911a190fSTom Rhodes # endif
271911a190fSTom Rhodes 			}
272911a190fSTom Rhodes 			p = &regs[regs_alloc];
273911a190fSTom Rhodes 			regs_alloc = newsize;
274911a190fSTom Rhodes 		}
275911a190fSTom Rhodes 
276911a190fSTom Rhodes 		p->fd = fd;
277911a190fSTom Rhodes 		p->arg = arg;
278911a190fSTom Rhodes 		p->mask = mask;
279911a190fSTom Rhodes 		p->func = func;
280911a190fSTom Rhodes 
281911a190fSTom Rhodes 		regs_used++;
282911a190fSTom Rhodes 		rebuild = 1;
283911a190fSTom Rhodes 	}
284911a190fSTom Rhodes 
285911a190fSTom Rhodes 	poll_unblocksig();
286911a190fSTom Rhodes 
287911a190fSTom Rhodes 	if(rpoll_trace)
288ca77a23fSHartmut Brandt 		fprintf(stderr, "poll_register(%d, %p, %p, %#x)->%tu",
289ca77a23fSHartmut Brandt 			fd, (void *)func, (void *)arg, mask, p - regs);
290911a190fSTom Rhodes 	return p - regs;
291911a190fSTom Rhodes }
292911a190fSTom Rhodes 
293911a190fSTom Rhodes /*
294911a190fSTom Rhodes  * remove registration
295911a190fSTom Rhodes  */
296911a190fSTom Rhodes void
poll_unregister(int handle)297911a190fSTom Rhodes poll_unregister(int handle)
298911a190fSTom Rhodes {
299911a190fSTom Rhodes 	if(rpoll_trace)
300911a190fSTom Rhodes 		fprintf(stderr, "poll_unregister(%d)", handle);
301911a190fSTom Rhodes 
302911a190fSTom Rhodes 	poll_blocksig();
303911a190fSTom Rhodes 
304911a190fSTom Rhodes 	regs[handle].fd = -1;
305911a190fSTom Rhodes # ifdef USE_POLL
306911a190fSTom Rhodes 	regs[handle].pfd = NULL;
307911a190fSTom Rhodes # endif
308911a190fSTom Rhodes 	rebuild = 1;
309911a190fSTom Rhodes 	regs_used--;
310911a190fSTom Rhodes 
311911a190fSTom Rhodes 	poll_unblocksig();
312911a190fSTom Rhodes }
313911a190fSTom Rhodes 
314911a190fSTom Rhodes /*
315911a190fSTom Rhodes  * Build the structures used by poll() or select()
316911a190fSTom Rhodes  */
317911a190fSTom Rhodes static void
poll_build(void)318911a190fSTom Rhodes poll_build(void)
319911a190fSTom Rhodes {
320911a190fSTom Rhodes 	PollReg_t * p;
321911a190fSTom Rhodes 
322911a190fSTom Rhodes # ifdef USE_POLL
323911a190fSTom Rhodes 	struct pollfd * f;
324911a190fSTom Rhodes 
325911a190fSTom Rhodes 	f = pfd = _xrealloc(pfd, sizeof(pfd[0]) * regs_used);
326911a190fSTom Rhodes 
327911a190fSTom Rhodes 	for(p = regs; p < &regs[regs_alloc]; p++)
328911a190fSTom Rhodes 		if(p->fd >= 0) {
329911a190fSTom Rhodes 			f->fd = p->fd;
330911a190fSTom Rhodes 			f->events = 0;
331*8e9b3e70SHartmut Brandt 			if(p->mask & RPOLL_IN)
332911a190fSTom Rhodes 				f->events |= poll_in;
333*8e9b3e70SHartmut Brandt 			if(p->mask & RPOLL_OUT)
334911a190fSTom Rhodes 				f->events |= poll_out;
335*8e9b3e70SHartmut Brandt 			if(p->mask & RPOLL_EXCEPT)
336911a190fSTom Rhodes 				f->events |= poll_except;
337911a190fSTom Rhodes 			f->revents = 0;
338911a190fSTom Rhodes 			p->pfd = f++;
339911a190fSTom Rhodes 		}
340911a190fSTom Rhodes 	assert(f == &pfd[regs_used]);
341911a190fSTom Rhodes # endif
342911a190fSTom Rhodes 
343911a190fSTom Rhodes # ifdef USE_SELECT
344911a190fSTom Rhodes 	FD_ZERO(&rset);
345911a190fSTom Rhodes 	FD_ZERO(&wset);
346911a190fSTom Rhodes 	FD_ZERO(&xset);
347911a190fSTom Rhodes 	maxfd = -1;
348911a190fSTom Rhodes 	for(p = regs; p < &regs[regs_alloc]; p++)
349911a190fSTom Rhodes 		if(p->fd >= 0) {
350911a190fSTom Rhodes 			if(p->fd > maxfd)
351911a190fSTom Rhodes 				maxfd = p->fd;
352*8e9b3e70SHartmut Brandt 			if(p->mask & RPOLL_IN)
353911a190fSTom Rhodes 				FD_SET(p->fd, &rset);
354*8e9b3e70SHartmut Brandt 			if(p->mask & RPOLL_OUT)
355911a190fSTom Rhodes 				FD_SET(p->fd, &wset);
356*8e9b3e70SHartmut Brandt 			if(p->mask & RPOLL_EXCEPT)
357911a190fSTom Rhodes 				FD_SET(p->fd, &xset);
358911a190fSTom Rhodes 		}
359911a190fSTom Rhodes # endif
360911a190fSTom Rhodes }
361911a190fSTom Rhodes 
362911a190fSTom Rhodes int
poll_start_timer(u_int msecs,int repeat,timer_f func,void * arg)363911a190fSTom Rhodes poll_start_timer(u_int msecs, int repeat, timer_f func, void *arg)
364911a190fSTom Rhodes {
365ca77a23fSHartmut Brandt 	return (poll_start_utimer((unsigned long long)msecs * 1000,
366ca77a23fSHartmut Brandt 	    repeat, func, arg));
367ca77a23fSHartmut Brandt }
368ca77a23fSHartmut Brandt 
369ca77a23fSHartmut Brandt int
poll_start_utimer(unsigned long long usecs,int repeat,timer_f func,void * arg)370ca77a23fSHartmut Brandt poll_start_utimer(unsigned long long usecs, int repeat, timer_f func, void *arg)
371ca77a23fSHartmut Brandt {
372911a190fSTom Rhodes 	PollTim_t *p;
373911a190fSTom Rhodes 
374911a190fSTom Rhodes 	/* find unused entry */
375911a190fSTom Rhodes 	for(p = tims; p < &tims[tims_alloc]; p++)
376911a190fSTom Rhodes 		if(p->func == NULL)
377911a190fSTom Rhodes 			break;
378911a190fSTom Rhodes 
379911a190fSTom Rhodes 	if(p == &tims[tims_alloc]) {
380911a190fSTom Rhodes 		if(tims_alloc == tims_used) {
381911a190fSTom Rhodes 			size_t newsize = tims_alloc + POLL_REG_GROW;
382911a190fSTom Rhodes 			tims = _xrealloc(tims, sizeof(tims[0]) * newsize);
383911a190fSTom Rhodes 			for(p = &tims[tims_alloc]; p < &tims[newsize]; p++)
384911a190fSTom Rhodes 				p->func = NULL;
385911a190fSTom Rhodes 			p = &tims[tims_alloc];
386911a190fSTom Rhodes 			tims_alloc = newsize;
387911a190fSTom Rhodes 		}
388911a190fSTom Rhodes 	}
389911a190fSTom Rhodes 
390911a190fSTom Rhodes 	/* create entry */
391ca77a23fSHartmut Brandt 	p->usecs = usecs;
392911a190fSTom Rhodes 	p->repeat = repeat;
393911a190fSTom Rhodes 	p->arg = arg;
394911a190fSTom Rhodes 	p->func = func;
395ca77a23fSHartmut Brandt 	p->when = GETUSECS() + usecs;
396911a190fSTom Rhodes 
397911a190fSTom Rhodes 	tims_used++;
398911a190fSTom Rhodes 
399911a190fSTom Rhodes 	resort = 1;
400911a190fSTom Rhodes 
401911a190fSTom Rhodes 	if(rpoll_trace)
402ca77a23fSHartmut Brandt 		fprintf(stderr, "poll_start_utimer(%llu, %d, %p, %p)->%tu",
403ca77a23fSHartmut Brandt 			usecs, repeat, (void *)func, (void *)arg, p - tims);
404911a190fSTom Rhodes 
405911a190fSTom Rhodes 	return p - tims;
406911a190fSTom Rhodes }
407911a190fSTom Rhodes 
408911a190fSTom Rhodes /*
409911a190fSTom Rhodes  * Here we have to look into the sorted table, whether any entry there points
410911a190fSTom Rhodes  * into the registration table for the deleted entry. This is needed,
411911a190fSTom Rhodes  * because a unregistration can occure while we are scanning through the
412911a190fSTom Rhodes  * table in dispatch(). Do this only, if we are really there - resorting
413911a190fSTom Rhodes  * will sort out things if we are called from outside the loop.
414911a190fSTom Rhodes  */
415911a190fSTom Rhodes void
poll_stop_timer(int handle)416911a190fSTom Rhodes poll_stop_timer(int handle)
417911a190fSTom Rhodes {
418911a190fSTom Rhodes 	u_int i;
419911a190fSTom Rhodes 
420911a190fSTom Rhodes 	if(rpoll_trace)
421911a190fSTom Rhodes 		fprintf(stderr, "poll_stop_timer(%d)", handle);
422911a190fSTom Rhodes 
423911a190fSTom Rhodes 	tims[handle].func = NULL;
424911a190fSTom Rhodes 	tims_used--;
425911a190fSTom Rhodes 
426911a190fSTom Rhodes 	resort = 1;
427911a190fSTom Rhodes 
428911a190fSTom Rhodes 	if(!in_dispatch)
429911a190fSTom Rhodes 		return;
430911a190fSTom Rhodes 
431911a190fSTom Rhodes 	for(i = 0; i < tfd_used; i++)
432911a190fSTom Rhodes 		if(tfd[i] == handle) {
433911a190fSTom Rhodes 			tfd[i] = -1;
434911a190fSTom Rhodes 			break;
435911a190fSTom Rhodes 		}
436911a190fSTom Rhodes }
437911a190fSTom Rhodes 
438911a190fSTom Rhodes /*
439911a190fSTom Rhodes  * Squeeze and sort timer table.
440911a190fSTom Rhodes  * Should perhaps use a custom sort.
441911a190fSTom Rhodes  */
442911a190fSTom Rhodes static int
tim_cmp(const void * p1,const void * p2)443911a190fSTom Rhodes tim_cmp(const void *p1, const void *p2)
444911a190fSTom Rhodes {
445911a190fSTom Rhodes 	int t1 = *(const int *)p1;
446911a190fSTom Rhodes 	int t2 = *(const int *)p2;
447911a190fSTom Rhodes 
448911a190fSTom Rhodes 	return tims[t1].when < tims[t2].when ? -1
449911a190fSTom Rhodes 	     : tims[t1].when > tims[t2].when ? +1
450911a190fSTom Rhodes 	     :                        		  0;
451911a190fSTom Rhodes }
452911a190fSTom Rhodes 
453911a190fSTom Rhodes /*
454911a190fSTom Rhodes  * Reconstruct the tfd-array. This will be an sorted array of indexes
455911a190fSTom Rhodes  * to the used entries in tims. The next timer to expire will be infront
456911a190fSTom Rhodes  * of the array. tfd_used is the number of used entries. The array is
457911a190fSTom Rhodes  * re-allocated if needed.
458911a190fSTom Rhodes  */
459911a190fSTom Rhodes static void
sort_timers(void)460911a190fSTom Rhodes sort_timers(void)
461911a190fSTom Rhodes {
462911a190fSTom Rhodes 	int *pp;
463911a190fSTom Rhodes 	u_int i;
464911a190fSTom Rhodes 
465911a190fSTom Rhodes 	if(tims_used > tfd_alloc) {
466911a190fSTom Rhodes 		tfd_alloc = tims_used;
467911a190fSTom Rhodes 		tfd  = _xrealloc(tfd, sizeof(int *) * tfd_alloc);
468911a190fSTom Rhodes 	}
469911a190fSTom Rhodes 
470911a190fSTom Rhodes 	pp = tfd;
471911a190fSTom Rhodes 
472911a190fSTom Rhodes 	for(i = 0; i < tims_alloc; i++)
473911a190fSTom Rhodes 		if(tims[i].func)
474911a190fSTom Rhodes 			*pp++ = i;
475911a190fSTom Rhodes 	assert(pp - tfd == (ptrdiff_t)tims_used);
476911a190fSTom Rhodes 
477911a190fSTom Rhodes 	tfd_used = tims_used;
478911a190fSTom Rhodes 	if(tfd_used > 1)
479911a190fSTom Rhodes 		qsort(tfd, tfd_used, sizeof(int), tim_cmp);
480911a190fSTom Rhodes }
481911a190fSTom Rhodes 
482911a190fSTom Rhodes /*
483911a190fSTom Rhodes  * Poll the file descriptors and dispatch to the right function
484911a190fSTom Rhodes  * If wait is true the poll blocks until somewhat happens.
485911a190fSTom Rhodes  * Don't use a pointer here, because the called function may cause
486911a190fSTom Rhodes  * a reallocation! The check for pfd != NULL is required, because
487911a190fSTom Rhodes  * a sequence of unregister/register could make the wrong callback
488911a190fSTom Rhodes  * to be called. So we clear pfd in unregister and check here.
489911a190fSTom Rhodes  */
490911a190fSTom Rhodes void
poll_dispatch(int wait)491911a190fSTom Rhodes poll_dispatch(int wait)
492911a190fSTom Rhodes {
493911a190fSTom Rhodes 	u_int i, idx;
494911a190fSTom Rhodes 	int ret;
495911a190fSTom Rhodes 	tval_t now;
496ca77a23fSHartmut Brandt 	tval_t tout;
497911a190fSTom Rhodes 	static u_int last_index;
498911a190fSTom Rhodes 
499911a190fSTom Rhodes # ifdef USE_SELECT
500911a190fSTom Rhodes 	fd_set nrset, nwset, nxset;
501911a190fSTom Rhodes 	struct timeval tv;
502911a190fSTom Rhodes # endif
503911a190fSTom Rhodes 
504911a190fSTom Rhodes 	in_dispatch = 1;
505911a190fSTom Rhodes 
506911a190fSTom Rhodes 	if(rebuild) {
507911a190fSTom Rhodes 		rebuild = 0;
508911a190fSTom Rhodes 		poll_build();
509911a190fSTom Rhodes 	}
510911a190fSTom Rhodes 	if(resort) {
511911a190fSTom Rhodes 		resort = 0;
512911a190fSTom Rhodes 		sort_timers();
513911a190fSTom Rhodes 	}
514911a190fSTom Rhodes 
515911a190fSTom Rhodes 	/* in wait mode - compute the timeout */
516911a190fSTom Rhodes 	if(wait) {
517911a190fSTom Rhodes 		if(tfd_used) {
518ca77a23fSHartmut Brandt 			now = GETUSECS();
519911a190fSTom Rhodes # ifdef DEBUG
520911a190fSTom Rhodes 			{
521ca77a23fSHartmut Brandt 				fprintf(stderr, "now=%llu", now);
522911a190fSTom Rhodes 				for(i = 0; i < tims_used; i++)
523ca77a23fSHartmut Brandt 					fprintf(stderr, "timers[%2d] = %lld",
524ca77a23fSHartmut Brandt 					    i, tfd[i]->when - now);
525911a190fSTom Rhodes 			}
526911a190fSTom Rhodes # endif
527911a190fSTom Rhodes 			if((tout = tims[tfd[0]].when - now) < 0)
528911a190fSTom Rhodes 				tout = 0;
529911a190fSTom Rhodes 		} else
530911a190fSTom Rhodes 			tout = INFTIM;
531911a190fSTom Rhodes 	} else
532911a190fSTom Rhodes 		tout = 0;
533911a190fSTom Rhodes 
534911a190fSTom Rhodes # ifdef DEBUG
535911a190fSTom Rhodes 	fprintf(stderr, "rpoll -- selecting with tout=%u", tout);
536911a190fSTom Rhodes # endif
537911a190fSTom Rhodes 
538911a190fSTom Rhodes # ifdef USE_POLL
539ca77a23fSHartmut Brandt 	ret = poll(pfd, regs_used, tout == INFTIM ? INFTIM : (tout / 1000));
540911a190fSTom Rhodes # endif
541911a190fSTom Rhodes 
542911a190fSTom Rhodes # ifdef USE_SELECT
543911a190fSTom Rhodes 	nrset = rset;
544911a190fSTom Rhodes 	nwset = wset;
545911a190fSTom Rhodes 	nxset = xset;
546911a190fSTom Rhodes 	if(tout != INFTIM) {
547ca77a23fSHartmut Brandt 		tv.tv_sec = tout / 1000000;
548ca77a23fSHartmut Brandt 		tv.tv_usec = tout % 1000000;
549911a190fSTom Rhodes 	}
550911a190fSTom Rhodes 	ret = select(maxfd+1,
551911a190fSTom Rhodes 		SELECT_CAST(&nrset),
552911a190fSTom Rhodes 		SELECT_CAST(&nwset),
553ca77a23fSHartmut Brandt 		SELECT_CAST(&nxset), (tout==INFTIM) ? NULL : &tv);
554911a190fSTom Rhodes # endif
555911a190fSTom Rhodes 
556911a190fSTom Rhodes 	if(ret == -1) {
557911a190fSTom Rhodes 		if(errno == EINTR)
558911a190fSTom Rhodes 			return;
559911a190fSTom Rhodes 		_panic("poll/select: %s", strerror(errno));
560911a190fSTom Rhodes 	}
561911a190fSTom Rhodes 
562911a190fSTom Rhodes 	/* dispatch files */
563911a190fSTom Rhodes 	if(ret > 0) {
564911a190fSTom Rhodes 		for(i = 0; i < regs_alloc; i++) {
565911a190fSTom Rhodes 			idx = rpoll_policy ? ((last_index+i) % regs_alloc) : i;
566911a190fSTom Rhodes 
567911a190fSTom Rhodes 			assert(idx < regs_alloc);
568911a190fSTom Rhodes 
569911a190fSTom Rhodes 			if(regs[idx].fd >= 0) {
570911a190fSTom Rhodes 				int mask = 0;
571911a190fSTom Rhodes 
572911a190fSTom Rhodes # ifdef USE_POLL
573911a190fSTom Rhodes 				if(regs[idx].pfd) {
574*8e9b3e70SHartmut Brandt 					if ((regs[idx].mask & RPOLL_IN) &&
575ca77a23fSHartmut Brandt 					    (regs[idx].pfd->revents & poll_in))
576*8e9b3e70SHartmut Brandt 						mask |= RPOLL_IN;
577*8e9b3e70SHartmut Brandt 					if ((regs[idx].mask & RPOLL_OUT) &&
578ca77a23fSHartmut Brandt 					    (regs[idx].pfd->revents & poll_out))
579*8e9b3e70SHartmut Brandt 						mask |= RPOLL_OUT;
580*8e9b3e70SHartmut Brandt 					if((regs[idx].mask & RPOLL_EXCEPT) &&
581ca77a23fSHartmut Brandt 					    (regs[idx].pfd->revents & poll_except))
582*8e9b3e70SHartmut Brandt 						mask |= RPOLL_EXCEPT;
583911a190fSTom Rhodes 				}
584911a190fSTom Rhodes # endif
585911a190fSTom Rhodes # ifdef USE_SELECT
586*8e9b3e70SHartmut Brandt 				if ((regs[idx].mask & RPOLL_IN) &&
587ca77a23fSHartmut Brandt 				    FD_ISSET(regs[idx].fd, &nrset))
588*8e9b3e70SHartmut Brandt 					mask |= RPOLL_IN;
589*8e9b3e70SHartmut Brandt 				if ((regs[idx].mask & RPOLL_OUT) &&
590ca77a23fSHartmut Brandt 				    FD_ISSET(regs[idx].fd, &nwset))
591*8e9b3e70SHartmut Brandt 					mask |= RPOLL_OUT;
592*8e9b3e70SHartmut Brandt 				if ((regs[idx].mask & RPOLL_EXCEPT) &&
593ca77a23fSHartmut Brandt 				    FD_ISSET(regs[idx].fd, &nxset))
594*8e9b3e70SHartmut Brandt 					mask |= RPOLL_EXCEPT;
595911a190fSTom Rhodes # endif
596911a190fSTom Rhodes 				assert(idx < regs_alloc);
597911a190fSTom Rhodes 
598911a190fSTom Rhodes 				if(mask) {
599911a190fSTom Rhodes 					if(rpoll_trace)
600911a190fSTom Rhodes 						fprintf(stderr, "poll_dispatch() -- "
601ca77a23fSHartmut Brandt 						    "file %d/%d %x",
602ca77a23fSHartmut Brandt 						    regs[idx].fd, idx, mask);
603911a190fSTom Rhodes 					(*regs[idx].func)(regs[idx].fd, mask, regs[idx].arg);
604911a190fSTom Rhodes 				}
605911a190fSTom Rhodes 			}
606911a190fSTom Rhodes 
607911a190fSTom Rhodes 		}
608911a190fSTom Rhodes 		last_index++;
609911a190fSTom Rhodes 	}
610911a190fSTom Rhodes 
611911a190fSTom Rhodes 	/* dispatch timeouts */
612911a190fSTom Rhodes 	if(tfd_used) {
613ca77a23fSHartmut Brandt 		now = GETUSECS();
614911a190fSTom Rhodes 		for(i = 0; i < tfd_used; i++) {
615911a190fSTom Rhodes 			if(tfd[i] < 0)
616911a190fSTom Rhodes 				continue;
617911a190fSTom Rhodes 			if(tims[tfd[i]].when > now)
618911a190fSTom Rhodes 				break;
619911a190fSTom Rhodes 			if(rpoll_trace)
620911a190fSTom Rhodes 				fprintf(stderr, "rpoll_dispatch() -- timeout %d",tfd[i]);
621911a190fSTom Rhodes 			(*tims[tfd[i]].func)(tfd[i], tims[tfd[i]].arg);
622911a190fSTom Rhodes 			if(tfd[i] < 0)
623911a190fSTom Rhodes 				continue;
624911a190fSTom Rhodes 			if(tims[tfd[i]].repeat)
625ca77a23fSHartmut Brandt 				tims[tfd[i]].when = now + tims[tfd[i]].usecs;
626911a190fSTom Rhodes 			else {
627911a190fSTom Rhodes 				tims[tfd[i]].func = NULL;
628911a190fSTom Rhodes 				tims_used--;
629911a190fSTom Rhodes 				tfd[i] = -1;
630911a190fSTom Rhodes 			}
631911a190fSTom Rhodes 			resort = 1;
632911a190fSTom Rhodes 		}
633911a190fSTom Rhodes 	}
634911a190fSTom Rhodes 	in_dispatch = 0;
635911a190fSTom Rhodes }
636911a190fSTom Rhodes 
637911a190fSTom Rhodes 
638911a190fSTom Rhodes # ifdef TESTME
639911a190fSTom Rhodes struct timeval start, now;
640911a190fSTom Rhodes int t0, t1;
641911a190fSTom Rhodes 
642911a190fSTom Rhodes double elaps(void);
643911a190fSTom Rhodes void infunc(int fd, int mask, void *arg);
644911a190fSTom Rhodes 
645911a190fSTom Rhodes double
elaps(void)646911a190fSTom Rhodes elaps(void)
647911a190fSTom Rhodes {
648911a190fSTom Rhodes 	gettimeofday(&now, NULL);
649911a190fSTom Rhodes 
650ca77a23fSHartmut Brandt 	return (double)(10 * now.tv_sec + now.tv_usec / 100000 -
651ca77a23fSHartmut Brandt 	    10 * start.tv_sec - start.tv_usec / 100000) / 10;
652911a190fSTom Rhodes }
653911a190fSTom Rhodes 
654911a190fSTom Rhodes void
infunc(int fd,int mask,void * arg)655911a190fSTom Rhodes infunc(int fd, int mask, void *arg)
656911a190fSTom Rhodes {
657911a190fSTom Rhodes 	char buf[1024];
658911a190fSTom Rhodes 	int ret;
659911a190fSTom Rhodes 
660911a190fSTom Rhodes 	mask = mask;
661911a190fSTom Rhodes 	arg = arg;
662911a190fSTom Rhodes 	if((ret = read(fd, buf, sizeof(buf))) < 0)
663911a190fSTom Rhodes 		_panic("read: %s", strerror(errno));
664911a190fSTom Rhodes 	write(1, "stdin:", 6);
665911a190fSTom Rhodes 	write(1, buf, ret);
666911a190fSTom Rhodes }
667911a190fSTom Rhodes 
668911a190fSTom Rhodes void tfunc0(int tid, void *arg);
669911a190fSTom Rhodes void tfunc1(int tid, void *arg);
670911a190fSTom Rhodes 
671911a190fSTom Rhodes void
tfunc0(int tid,void * arg)672911a190fSTom Rhodes tfunc0(int tid, void *arg)
673911a190fSTom Rhodes {
674911a190fSTom Rhodes 	printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg);
675911a190fSTom Rhodes }
676911a190fSTom Rhodes void
tfunc1(int tid,void * arg)677911a190fSTom Rhodes tfunc1(int tid, void *arg)
678911a190fSTom Rhodes {
679911a190fSTom Rhodes 	printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg);
680911a190fSTom Rhodes }
681ca77a23fSHartmut Brandt void
tfunc2(int tid,void * arg)682ca77a23fSHartmut Brandt tfunc2(int tid, void *arg)
683ca77a23fSHartmut Brandt {
684ca77a23fSHartmut Brandt 	static u_int count = 0;
685ca77a23fSHartmut Brandt 
686ca77a23fSHartmut Brandt 	if (++count % 10000 == 0)
687ca77a23fSHartmut Brandt 		printf("%4.1f -- %d\n", elaps(), tid);
688ca77a23fSHartmut Brandt }
689911a190fSTom Rhodes 
690911a190fSTom Rhodes void first(int tid, void *arg);
691911a190fSTom Rhodes void second(int tid, void *arg);
692911a190fSTom Rhodes 
693911a190fSTom Rhodes void
second(int tid,void * arg)694911a190fSTom Rhodes second(int tid, void *arg)
695911a190fSTom Rhodes {
696911a190fSTom Rhodes 	printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg);
697ca77a23fSHartmut Brandt 	poll_start_utimer(5500000, 0, first, "first");
698911a190fSTom Rhodes 	poll_stop_timer(t1);
699911a190fSTom Rhodes 	t0 = poll_start_timer(1000, 1, tfunc0, "1 second");
700911a190fSTom Rhodes }
701911a190fSTom Rhodes void
first(int tid,void * arg)702911a190fSTom Rhodes first(int tid, void *arg)
703911a190fSTom Rhodes {
704911a190fSTom Rhodes 	printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg);
705911a190fSTom Rhodes 	poll_start_timer(3700, 0, second, "second");
706911a190fSTom Rhodes 	poll_stop_timer(t0);
707911a190fSTom Rhodes 	t1 = poll_start_timer(250, 1, tfunc1, "1/4 second");
708911a190fSTom Rhodes }
709911a190fSTom Rhodes 
710911a190fSTom Rhodes int
main(int argc,char * argv[])711911a190fSTom Rhodes main(int argc, char *argv[])
712911a190fSTom Rhodes {
713911a190fSTom Rhodes 	argv = argv;
714911a190fSTom Rhodes 	gettimeofday(&start, NULL);
715*8e9b3e70SHartmut Brandt 	poll_register(0, infunc, NULL, RPOLL_IN);
716ca77a23fSHartmut Brandt 
717ca77a23fSHartmut Brandt 	if (argc < 2) {
718911a190fSTom Rhodes 		t0 = poll_start_timer(1000, 1, tfunc0, "1 second");
719911a190fSTom Rhodes 		poll_start_timer(2500, 0, first, "first");
720ca77a23fSHartmut Brandt 	} else {
721ca77a23fSHartmut Brandt 		t0 = poll_start_utimer(300, 1, tfunc2, NULL);
722ca77a23fSHartmut Brandt 	}
723911a190fSTom Rhodes 
724911a190fSTom Rhodes 	while(1)
725911a190fSTom Rhodes 		poll_dispatch(1);
726911a190fSTom Rhodes 
727911a190fSTom Rhodes 	return 0;
728911a190fSTom Rhodes }
729911a190fSTom Rhodes # endif
730