xref: /freebsd-src/contrib/libbegemot/rpoll.c (revision ca77a23f2350aa873e5cd7df24d72aad4fb5df2a)
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 /*
48911a190fSTom Rhodes  * There happens to be linuxes which read siginfo.h when including
49911a190fSTom Rhodes  * signal.h, which, for no appearent reason, defines these symbols.
50911a190fSTom Rhodes  */
51911a190fSTom Rhodes # ifdef POLL_IN
52911a190fSTom Rhodes #  undef POLL_IN
53911a190fSTom Rhodes # endif
54911a190fSTom Rhodes # ifdef POLL_OUT
55911a190fSTom Rhodes #  undef POLL_OUT
56911a190fSTom Rhodes # endif
57911a190fSTom Rhodes 
58911a190fSTom Rhodes # include "rpoll.h"
59911a190fSTom Rhodes 
60911a190fSTom Rhodes /*
61911a190fSTom Rhodes # define DEBUG
62911a190fSTom Rhodes */
63911a190fSTom Rhodes 
64911a190fSTom Rhodes # ifdef USE_POLL
65911a190fSTom Rhodes #  ifdef NEED_POLL_XOPEN_TWIDDLE
66911a190fSTom Rhodes #   define __USE_XOPEN
67911a190fSTom Rhodes #  endif
68911a190fSTom Rhodes #  include <poll.h>
69911a190fSTom Rhodes #  ifdef NEED_POLL_XOPEN_TWIDDLE
70911a190fSTom Rhodes #   undef __USE_XOPEN
71911a190fSTom Rhodes #  endif
72911a190fSTom Rhodes #  include <stropts.h>
73911a190fSTom Rhodes # endif
74911a190fSTom Rhodes 
75911a190fSTom Rhodes /*
76911a190fSTom Rhodes  * the second define is for Linux, which sometimes fails to
77911a190fSTom Rhodes  * declare INFTIM.
78911a190fSTom Rhodes  */
79911a190fSTom Rhodes # if defined(USE_SELECT) || !defined(INFTIM)
80911a190fSTom Rhodes #  define INFTIM (-1)
81911a190fSTom Rhodes # endif
82911a190fSTom Rhodes 
83911a190fSTom Rhodes # if defined(SIGPOLL)
84911a190fSTom Rhodes #  define SIGNAL	SIGPOLL
85911a190fSTom Rhodes # else
86911a190fSTom Rhodes #  if defined(SIGIO)
87911a190fSTom Rhodes #   define SIGNAL	SIGIO
88911a190fSTom Rhodes #  endif
89911a190fSTom Rhodes # endif
90911a190fSTom Rhodes 
91911a190fSTom Rhodes # ifdef USE_POLL
92911a190fSTom Rhodes #  define poll_in	(POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)
93911a190fSTom Rhodes #  define poll_out	(POLLOUT | POLLWRNORM | POLLWRBAND)
94911a190fSTom Rhodes #  define poll_except	(POLLERR | POLLHUP)
95911a190fSTom Rhodes # endif
96911a190fSTom Rhodes 
97911a190fSTom Rhodes # ifdef BROKEN_SELECT_PROTO
98911a190fSTom Rhodes #  define SELECT_CAST(P)	(int *)P
99911a190fSTom Rhodes # else
100911a190fSTom Rhodes #  define SELECT_CAST(P)	P
101911a190fSTom Rhodes # endif
102911a190fSTom Rhodes 
103911a190fSTom Rhodes 
104*ca77a23fSHartmut Brandt typedef int64_t tval_t;
105911a190fSTom Rhodes 
106*ca77a23fSHartmut Brandt static inline tval_t GETUSECS(void);
107911a190fSTom Rhodes 
108911a190fSTom Rhodes static inline tval_t
109*ca77a23fSHartmut Brandt GETUSECS(void) {
110911a190fSTom Rhodes 	struct timeval tval;
111911a190fSTom Rhodes 
112911a190fSTom Rhodes 	(void)gettimeofday(&tval, NULL);
113*ca77a23fSHartmut Brandt 	return (tval_t)tval.tv_sec * 1000000 + tval.tv_usec;
114911a190fSTom Rhodes }
115911a190fSTom Rhodes 
116911a190fSTom Rhodes /*
117911a190fSTom Rhodes  * Simple fatal exit.
118911a190fSTom Rhodes  */
119911a190fSTom Rhodes static void
120911a190fSTom Rhodes _panic(const char *fmt, ...)
121911a190fSTom Rhodes {
122911a190fSTom Rhodes 	va_list ap;
123911a190fSTom Rhodes 
124911a190fSTom Rhodes 	va_start(ap, fmt);
125911a190fSTom Rhodes 	fprintf(stderr, "panic: ");
126911a190fSTom Rhodes 	vfprintf(stderr, fmt, ap);
127911a190fSTom Rhodes 	fprintf(stderr, "\n");
128911a190fSTom Rhodes 	va_end(ap);
129911a190fSTom Rhodes 
130911a190fSTom Rhodes 	exit(1);
131911a190fSTom Rhodes }
132911a190fSTom Rhodes 
133911a190fSTom Rhodes static void *
134911a190fSTom Rhodes _xrealloc(void *p, size_t s)
135911a190fSTom Rhodes {
136911a190fSTom Rhodes 	void *ptr;
137911a190fSTom Rhodes 
138911a190fSTom Rhodes 	if(p == NULL) {
139911a190fSTom Rhodes 		if((ptr=malloc(s)) == NULL && (s!=0 || (ptr=malloc(1)) == NULL))
140911a190fSTom Rhodes 			_panic("out of memory: xrealloc(%lx, %lu)",
141911a190fSTom Rhodes 				(unsigned long)p, (unsigned long)s);
142911a190fSTom Rhodes 	} else if(s == 0) {
143911a190fSTom Rhodes 		free(p);
144911a190fSTom Rhodes 		if((ptr=malloc(s)) == NULL && (ptr=malloc(1)) == NULL)
145911a190fSTom Rhodes 			_panic("out of memory: xrealloc(%lx, %lu)",
146911a190fSTom Rhodes 				(unsigned long)p, (unsigned long)s);
147911a190fSTom Rhodes 	} else {
148911a190fSTom Rhodes 		if((ptr = realloc(p, s)) == NULL)
149911a190fSTom Rhodes 			_panic("out of memory: xrealloc(%lx, %lu)",
150911a190fSTom Rhodes 				(unsigned long)p, (unsigned long)s);
151911a190fSTom Rhodes 	}
152911a190fSTom Rhodes 
153911a190fSTom Rhodes 	return ptr;
154911a190fSTom Rhodes }
155911a190fSTom Rhodes 
156911a190fSTom Rhodes /*
157911a190fSTom Rhodes  * This structure holds one registration record for files
158911a190fSTom Rhodes  */
159911a190fSTom Rhodes typedef struct {
160911a190fSTom Rhodes 	int	fd;		/* file descriptor (-1 if struct unused) */
161911a190fSTom Rhodes 	int	mask;		/* event flags */
162911a190fSTom Rhodes 	void *	arg;		/* client arg */
163911a190fSTom Rhodes 	poll_f	func;		/* handler */
164911a190fSTom Rhodes # ifdef USE_POLL
165911a190fSTom Rhodes 	struct pollfd *pfd;	/* pointer to corresponding poll() structure */
166911a190fSTom Rhodes # endif
167911a190fSTom Rhodes } PollReg_t;
168911a190fSTom Rhodes 
169911a190fSTom Rhodes /*
170911a190fSTom Rhodes  * Now for timers
171911a190fSTom Rhodes  */
172911a190fSTom Rhodes typedef struct {
173*ca77a23fSHartmut Brandt 	uint64_t usecs;		/* microsecond value of the timer */
174911a190fSTom Rhodes 	int	repeat;		/* one shot or repeat? */
175911a190fSTom Rhodes 	void	*arg;		/* client arg */
176911a190fSTom Rhodes 	timer_f	func;		/* handler, 0 means disfunct */
177*ca77a23fSHartmut Brandt 	tval_t	when;		/* next time to trigger in usecs! */
178911a190fSTom Rhodes } PollTim_t;
179911a190fSTom Rhodes 
180911a190fSTom Rhodes /* how many records should our table grow at once? */
181911a190fSTom Rhodes # define POLL_REG_GROW	100
182911a190fSTom Rhodes 
183911a190fSTom Rhodes # ifdef USE_POLL
184911a190fSTom Rhodes static struct pollfd *	pfd;		/* fd list for poll() */
185911a190fSTom Rhodes # endif
186911a190fSTom Rhodes 
187911a190fSTom Rhodes # ifdef USE_SELECT
188911a190fSTom Rhodes static fd_set rset, wset, xset;		/* file descriptor sets for select() */
189911a190fSTom Rhodes static int maxfd;			/* maximum fd number */
190911a190fSTom Rhodes # endif
191911a190fSTom Rhodes 
192911a190fSTom Rhodes static int		in_dispatch;
193911a190fSTom Rhodes 
194911a190fSTom Rhodes static PollReg_t *	regs;		/* registration records */
195911a190fSTom Rhodes static u_int		regs_alloc;	/* how many are allocated */
196911a190fSTom Rhodes static u_int		regs_used;	/* upper used limit */
197911a190fSTom Rhodes static sigset_t		bset;		/* blocked signals */
198911a190fSTom Rhodes static int 		rebuild;	/* rebuild table on next dispatch() */
199911a190fSTom Rhodes 
200911a190fSTom Rhodes static int *		tfd;		/* sorted entries */
201911a190fSTom Rhodes static u_int		tfd_alloc;	/* number of entries allocated */
202911a190fSTom Rhodes static u_int		tfd_used;	/* number of entries used */
203911a190fSTom Rhodes static PollTim_t *	tims;		/* timer registration records */
204911a190fSTom Rhodes static u_int		tims_alloc;	/* how many are allocated */
205911a190fSTom Rhodes static u_int		tims_used;	/* how many are used */
206911a190fSTom Rhodes static int		resort;		/* resort on next dispatch */
207911a190fSTom Rhodes 
208911a190fSTom Rhodes int	rpoll_trace;
209911a190fSTom Rhodes int	rpoll_policy;	/* if 0 start sched callbacks from 0 else try round robin */
210911a190fSTom Rhodes 
211911a190fSTom Rhodes static void poll_build(void);
212911a190fSTom Rhodes static void poll_blocksig(void);
213911a190fSTom Rhodes static void poll_unblocksig(void);
214911a190fSTom Rhodes static void sort_timers(void);
215911a190fSTom Rhodes 
216911a190fSTom Rhodes 
217911a190fSTom Rhodes /*
218911a190fSTom Rhodes  * Private function to block SIGPOLL or SIGIO for a short time.
219911a190fSTom Rhodes  * Don't forget to call poll_unblock before return from the calling function.
220911a190fSTom Rhodes  * Don't change the mask between this calls (your changes will be lost).
221911a190fSTom Rhodes  */
222911a190fSTom Rhodes static void
223911a190fSTom Rhodes poll_blocksig(void)
224911a190fSTom Rhodes {
225911a190fSTom Rhodes 	sigset_t set;
226911a190fSTom Rhodes 
227911a190fSTom Rhodes 	sigemptyset(&set);
228911a190fSTom Rhodes 	sigaddset(&set, SIGNAL);
229911a190fSTom Rhodes 
230911a190fSTom Rhodes 	if(sigprocmask(SIG_BLOCK, &set, &bset))
231911a190fSTom Rhodes 		_panic("sigprocmask(SIG_BLOCK): %s", strerror(errno));
232911a190fSTom Rhodes }
233911a190fSTom Rhodes 
234911a190fSTom Rhodes /*
235911a190fSTom Rhodes  * unblock the previously blocked signal
236911a190fSTom Rhodes  */
237911a190fSTom Rhodes static void
238911a190fSTom Rhodes poll_unblocksig(void)
239911a190fSTom Rhodes {
240911a190fSTom Rhodes 	if(sigprocmask(SIG_SETMASK, &bset, NULL))
241911a190fSTom Rhodes 		_panic("sigprocmask(SIG_SETMASK): %s", strerror(errno));
242911a190fSTom Rhodes }
243911a190fSTom Rhodes 
244911a190fSTom Rhodes /*
245911a190fSTom Rhodes  * Register the file descriptor fd. If the event corresponding to
246911a190fSTom Rhodes  * mask arrives func is called with arg.
247911a190fSTom Rhodes  * If fd is already registered with that func and arg, only the mask
248911a190fSTom Rhodes  * is changed.
249911a190fSTom Rhodes  * We block the IO-signal, so the dispatch function can be called from
250911a190fSTom Rhodes  * within the signal handler.
251911a190fSTom Rhodes  */
252911a190fSTom Rhodes int
253911a190fSTom Rhodes poll_register(int fd, poll_f func, void *arg, int mask)
254911a190fSTom Rhodes {
255911a190fSTom Rhodes 	PollReg_t * p;
256911a190fSTom Rhodes 
257911a190fSTom Rhodes 	poll_blocksig();
258911a190fSTom Rhodes 
259911a190fSTom Rhodes 	/* already registered? */
260911a190fSTom Rhodes 	for(p = regs; p < &regs[regs_alloc]; p++)
261911a190fSTom Rhodes 		if(p->fd == fd && p->func == func && p->arg == arg) {
262911a190fSTom Rhodes 			p->mask = mask;
263911a190fSTom Rhodes 			break;
264911a190fSTom Rhodes 		}
265911a190fSTom Rhodes 
266911a190fSTom Rhodes 	if(p == &regs[regs_alloc]) {
267911a190fSTom Rhodes 		/* no - register */
268911a190fSTom Rhodes 
269911a190fSTom Rhodes 		/* find a free slot */
270911a190fSTom Rhodes 		for(p = regs; p < &regs[regs_alloc]; p++)
271911a190fSTom Rhodes 			if(p->fd == -1)
272911a190fSTom Rhodes 				break;
273911a190fSTom Rhodes 
274911a190fSTom Rhodes 		if(p == &regs[regs_alloc]) {
275911a190fSTom Rhodes 			size_t newsize = regs_alloc + POLL_REG_GROW;
276911a190fSTom Rhodes 			regs = _xrealloc(regs, sizeof(regs[0]) * newsize);
277911a190fSTom Rhodes 			for(p = &regs[regs_alloc]; p < &regs[newsize]; p++) {
278911a190fSTom Rhodes 				p->fd = -1;
279911a190fSTom Rhodes # ifdef USE_POLL
280911a190fSTom Rhodes 				p->pfd = NULL;
281911a190fSTom Rhodes # endif
282911a190fSTom Rhodes 			}
283911a190fSTom Rhodes 			p = &regs[regs_alloc];
284911a190fSTom Rhodes 			regs_alloc = newsize;
285911a190fSTom Rhodes 		}
286911a190fSTom Rhodes 
287911a190fSTom Rhodes 		p->fd = fd;
288911a190fSTom Rhodes 		p->arg = arg;
289911a190fSTom Rhodes 		p->mask = mask;
290911a190fSTom Rhodes 		p->func = func;
291911a190fSTom Rhodes 
292911a190fSTom Rhodes 		regs_used++;
293911a190fSTom Rhodes 		rebuild = 1;
294911a190fSTom Rhodes 	}
295911a190fSTom Rhodes 
296911a190fSTom Rhodes 	poll_unblocksig();
297911a190fSTom Rhodes 
298911a190fSTom Rhodes 	if(rpoll_trace)
299*ca77a23fSHartmut Brandt 		fprintf(stderr, "poll_register(%d, %p, %p, %#x)->%tu",
300*ca77a23fSHartmut Brandt 			fd, (void *)func, (void *)arg, mask, p - regs);
301911a190fSTom Rhodes 	return p - regs;
302911a190fSTom Rhodes }
303911a190fSTom Rhodes 
304911a190fSTom Rhodes /*
305911a190fSTom Rhodes  * remove registration
306911a190fSTom Rhodes  */
307911a190fSTom Rhodes void
308911a190fSTom Rhodes poll_unregister(int handle)
309911a190fSTom Rhodes {
310911a190fSTom Rhodes 	if(rpoll_trace)
311911a190fSTom Rhodes 		fprintf(stderr, "poll_unregister(%d)", handle);
312911a190fSTom Rhodes 
313911a190fSTom Rhodes 	poll_blocksig();
314911a190fSTom Rhodes 
315911a190fSTom Rhodes 	regs[handle].fd = -1;
316911a190fSTom Rhodes # ifdef USE_POLL
317911a190fSTom Rhodes 	regs[handle].pfd = NULL;
318911a190fSTom Rhodes # endif
319911a190fSTom Rhodes 	rebuild = 1;
320911a190fSTom Rhodes 	regs_used--;
321911a190fSTom Rhodes 
322911a190fSTom Rhodes 	poll_unblocksig();
323911a190fSTom Rhodes }
324911a190fSTom Rhodes 
325911a190fSTom Rhodes /*
326911a190fSTom Rhodes  * Build the structures used by poll() or select()
327911a190fSTom Rhodes  */
328911a190fSTom Rhodes static void
329911a190fSTom Rhodes poll_build(void)
330911a190fSTom Rhodes {
331911a190fSTom Rhodes 	PollReg_t * p;
332911a190fSTom Rhodes 
333911a190fSTom Rhodes # ifdef USE_POLL
334911a190fSTom Rhodes 	struct pollfd * f;
335911a190fSTom Rhodes 
336911a190fSTom Rhodes 	f = pfd = _xrealloc(pfd, sizeof(pfd[0]) * regs_used);
337911a190fSTom Rhodes 
338911a190fSTom Rhodes 	for(p = regs; p < &regs[regs_alloc]; p++)
339911a190fSTom Rhodes 		if(p->fd >= 0) {
340911a190fSTom Rhodes 			f->fd = p->fd;
341911a190fSTom Rhodes 			f->events = 0;
342911a190fSTom Rhodes 			if(p->mask & POLL_IN)
343911a190fSTom Rhodes 				f->events |= poll_in;
344911a190fSTom Rhodes 			if(p->mask & POLL_OUT)
345911a190fSTom Rhodes 				f->events |= poll_out;
346911a190fSTom Rhodes 			if(p->mask & POLL_EXCEPT)
347911a190fSTom Rhodes 				f->events |= poll_except;
348911a190fSTom Rhodes 			f->revents = 0;
349911a190fSTom Rhodes 			p->pfd = f++;
350911a190fSTom Rhodes 		}
351911a190fSTom Rhodes 	assert(f == &pfd[regs_used]);
352911a190fSTom Rhodes # endif
353911a190fSTom Rhodes 
354911a190fSTom Rhodes # ifdef USE_SELECT
355911a190fSTom Rhodes 	FD_ZERO(&rset);
356911a190fSTom Rhodes 	FD_ZERO(&wset);
357911a190fSTom Rhodes 	FD_ZERO(&xset);
358911a190fSTom Rhodes 	maxfd = -1;
359911a190fSTom Rhodes 	for(p = regs; p < &regs[regs_alloc]; p++)
360911a190fSTom Rhodes 		if(p->fd >= 0) {
361911a190fSTom Rhodes 			if(p->fd > maxfd)
362911a190fSTom Rhodes 				maxfd = p->fd;
363911a190fSTom Rhodes 			if(p->mask & POLL_IN)
364911a190fSTom Rhodes 				FD_SET(p->fd, &rset);
365911a190fSTom Rhodes 			if(p->mask & POLL_OUT)
366911a190fSTom Rhodes 				FD_SET(p->fd, &wset);
367911a190fSTom Rhodes 			if(p->mask & POLL_EXCEPT)
368911a190fSTom Rhodes 				FD_SET(p->fd, &xset);
369911a190fSTom Rhodes 		}
370911a190fSTom Rhodes # endif
371911a190fSTom Rhodes }
372911a190fSTom Rhodes 
373911a190fSTom Rhodes int
374911a190fSTom Rhodes poll_start_timer(u_int msecs, int repeat, timer_f func, void *arg)
375911a190fSTom Rhodes {
376*ca77a23fSHartmut Brandt 	return (poll_start_utimer((unsigned long long)msecs * 1000,
377*ca77a23fSHartmut Brandt 	    repeat, func, arg));
378*ca77a23fSHartmut Brandt }
379*ca77a23fSHartmut Brandt 
380*ca77a23fSHartmut Brandt int
381*ca77a23fSHartmut Brandt poll_start_utimer(unsigned long long usecs, int repeat, timer_f func, void *arg)
382*ca77a23fSHartmut Brandt {
383911a190fSTom Rhodes 	PollTim_t *p;
384911a190fSTom Rhodes 
385911a190fSTom Rhodes 	/* find unused entry */
386911a190fSTom Rhodes 	for(p = tims; p < &tims[tims_alloc]; p++)
387911a190fSTom Rhodes 		if(p->func == NULL)
388911a190fSTom Rhodes 			break;
389911a190fSTom Rhodes 
390911a190fSTom Rhodes 	if(p == &tims[tims_alloc]) {
391911a190fSTom Rhodes 		if(tims_alloc == tims_used) {
392911a190fSTom Rhodes 			size_t newsize = tims_alloc + POLL_REG_GROW;
393911a190fSTom Rhodes 			tims = _xrealloc(tims, sizeof(tims[0]) * newsize);
394911a190fSTom Rhodes 			for(p = &tims[tims_alloc]; p < &tims[newsize]; p++)
395911a190fSTom Rhodes 				p->func = NULL;
396911a190fSTom Rhodes 			p = &tims[tims_alloc];
397911a190fSTom Rhodes 			tims_alloc = newsize;
398911a190fSTom Rhodes 		}
399911a190fSTom Rhodes 	}
400911a190fSTom Rhodes 
401911a190fSTom Rhodes 	/* create entry */
402*ca77a23fSHartmut Brandt 	p->usecs = usecs;
403911a190fSTom Rhodes 	p->repeat = repeat;
404911a190fSTom Rhodes 	p->arg = arg;
405911a190fSTom Rhodes 	p->func = func;
406*ca77a23fSHartmut Brandt 	p->when = GETUSECS() + usecs;
407911a190fSTom Rhodes 
408911a190fSTom Rhodes 	tims_used++;
409911a190fSTom Rhodes 
410911a190fSTom Rhodes 	resort = 1;
411911a190fSTom Rhodes 
412911a190fSTom Rhodes 	if(rpoll_trace)
413*ca77a23fSHartmut Brandt 		fprintf(stderr, "poll_start_utimer(%llu, %d, %p, %p)->%tu",
414*ca77a23fSHartmut Brandt 			usecs, repeat, (void *)func, (void *)arg, p - tims);
415911a190fSTom Rhodes 
416911a190fSTom Rhodes 	return p - tims;
417911a190fSTom Rhodes }
418911a190fSTom Rhodes 
419911a190fSTom Rhodes /*
420911a190fSTom Rhodes  * Here we have to look into the sorted table, whether any entry there points
421911a190fSTom Rhodes  * into the registration table for the deleted entry. This is needed,
422911a190fSTom Rhodes  * because a unregistration can occure while we are scanning through the
423911a190fSTom Rhodes  * table in dispatch(). Do this only, if we are really there - resorting
424911a190fSTom Rhodes  * will sort out things if we are called from outside the loop.
425911a190fSTom Rhodes  */
426911a190fSTom Rhodes void
427911a190fSTom Rhodes poll_stop_timer(int handle)
428911a190fSTom Rhodes {
429911a190fSTom Rhodes 	u_int i;
430911a190fSTom Rhodes 
431911a190fSTom Rhodes 	if(rpoll_trace)
432911a190fSTom Rhodes 		fprintf(stderr, "poll_stop_timer(%d)", handle);
433911a190fSTom Rhodes 
434911a190fSTom Rhodes 	tims[handle].func = NULL;
435911a190fSTom Rhodes 	tims_used--;
436911a190fSTom Rhodes 
437911a190fSTom Rhodes 	resort = 1;
438911a190fSTom Rhodes 
439911a190fSTom Rhodes 	if(!in_dispatch)
440911a190fSTom Rhodes 		return;
441911a190fSTom Rhodes 
442911a190fSTom Rhodes 	for(i = 0; i < tfd_used; i++)
443911a190fSTom Rhodes 		if(tfd[i] == handle) {
444911a190fSTom Rhodes 			tfd[i] = -1;
445911a190fSTom Rhodes 			break;
446911a190fSTom Rhodes 		}
447911a190fSTom Rhodes }
448911a190fSTom Rhodes 
449911a190fSTom Rhodes /*
450911a190fSTom Rhodes  * Squeeze and sort timer table.
451911a190fSTom Rhodes  * Should perhaps use a custom sort.
452911a190fSTom Rhodes  */
453911a190fSTom Rhodes static int
454911a190fSTom Rhodes tim_cmp(const void *p1, const void *p2)
455911a190fSTom Rhodes {
456911a190fSTom Rhodes 	int t1 = *(const int *)p1;
457911a190fSTom Rhodes 	int t2 = *(const int *)p2;
458911a190fSTom Rhodes 
459911a190fSTom Rhodes 	return tims[t1].when < tims[t2].when ? -1
460911a190fSTom Rhodes 	     : tims[t1].when > tims[t2].when ? +1
461911a190fSTom Rhodes 	     :                        		  0;
462911a190fSTom Rhodes }
463911a190fSTom Rhodes 
464911a190fSTom Rhodes /*
465911a190fSTom Rhodes  * Reconstruct the tfd-array. This will be an sorted array of indexes
466911a190fSTom Rhodes  * to the used entries in tims. The next timer to expire will be infront
467911a190fSTom Rhodes  * of the array. tfd_used is the number of used entries. The array is
468911a190fSTom Rhodes  * re-allocated if needed.
469911a190fSTom Rhodes  */
470911a190fSTom Rhodes static void
471911a190fSTom Rhodes sort_timers(void)
472911a190fSTom Rhodes {
473911a190fSTom Rhodes 	int *pp;
474911a190fSTom Rhodes 	u_int i;
475911a190fSTom Rhodes 
476911a190fSTom Rhodes 	if(tims_used > tfd_alloc) {
477911a190fSTom Rhodes 		tfd_alloc = tims_used;
478911a190fSTom Rhodes 		tfd  = _xrealloc(tfd, sizeof(int *) * tfd_alloc);
479911a190fSTom Rhodes 	}
480911a190fSTom Rhodes 
481911a190fSTom Rhodes 	pp = tfd;
482911a190fSTom Rhodes 
483911a190fSTom Rhodes 	for(i = 0; i < tims_alloc; i++)
484911a190fSTom Rhodes 		if(tims[i].func)
485911a190fSTom Rhodes 			*pp++ = i;
486911a190fSTom Rhodes 	assert(pp - tfd == (ptrdiff_t)tims_used);
487911a190fSTom Rhodes 
488911a190fSTom Rhodes 	tfd_used = tims_used;
489911a190fSTom Rhodes 	if(tfd_used > 1)
490911a190fSTom Rhodes 		qsort(tfd, tfd_used, sizeof(int), tim_cmp);
491911a190fSTom Rhodes }
492911a190fSTom Rhodes 
493911a190fSTom Rhodes /*
494911a190fSTom Rhodes  * Poll the file descriptors and dispatch to the right function
495911a190fSTom Rhodes  * If wait is true the poll blocks until somewhat happens.
496911a190fSTom Rhodes  * Don't use a pointer here, because the called function may cause
497911a190fSTom Rhodes  * a reallocation! The check for pfd != NULL is required, because
498911a190fSTom Rhodes  * a sequence of unregister/register could make the wrong callback
499911a190fSTom Rhodes  * to be called. So we clear pfd in unregister and check here.
500911a190fSTom Rhodes  */
501911a190fSTom Rhodes void
502911a190fSTom Rhodes poll_dispatch(int wait)
503911a190fSTom Rhodes {
504911a190fSTom Rhodes 	u_int i, idx;
505911a190fSTom Rhodes 	int ret;
506911a190fSTom Rhodes 	tval_t now;
507*ca77a23fSHartmut Brandt 	tval_t tout;
508911a190fSTom Rhodes 	static u_int last_index;
509911a190fSTom Rhodes 
510911a190fSTom Rhodes # ifdef USE_SELECT
511911a190fSTom Rhodes 	fd_set nrset, nwset, nxset;
512911a190fSTom Rhodes 	struct timeval tv;
513911a190fSTom Rhodes # endif
514911a190fSTom Rhodes 
515911a190fSTom Rhodes 	in_dispatch = 1;
516911a190fSTom Rhodes 
517911a190fSTom Rhodes 	if(rebuild) {
518911a190fSTom Rhodes 		rebuild = 0;
519911a190fSTom Rhodes 		poll_build();
520911a190fSTom Rhodes 	}
521911a190fSTom Rhodes 	if(resort) {
522911a190fSTom Rhodes 		resort = 0;
523911a190fSTom Rhodes 		sort_timers();
524911a190fSTom Rhodes 	}
525911a190fSTom Rhodes 
526911a190fSTom Rhodes 	/* in wait mode - compute the timeout */
527911a190fSTom Rhodes 	if(wait) {
528911a190fSTom Rhodes 		if(tfd_used) {
529*ca77a23fSHartmut Brandt 			now = GETUSECS();
530911a190fSTom Rhodes # ifdef DEBUG
531911a190fSTom Rhodes 			{
532*ca77a23fSHartmut Brandt 				fprintf(stderr, "now=%llu", now);
533911a190fSTom Rhodes 				for(i = 0; i < tims_used; i++)
534*ca77a23fSHartmut Brandt 					fprintf(stderr, "timers[%2d] = %lld",
535*ca77a23fSHartmut Brandt 					    i, tfd[i]->when - now);
536911a190fSTom Rhodes 			}
537911a190fSTom Rhodes # endif
538911a190fSTom Rhodes 			if((tout = tims[tfd[0]].when - now) < 0)
539911a190fSTom Rhodes 				tout = 0;
540911a190fSTom Rhodes 		} else
541911a190fSTom Rhodes 			tout = INFTIM;
542911a190fSTom Rhodes 	} else
543911a190fSTom Rhodes 		tout = 0;
544911a190fSTom Rhodes 
545911a190fSTom Rhodes # ifdef DEBUG
546911a190fSTom Rhodes 	fprintf(stderr, "rpoll -- selecting with tout=%u", tout);
547911a190fSTom Rhodes # endif
548911a190fSTom Rhodes 
549911a190fSTom Rhodes # ifdef USE_POLL
550*ca77a23fSHartmut Brandt 	ret = poll(pfd, regs_used, tout == INFTIM ? INFTIM : (tout / 1000));
551911a190fSTom Rhodes # endif
552911a190fSTom Rhodes 
553911a190fSTom Rhodes # ifdef USE_SELECT
554911a190fSTom Rhodes 	nrset = rset;
555911a190fSTom Rhodes 	nwset = wset;
556911a190fSTom Rhodes 	nxset = xset;
557911a190fSTom Rhodes 	if(tout != INFTIM) {
558*ca77a23fSHartmut Brandt 		tv.tv_sec = tout / 1000000;
559*ca77a23fSHartmut Brandt 		tv.tv_usec = tout % 1000000;
560911a190fSTom Rhodes 	}
561911a190fSTom Rhodes 	ret = select(maxfd+1,
562911a190fSTom Rhodes 		SELECT_CAST(&nrset),
563911a190fSTom Rhodes 		SELECT_CAST(&nwset),
564*ca77a23fSHartmut Brandt 		SELECT_CAST(&nxset), (tout==INFTIM) ? NULL : &tv);
565911a190fSTom Rhodes # endif
566911a190fSTom Rhodes 
567911a190fSTom Rhodes 	if(ret == -1) {
568911a190fSTom Rhodes 		if(errno == EINTR)
569911a190fSTom Rhodes 			return;
570911a190fSTom Rhodes 		_panic("poll/select: %s", strerror(errno));
571911a190fSTom Rhodes 	}
572911a190fSTom Rhodes 
573911a190fSTom Rhodes 	/* dispatch files */
574911a190fSTom Rhodes 	if(ret > 0) {
575911a190fSTom Rhodes 		for(i = 0; i < regs_alloc; i++) {
576911a190fSTom Rhodes 			idx = rpoll_policy ? ((last_index+i) % regs_alloc) : i;
577911a190fSTom Rhodes 
578911a190fSTom Rhodes 			assert(idx < regs_alloc);
579911a190fSTom Rhodes 
580911a190fSTom Rhodes 			if(regs[idx].fd >= 0) {
581911a190fSTom Rhodes 				int mask = 0;
582911a190fSTom Rhodes 
583911a190fSTom Rhodes # ifdef USE_POLL
584911a190fSTom Rhodes 				if(regs[idx].pfd) {
585*ca77a23fSHartmut Brandt 					if ((regs[idx].mask & POLL_IN) &&
586*ca77a23fSHartmut Brandt 					    (regs[idx].pfd->revents & poll_in))
587911a190fSTom Rhodes 						mask |= POLL_IN;
588*ca77a23fSHartmut Brandt 					if ((regs[idx].mask & POLL_OUT) &&
589*ca77a23fSHartmut Brandt 					    (regs[idx].pfd->revents & poll_out))
590911a190fSTom Rhodes 						mask |= POLL_OUT;
591*ca77a23fSHartmut Brandt 					if((regs[idx].mask & POLL_EXCEPT) &&
592*ca77a23fSHartmut Brandt 					    (regs[idx].pfd->revents & poll_except))
593911a190fSTom Rhodes 						mask |= POLL_EXCEPT;
594911a190fSTom Rhodes 				}
595911a190fSTom Rhodes # endif
596911a190fSTom Rhodes # ifdef USE_SELECT
597*ca77a23fSHartmut Brandt 				if ((regs[idx].mask & POLL_IN) &&
598*ca77a23fSHartmut Brandt 				    FD_ISSET(regs[idx].fd, &nrset))
599911a190fSTom Rhodes 					mask |= POLL_IN;
600*ca77a23fSHartmut Brandt 				if ((regs[idx].mask & POLL_OUT) &&
601*ca77a23fSHartmut Brandt 				    FD_ISSET(regs[idx].fd, &nwset))
602911a190fSTom Rhodes 					mask |= POLL_OUT;
603*ca77a23fSHartmut Brandt 				if ((regs[idx].mask & POLL_EXCEPT) &&
604*ca77a23fSHartmut Brandt 				    FD_ISSET(regs[idx].fd, &nxset))
605911a190fSTom Rhodes 					mask |= POLL_EXCEPT;
606911a190fSTom Rhodes # endif
607911a190fSTom Rhodes 				assert(idx < regs_alloc);
608911a190fSTom Rhodes 
609911a190fSTom Rhodes 				if(mask) {
610911a190fSTom Rhodes 					if(rpoll_trace)
611911a190fSTom Rhodes 						fprintf(stderr, "poll_dispatch() -- "
612*ca77a23fSHartmut Brandt 						    "file %d/%d %x",
613*ca77a23fSHartmut Brandt 						    regs[idx].fd, idx, mask);
614911a190fSTom Rhodes 					(*regs[idx].func)(regs[idx].fd, mask, regs[idx].arg);
615911a190fSTom Rhodes 				}
616911a190fSTom Rhodes 			}
617911a190fSTom Rhodes 
618911a190fSTom Rhodes 		}
619911a190fSTom Rhodes 		last_index++;
620911a190fSTom Rhodes 	}
621911a190fSTom Rhodes 
622911a190fSTom Rhodes 	/* dispatch timeouts */
623911a190fSTom Rhodes 	if(tfd_used) {
624*ca77a23fSHartmut Brandt 		now = GETUSECS();
625911a190fSTom Rhodes 		for(i = 0; i < tfd_used; i++) {
626911a190fSTom Rhodes 			if(tfd[i] < 0)
627911a190fSTom Rhodes 				continue;
628911a190fSTom Rhodes 			if(tims[tfd[i]].when > now)
629911a190fSTom Rhodes 				break;
630911a190fSTom Rhodes 			if(rpoll_trace)
631911a190fSTom Rhodes 				fprintf(stderr, "rpoll_dispatch() -- timeout %d",tfd[i]);
632911a190fSTom Rhodes 			(*tims[tfd[i]].func)(tfd[i], tims[tfd[i]].arg);
633911a190fSTom Rhodes 			if(tfd[i] < 0)
634911a190fSTom Rhodes 				continue;
635911a190fSTom Rhodes 			if(tims[tfd[i]].repeat)
636*ca77a23fSHartmut Brandt 				tims[tfd[i]].when = now + tims[tfd[i]].usecs;
637911a190fSTom Rhodes 			else {
638911a190fSTom Rhodes 				tims[tfd[i]].func = NULL;
639911a190fSTom Rhodes 				tims_used--;
640911a190fSTom Rhodes 				tfd[i] = -1;
641911a190fSTom Rhodes 			}
642911a190fSTom Rhodes 			resort = 1;
643911a190fSTom Rhodes 		}
644911a190fSTom Rhodes 	}
645911a190fSTom Rhodes 	in_dispatch = 0;
646911a190fSTom Rhodes }
647911a190fSTom Rhodes 
648911a190fSTom Rhodes 
649911a190fSTom Rhodes # ifdef TESTME
650911a190fSTom Rhodes struct timeval start, now;
651911a190fSTom Rhodes int t0, t1;
652911a190fSTom Rhodes 
653911a190fSTom Rhodes double elaps(void);
654911a190fSTom Rhodes void infunc(int fd, int mask, void *arg);
655911a190fSTom Rhodes 
656911a190fSTom Rhodes double
657911a190fSTom Rhodes elaps(void)
658911a190fSTom Rhodes {
659911a190fSTom Rhodes 	gettimeofday(&now, NULL);
660911a190fSTom Rhodes 
661*ca77a23fSHartmut Brandt 	return (double)(10 * now.tv_sec + now.tv_usec / 100000 -
662*ca77a23fSHartmut Brandt 	    10 * start.tv_sec - start.tv_usec / 100000) / 10;
663911a190fSTom Rhodes }
664911a190fSTom Rhodes 
665911a190fSTom Rhodes void
666911a190fSTom Rhodes infunc(int fd, int mask, void *arg)
667911a190fSTom Rhodes {
668911a190fSTom Rhodes 	char buf[1024];
669911a190fSTom Rhodes 	int ret;
670911a190fSTom Rhodes 
671911a190fSTom Rhodes 	mask = mask;
672911a190fSTom Rhodes 	arg = arg;
673911a190fSTom Rhodes 	if((ret = read(fd, buf, sizeof(buf))) < 0)
674911a190fSTom Rhodes 		_panic("read: %s", strerror(errno));
675911a190fSTom Rhodes 	write(1, "stdin:", 6);
676911a190fSTom Rhodes 	write(1, buf, ret);
677911a190fSTom Rhodes }
678911a190fSTom Rhodes 
679911a190fSTom Rhodes void tfunc0(int tid, void *arg);
680911a190fSTom Rhodes void tfunc1(int tid, void *arg);
681911a190fSTom Rhodes 
682911a190fSTom Rhodes void
683911a190fSTom Rhodes tfunc0(int tid, void *arg)
684911a190fSTom Rhodes {
685911a190fSTom Rhodes 	printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg);
686911a190fSTom Rhodes }
687911a190fSTom Rhodes void
688911a190fSTom Rhodes tfunc1(int tid, void *arg)
689911a190fSTom Rhodes {
690911a190fSTom Rhodes 	printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg);
691911a190fSTom Rhodes }
692*ca77a23fSHartmut Brandt void
693*ca77a23fSHartmut Brandt tfunc2(int tid, void *arg)
694*ca77a23fSHartmut Brandt {
695*ca77a23fSHartmut Brandt 	static u_int count = 0;
696*ca77a23fSHartmut Brandt 
697*ca77a23fSHartmut Brandt 	if (++count % 10000 == 0)
698*ca77a23fSHartmut Brandt 		printf("%4.1f -- %d\n", elaps(), tid);
699*ca77a23fSHartmut Brandt }
700911a190fSTom Rhodes 
701911a190fSTom Rhodes void first(int tid, void *arg);
702911a190fSTom Rhodes void second(int tid, void *arg);
703911a190fSTom Rhodes 
704911a190fSTom Rhodes void
705911a190fSTom Rhodes second(int tid, void *arg)
706911a190fSTom Rhodes {
707911a190fSTom Rhodes 	printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg);
708*ca77a23fSHartmut Brandt 	poll_start_utimer(5500000, 0, first, "first");
709911a190fSTom Rhodes 	poll_stop_timer(t1);
710911a190fSTom Rhodes 	t0 = poll_start_timer(1000, 1, tfunc0, "1 second");
711911a190fSTom Rhodes }
712911a190fSTom Rhodes void
713911a190fSTom Rhodes first(int tid, void *arg)
714911a190fSTom Rhodes {
715911a190fSTom Rhodes 	printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg);
716911a190fSTom Rhodes 	poll_start_timer(3700, 0, second, "second");
717911a190fSTom Rhodes 	poll_stop_timer(t0);
718911a190fSTom Rhodes 	t1 = poll_start_timer(250, 1, tfunc1, "1/4 second");
719911a190fSTom Rhodes }
720911a190fSTom Rhodes 
721911a190fSTom Rhodes int
722911a190fSTom Rhodes main(int argc, char *argv[])
723911a190fSTom Rhodes {
724911a190fSTom Rhodes 	argv = argv;
725911a190fSTom Rhodes 	gettimeofday(&start, NULL);
726911a190fSTom Rhodes 	poll_register(0, infunc, NULL, POLL_IN);
727*ca77a23fSHartmut Brandt 
728*ca77a23fSHartmut Brandt 	if (argc < 2) {
729911a190fSTom Rhodes 		t0 = poll_start_timer(1000, 1, tfunc0, "1 second");
730911a190fSTom Rhodes 		poll_start_timer(2500, 0, first, "first");
731*ca77a23fSHartmut Brandt 	} else {
732*ca77a23fSHartmut Brandt 		t0 = poll_start_utimer(300, 1, tfunc2, NULL);
733*ca77a23fSHartmut Brandt 	}
734911a190fSTom Rhodes 
735911a190fSTom Rhodes 	while(1)
736911a190fSTom Rhodes 		poll_dispatch(1);
737911a190fSTom Rhodes 
738911a190fSTom Rhodes 	return 0;
739911a190fSTom Rhodes }
740911a190fSTom Rhodes # endif
741