Vendor import of harti's begemot library.
This commit is contained in:
commit
911a190fe5
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/vendor/libbegemot/dist/; revision=135768 svn path=/vendor/libbegemot/1.1.1/; revision=135770; tag=vendor/libbegemot/1.1.1
714
contrib/libbegemot/rpoll.c
Normal file
714
contrib/libbegemot/rpoll.c
Normal file
@ -0,0 +1,714 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c)1996-2002 by Hartmut Brandt
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Author: Hartmut Brandt
|
||||||
|
*
|
||||||
|
* Redistribution of this software and documentation and use in source and
|
||||||
|
* binary forms, with or without modification, are permitted provided that
|
||||||
|
* the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code or documentation must retain the above
|
||||||
|
* copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE AUTHOR
|
||||||
|
* AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||||
|
* THE AUTHOR OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||||
|
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
|
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* These functions try to hide the poll/select/setitimer interface from the
|
||||||
|
* user. You associate callback functions with file descriptors and timers.
|
||||||
|
*
|
||||||
|
* $Begemot: libbegemot/rpoll.c,v 1.14 2004/09/21 15:59:00 brandt Exp $
|
||||||
|
*/
|
||||||
|
# include <stdio.h>
|
||||||
|
# include <stdlib.h>
|
||||||
|
# include <stddef.h>
|
||||||
|
# include <stdarg.h>
|
||||||
|
# include <signal.h>
|
||||||
|
# include <string.h>
|
||||||
|
# include <errno.h>
|
||||||
|
# include <time.h>
|
||||||
|
# include <assert.h>
|
||||||
|
# include <unistd.h>
|
||||||
|
# include <sys/time.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* There happens to be linuxes which read siginfo.h when including
|
||||||
|
* signal.h, which, for no appearent reason, defines these symbols.
|
||||||
|
*/
|
||||||
|
# ifdef POLL_IN
|
||||||
|
# undef POLL_IN
|
||||||
|
# endif
|
||||||
|
# ifdef POLL_OUT
|
||||||
|
# undef POLL_OUT
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# include "rpoll.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
# define DEBUG
|
||||||
|
*/
|
||||||
|
|
||||||
|
# ifdef USE_POLL
|
||||||
|
# ifdef NEED_POLL_XOPEN_TWIDDLE
|
||||||
|
# define __USE_XOPEN
|
||||||
|
# endif
|
||||||
|
# include <poll.h>
|
||||||
|
# ifdef NEED_POLL_XOPEN_TWIDDLE
|
||||||
|
# undef __USE_XOPEN
|
||||||
|
# endif
|
||||||
|
# include <stropts.h>
|
||||||
|
# endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* the second define is for Linux, which sometimes fails to
|
||||||
|
* declare INFTIM.
|
||||||
|
*/
|
||||||
|
# if defined(USE_SELECT) || !defined(INFTIM)
|
||||||
|
# define INFTIM (-1)
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# if defined(SIGPOLL)
|
||||||
|
# define SIGNAL SIGPOLL
|
||||||
|
# else
|
||||||
|
# if defined(SIGIO)
|
||||||
|
# define SIGNAL SIGIO
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# ifdef USE_POLL
|
||||||
|
# define poll_in (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)
|
||||||
|
# define poll_out (POLLOUT | POLLWRNORM | POLLWRBAND)
|
||||||
|
# define poll_except (POLLERR | POLLHUP)
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# ifdef BROKEN_SELECT_PROTO
|
||||||
|
# define SELECT_CAST(P) (int *)P
|
||||||
|
# else
|
||||||
|
# define SELECT_CAST(P) P
|
||||||
|
# endif
|
||||||
|
|
||||||
|
|
||||||
|
typedef signed long long tval_t;
|
||||||
|
|
||||||
|
static inline tval_t GETMSECS(void);
|
||||||
|
|
||||||
|
static inline tval_t
|
||||||
|
GETMSECS(void) {
|
||||||
|
struct timeval tval;
|
||||||
|
|
||||||
|
(void)gettimeofday(&tval, NULL);
|
||||||
|
return (tval_t)tval.tv_sec*1000+tval.tv_usec/1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Simple fatal exit.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
_panic(const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
fprintf(stderr, "panic: ");
|
||||||
|
vfprintf(stderr, fmt, ap);
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *
|
||||||
|
_xrealloc(void *p, size_t s)
|
||||||
|
{
|
||||||
|
void *ptr;
|
||||||
|
|
||||||
|
if(p == NULL) {
|
||||||
|
if((ptr=malloc(s)) == NULL && (s!=0 || (ptr=malloc(1)) == NULL))
|
||||||
|
_panic("out of memory: xrealloc(%lx, %lu)",
|
||||||
|
(unsigned long)p, (unsigned long)s);
|
||||||
|
} else if(s == 0) {
|
||||||
|
free(p);
|
||||||
|
if((ptr=malloc(s)) == NULL && (ptr=malloc(1)) == NULL)
|
||||||
|
_panic("out of memory: xrealloc(%lx, %lu)",
|
||||||
|
(unsigned long)p, (unsigned long)s);
|
||||||
|
} else {
|
||||||
|
if((ptr = realloc(p, s)) == NULL)
|
||||||
|
_panic("out of memory: xrealloc(%lx, %lu)",
|
||||||
|
(unsigned long)p, (unsigned long)s);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This structure holds one registration record for files
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
int fd; /* file descriptor (-1 if struct unused) */
|
||||||
|
int mask; /* event flags */
|
||||||
|
void * arg; /* client arg */
|
||||||
|
poll_f func; /* handler */
|
||||||
|
# ifdef USE_POLL
|
||||||
|
struct pollfd *pfd; /* pointer to corresponding poll() structure */
|
||||||
|
# endif
|
||||||
|
} PollReg_t;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now for timers
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
u_int msecs; /* millisecond value of the timer */
|
||||||
|
int repeat; /* one shot or repeat? */
|
||||||
|
void *arg; /* client arg */
|
||||||
|
timer_f func; /* handler, 0 means disfunct */
|
||||||
|
tval_t when; /* next time to trigger in msecs! */
|
||||||
|
} PollTim_t;
|
||||||
|
|
||||||
|
/* how many records should our table grow at once? */
|
||||||
|
# define POLL_REG_GROW 100
|
||||||
|
|
||||||
|
# ifdef USE_POLL
|
||||||
|
static struct pollfd * pfd; /* fd list for poll() */
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# ifdef USE_SELECT
|
||||||
|
static fd_set rset, wset, xset; /* file descriptor sets for select() */
|
||||||
|
static int maxfd; /* maximum fd number */
|
||||||
|
# endif
|
||||||
|
|
||||||
|
static int in_dispatch;
|
||||||
|
|
||||||
|
static PollReg_t * regs; /* registration records */
|
||||||
|
static u_int regs_alloc; /* how many are allocated */
|
||||||
|
static u_int regs_used; /* upper used limit */
|
||||||
|
static sigset_t bset; /* blocked signals */
|
||||||
|
static int rebuild; /* rebuild table on next dispatch() */
|
||||||
|
|
||||||
|
static int * tfd; /* sorted entries */
|
||||||
|
static u_int tfd_alloc; /* number of entries allocated */
|
||||||
|
static u_int tfd_used; /* number of entries used */
|
||||||
|
static PollTim_t * tims; /* timer registration records */
|
||||||
|
static u_int tims_alloc; /* how many are allocated */
|
||||||
|
static u_int tims_used; /* how many are used */
|
||||||
|
static int resort; /* resort on next dispatch */
|
||||||
|
|
||||||
|
int rpoll_trace;
|
||||||
|
int rpoll_policy; /* if 0 start sched callbacks from 0 else try round robin */
|
||||||
|
|
||||||
|
static void poll_build(void);
|
||||||
|
static void poll_blocksig(void);
|
||||||
|
static void poll_unblocksig(void);
|
||||||
|
static void sort_timers(void);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Private function to block SIGPOLL or SIGIO for a short time.
|
||||||
|
* Don't forget to call poll_unblock before return from the calling function.
|
||||||
|
* Don't change the mask between this calls (your changes will be lost).
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
poll_blocksig(void)
|
||||||
|
{
|
||||||
|
sigset_t set;
|
||||||
|
|
||||||
|
sigemptyset(&set);
|
||||||
|
sigaddset(&set, SIGNAL);
|
||||||
|
|
||||||
|
if(sigprocmask(SIG_BLOCK, &set, &bset))
|
||||||
|
_panic("sigprocmask(SIG_BLOCK): %s", strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* unblock the previously blocked signal
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
poll_unblocksig(void)
|
||||||
|
{
|
||||||
|
if(sigprocmask(SIG_SETMASK, &bset, NULL))
|
||||||
|
_panic("sigprocmask(SIG_SETMASK): %s", strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Register the file descriptor fd. If the event corresponding to
|
||||||
|
* mask arrives func is called with arg.
|
||||||
|
* If fd is already registered with that func and arg, only the mask
|
||||||
|
* is changed.
|
||||||
|
* We block the IO-signal, so the dispatch function can be called from
|
||||||
|
* within the signal handler.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
poll_register(int fd, poll_f func, void *arg, int mask)
|
||||||
|
{
|
||||||
|
PollReg_t * p;
|
||||||
|
|
||||||
|
poll_blocksig();
|
||||||
|
|
||||||
|
/* already registered? */
|
||||||
|
for(p = regs; p < ®s[regs_alloc]; p++)
|
||||||
|
if(p->fd == fd && p->func == func && p->arg == arg) {
|
||||||
|
p->mask = mask;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(p == ®s[regs_alloc]) {
|
||||||
|
/* no - register */
|
||||||
|
|
||||||
|
/* find a free slot */
|
||||||
|
for(p = regs; p < ®s[regs_alloc]; p++)
|
||||||
|
if(p->fd == -1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if(p == ®s[regs_alloc]) {
|
||||||
|
size_t newsize = regs_alloc + POLL_REG_GROW;
|
||||||
|
regs = _xrealloc(regs, sizeof(regs[0]) * newsize);
|
||||||
|
for(p = ®s[regs_alloc]; p < ®s[newsize]; p++) {
|
||||||
|
p->fd = -1;
|
||||||
|
# ifdef USE_POLL
|
||||||
|
p->pfd = NULL;
|
||||||
|
# endif
|
||||||
|
}
|
||||||
|
p = ®s[regs_alloc];
|
||||||
|
regs_alloc = newsize;
|
||||||
|
}
|
||||||
|
|
||||||
|
p->fd = fd;
|
||||||
|
p->arg = arg;
|
||||||
|
p->mask = mask;
|
||||||
|
p->func = func;
|
||||||
|
|
||||||
|
regs_used++;
|
||||||
|
rebuild = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
poll_unblocksig();
|
||||||
|
|
||||||
|
if(rpoll_trace)
|
||||||
|
fprintf(stderr, "poll_register(%d, %#lx, %#lx, %#x)->%d",
|
||||||
|
fd, (u_long)func, (u_long)arg, mask, p - regs);
|
||||||
|
return p - regs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* remove registration
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
poll_unregister(int handle)
|
||||||
|
{
|
||||||
|
if(rpoll_trace)
|
||||||
|
fprintf(stderr, "poll_unregister(%d)", handle);
|
||||||
|
|
||||||
|
poll_blocksig();
|
||||||
|
|
||||||
|
regs[handle].fd = -1;
|
||||||
|
# ifdef USE_POLL
|
||||||
|
regs[handle].pfd = NULL;
|
||||||
|
# endif
|
||||||
|
rebuild = 1;
|
||||||
|
regs_used--;
|
||||||
|
|
||||||
|
poll_unblocksig();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Build the structures used by poll() or select()
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
poll_build(void)
|
||||||
|
{
|
||||||
|
PollReg_t * p;
|
||||||
|
|
||||||
|
# ifdef USE_POLL
|
||||||
|
struct pollfd * f;
|
||||||
|
|
||||||
|
f = pfd = _xrealloc(pfd, sizeof(pfd[0]) * regs_used);
|
||||||
|
|
||||||
|
for(p = regs; p < ®s[regs_alloc]; p++)
|
||||||
|
if(p->fd >= 0) {
|
||||||
|
f->fd = p->fd;
|
||||||
|
f->events = 0;
|
||||||
|
if(p->mask & POLL_IN)
|
||||||
|
f->events |= poll_in;
|
||||||
|
if(p->mask & POLL_OUT)
|
||||||
|
f->events |= poll_out;
|
||||||
|
if(p->mask & POLL_EXCEPT)
|
||||||
|
f->events |= poll_except;
|
||||||
|
f->revents = 0;
|
||||||
|
p->pfd = f++;
|
||||||
|
}
|
||||||
|
assert(f == &pfd[regs_used]);
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# ifdef USE_SELECT
|
||||||
|
FD_ZERO(&rset);
|
||||||
|
FD_ZERO(&wset);
|
||||||
|
FD_ZERO(&xset);
|
||||||
|
maxfd = -1;
|
||||||
|
for(p = regs; p < ®s[regs_alloc]; p++)
|
||||||
|
if(p->fd >= 0) {
|
||||||
|
if(p->fd > maxfd)
|
||||||
|
maxfd = p->fd;
|
||||||
|
if(p->mask & POLL_IN)
|
||||||
|
FD_SET(p->fd, &rset);
|
||||||
|
if(p->mask & POLL_OUT)
|
||||||
|
FD_SET(p->fd, &wset);
|
||||||
|
if(p->mask & POLL_EXCEPT)
|
||||||
|
FD_SET(p->fd, &xset);
|
||||||
|
}
|
||||||
|
# endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
poll_start_timer(u_int msecs, int repeat, timer_f func, void *arg)
|
||||||
|
{
|
||||||
|
PollTim_t *p;
|
||||||
|
|
||||||
|
/* find unused entry */
|
||||||
|
for(p = tims; p < &tims[tims_alloc]; p++)
|
||||||
|
if(p->func == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if(p == &tims[tims_alloc]) {
|
||||||
|
if(tims_alloc == tims_used) {
|
||||||
|
size_t newsize = tims_alloc + POLL_REG_GROW;
|
||||||
|
tims = _xrealloc(tims, sizeof(tims[0]) * newsize);
|
||||||
|
for(p = &tims[tims_alloc]; p < &tims[newsize]; p++)
|
||||||
|
p->func = NULL;
|
||||||
|
p = &tims[tims_alloc];
|
||||||
|
tims_alloc = newsize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* create entry */
|
||||||
|
p->msecs = msecs;
|
||||||
|
p->repeat = repeat;
|
||||||
|
p->arg = arg;
|
||||||
|
p->func = func;
|
||||||
|
p->when = GETMSECS() + msecs;
|
||||||
|
|
||||||
|
tims_used++;
|
||||||
|
|
||||||
|
resort = 1;
|
||||||
|
|
||||||
|
if(rpoll_trace)
|
||||||
|
fprintf(stderr, "poll_start_timer(%u, %d, %#lx, %#lx)->%u",
|
||||||
|
msecs, repeat, (u_long)func, (u_long)arg, p - tims);
|
||||||
|
|
||||||
|
return p - tims;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Here we have to look into the sorted table, whether any entry there points
|
||||||
|
* into the registration table for the deleted entry. This is needed,
|
||||||
|
* because a unregistration can occure while we are scanning through the
|
||||||
|
* table in dispatch(). Do this only, if we are really there - resorting
|
||||||
|
* will sort out things if we are called from outside the loop.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
poll_stop_timer(int handle)
|
||||||
|
{
|
||||||
|
u_int i;
|
||||||
|
|
||||||
|
if(rpoll_trace)
|
||||||
|
fprintf(stderr, "poll_stop_timer(%d)", handle);
|
||||||
|
|
||||||
|
tims[handle].func = NULL;
|
||||||
|
tims_used--;
|
||||||
|
|
||||||
|
resort = 1;
|
||||||
|
|
||||||
|
if(!in_dispatch)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for(i = 0; i < tfd_used; i++)
|
||||||
|
if(tfd[i] == handle) {
|
||||||
|
tfd[i] = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Squeeze and sort timer table.
|
||||||
|
* Should perhaps use a custom sort.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
tim_cmp(const void *p1, const void *p2)
|
||||||
|
{
|
||||||
|
int t1 = *(const int *)p1;
|
||||||
|
int t2 = *(const int *)p2;
|
||||||
|
|
||||||
|
return tims[t1].when < tims[t2].when ? -1
|
||||||
|
: tims[t1].when > tims[t2].when ? +1
|
||||||
|
: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reconstruct the tfd-array. This will be an sorted array of indexes
|
||||||
|
* to the used entries in tims. The next timer to expire will be infront
|
||||||
|
* of the array. tfd_used is the number of used entries. The array is
|
||||||
|
* re-allocated if needed.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
sort_timers(void)
|
||||||
|
{
|
||||||
|
int *pp;
|
||||||
|
u_int i;
|
||||||
|
|
||||||
|
if(tims_used > tfd_alloc) {
|
||||||
|
tfd_alloc = tims_used;
|
||||||
|
tfd = _xrealloc(tfd, sizeof(int *) * tfd_alloc);
|
||||||
|
}
|
||||||
|
|
||||||
|
pp = tfd;
|
||||||
|
|
||||||
|
for(i = 0; i < tims_alloc; i++)
|
||||||
|
if(tims[i].func)
|
||||||
|
*pp++ = i;
|
||||||
|
assert(pp - tfd == (ptrdiff_t)tims_used);
|
||||||
|
|
||||||
|
tfd_used = tims_used;
|
||||||
|
if(tfd_used > 1)
|
||||||
|
qsort(tfd, tfd_used, sizeof(int), tim_cmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Poll the file descriptors and dispatch to the right function
|
||||||
|
* If wait is true the poll blocks until somewhat happens.
|
||||||
|
* Don't use a pointer here, because the called function may cause
|
||||||
|
* a reallocation! The check for pfd != NULL is required, because
|
||||||
|
* a sequence of unregister/register could make the wrong callback
|
||||||
|
* to be called. So we clear pfd in unregister and check here.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
poll_dispatch(int wait)
|
||||||
|
{
|
||||||
|
u_int i, idx;
|
||||||
|
int ret;
|
||||||
|
tval_t now;
|
||||||
|
int tout;
|
||||||
|
static u_int last_index;
|
||||||
|
|
||||||
|
# ifdef USE_SELECT
|
||||||
|
fd_set nrset, nwset, nxset;
|
||||||
|
struct timeval tv;
|
||||||
|
# endif
|
||||||
|
|
||||||
|
in_dispatch = 1;
|
||||||
|
|
||||||
|
if(rebuild) {
|
||||||
|
rebuild = 0;
|
||||||
|
poll_build();
|
||||||
|
}
|
||||||
|
if(resort) {
|
||||||
|
resort = 0;
|
||||||
|
sort_timers();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* in wait mode - compute the timeout */
|
||||||
|
if(wait) {
|
||||||
|
if(tfd_used) {
|
||||||
|
now = GETMSECS();
|
||||||
|
# ifdef DEBUG
|
||||||
|
{
|
||||||
|
fprintf(stderr, "now=%"QUADFMT"u", now);
|
||||||
|
for(i = 0; i < tims_used; i++)
|
||||||
|
fprintf(stderr, "timers[%2d] = %"QUADFMT"d", i, tfd[i]->when - now);
|
||||||
|
}
|
||||||
|
# endif
|
||||||
|
if((tout = tims[tfd[0]].when - now) < 0)
|
||||||
|
tout = 0;
|
||||||
|
} else
|
||||||
|
tout = INFTIM;
|
||||||
|
} else
|
||||||
|
tout = 0;
|
||||||
|
|
||||||
|
# ifdef DEBUG
|
||||||
|
fprintf(stderr, "rpoll -- selecting with tout=%u", tout);
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# ifdef USE_POLL
|
||||||
|
ret = poll(pfd, regs_used, tout);
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# ifdef USE_SELECT
|
||||||
|
nrset = rset;
|
||||||
|
nwset = wset;
|
||||||
|
nxset = xset;
|
||||||
|
if(tout != INFTIM) {
|
||||||
|
tv.tv_sec = tout / 1000;
|
||||||
|
tv.tv_usec = (tout % 1000) * 1000;
|
||||||
|
}
|
||||||
|
ret = select(maxfd+1,
|
||||||
|
SELECT_CAST(&nrset),
|
||||||
|
SELECT_CAST(&nwset),
|
||||||
|
SELECT_CAST(&nxset), (tout==INFTIM) ? 0 : &tv);
|
||||||
|
# endif
|
||||||
|
|
||||||
|
if(ret == -1) {
|
||||||
|
if(errno == EINTR)
|
||||||
|
return;
|
||||||
|
_panic("poll/select: %s", strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* dispatch files */
|
||||||
|
if(ret > 0) {
|
||||||
|
for(i = 0; i < regs_alloc; i++) {
|
||||||
|
idx = rpoll_policy ? ((last_index+i) % regs_alloc) : i;
|
||||||
|
|
||||||
|
assert(idx < regs_alloc);
|
||||||
|
|
||||||
|
if(regs[idx].fd >= 0) {
|
||||||
|
int mask = 0;
|
||||||
|
|
||||||
|
# ifdef USE_POLL
|
||||||
|
if(regs[idx].pfd) {
|
||||||
|
if(regs[idx].pfd->revents & poll_in)
|
||||||
|
mask |= POLL_IN;
|
||||||
|
if(regs[idx].pfd->revents & poll_out)
|
||||||
|
mask |= POLL_OUT;
|
||||||
|
if(regs[idx].pfd->revents & poll_except)
|
||||||
|
mask |= POLL_EXCEPT;
|
||||||
|
}
|
||||||
|
# endif
|
||||||
|
# ifdef USE_SELECT
|
||||||
|
if(FD_ISSET(regs[idx].fd, &nrset))
|
||||||
|
mask |= POLL_IN;
|
||||||
|
if(FD_ISSET(regs[idx].fd, &nwset))
|
||||||
|
mask |= POLL_OUT;
|
||||||
|
if(FD_ISSET(regs[idx].fd, &nxset))
|
||||||
|
mask |= POLL_EXCEPT;
|
||||||
|
# endif
|
||||||
|
assert(idx < regs_alloc);
|
||||||
|
|
||||||
|
if(mask) {
|
||||||
|
if(rpoll_trace)
|
||||||
|
fprintf(stderr, "poll_dispatch() -- "
|
||||||
|
"file %d/%d",
|
||||||
|
regs[idx].fd, idx);
|
||||||
|
(*regs[idx].func)(regs[idx].fd, mask, regs[idx].arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
last_index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* dispatch timeouts */
|
||||||
|
if(tfd_used) {
|
||||||
|
now = GETMSECS();
|
||||||
|
for(i = 0; i < tfd_used; i++) {
|
||||||
|
if(tfd[i] < 0)
|
||||||
|
continue;
|
||||||
|
if(tims[tfd[i]].when > now)
|
||||||
|
break;
|
||||||
|
if(rpoll_trace)
|
||||||
|
fprintf(stderr, "rpoll_dispatch() -- timeout %d",tfd[i]);
|
||||||
|
(*tims[tfd[i]].func)(tfd[i], tims[tfd[i]].arg);
|
||||||
|
if(tfd[i] < 0)
|
||||||
|
continue;
|
||||||
|
if(tims[tfd[i]].repeat)
|
||||||
|
tims[tfd[i]].when = now + tims[tfd[i]].msecs;
|
||||||
|
else {
|
||||||
|
tims[tfd[i]].func = NULL;
|
||||||
|
tims_used--;
|
||||||
|
tfd[i] = -1;
|
||||||
|
}
|
||||||
|
resort = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
in_dispatch = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# ifdef TESTME
|
||||||
|
struct timeval start, now;
|
||||||
|
int t0, t1;
|
||||||
|
|
||||||
|
double elaps(void);
|
||||||
|
void infunc(int fd, int mask, void *arg);
|
||||||
|
|
||||||
|
double
|
||||||
|
elaps(void)
|
||||||
|
{
|
||||||
|
gettimeofday(&now, NULL);
|
||||||
|
|
||||||
|
return (double)(10 * now.tv_sec + now.tv_usec / 100000 - 10 * start.tv_sec - start.tv_usec / 100000)
|
||||||
|
/ 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
infunc(int fd, int mask, void *arg)
|
||||||
|
{
|
||||||
|
char buf[1024];
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
mask = mask;
|
||||||
|
arg = arg;
|
||||||
|
if((ret = read(fd, buf, sizeof(buf))) < 0)
|
||||||
|
_panic("read: %s", strerror(errno));
|
||||||
|
write(1, "stdin:", 6);
|
||||||
|
write(1, buf, ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tfunc0(int tid, void *arg);
|
||||||
|
void tfunc1(int tid, void *arg);
|
||||||
|
|
||||||
|
void
|
||||||
|
tfunc0(int tid, void *arg)
|
||||||
|
{
|
||||||
|
printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg);
|
||||||
|
}
|
||||||
|
void
|
||||||
|
tfunc1(int tid, void *arg)
|
||||||
|
{
|
||||||
|
printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void first(int tid, void *arg);
|
||||||
|
void second(int tid, void *arg);
|
||||||
|
|
||||||
|
void
|
||||||
|
second(int tid, void *arg)
|
||||||
|
{
|
||||||
|
printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg);
|
||||||
|
poll_start_timer(5500, 0, first, "first");
|
||||||
|
poll_stop_timer(t1);
|
||||||
|
t0 = poll_start_timer(1000, 1, tfunc0, "1 second");
|
||||||
|
}
|
||||||
|
void
|
||||||
|
first(int tid, void *arg)
|
||||||
|
{
|
||||||
|
printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg);
|
||||||
|
poll_start_timer(3700, 0, second, "second");
|
||||||
|
poll_stop_timer(t0);
|
||||||
|
t1 = poll_start_timer(250, 1, tfunc1, "1/4 second");
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
argc = argc;
|
||||||
|
argv = argv;
|
||||||
|
gettimeofday(&start, NULL);
|
||||||
|
poll_register(0, infunc, NULL, POLL_IN);
|
||||||
|
t0 = poll_start_timer(1000, 1, tfunc0, "1 second");
|
||||||
|
poll_start_timer(2500, 0, first, "first");
|
||||||
|
|
||||||
|
while(1)
|
||||||
|
poll_dispatch(1);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
# endif
|
66
contrib/libbegemot/rpoll.h
Normal file
66
contrib/libbegemot/rpoll.h
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c)1996-2002 by Hartmut Brandt
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Author: Hartmut Brandt
|
||||||
|
*
|
||||||
|
* Redistribution of this software and documentation and use in source and
|
||||||
|
* binary forms, with or without modification, are permitted provided that
|
||||||
|
* the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code or documentation must retain the above
|
||||||
|
* copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE AUTHOR
|
||||||
|
* AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||||
|
* THE AUTHOR OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||||
|
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
|
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* $Begemot: libbegemot/rpoll.h,v 1.5 2004/09/21 15:49:26 brandt Exp $
|
||||||
|
*/
|
||||||
|
# ifndef rpoll_h_
|
||||||
|
# define rpoll_h_
|
||||||
|
|
||||||
|
# ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
# endif
|
||||||
|
|
||||||
|
typedef void (*poll_f)(int fd, int mask, void *arg);
|
||||||
|
typedef void (*timer_f)(int, void *);
|
||||||
|
|
||||||
|
int poll_register(int fd, poll_f func, void *arg, int mask);
|
||||||
|
void poll_unregister(int);
|
||||||
|
void poll_dispatch(int wait);
|
||||||
|
int poll_start_timer(u_int msecs, int repeat, timer_f func, void *arg);
|
||||||
|
void poll_stop_timer(int);
|
||||||
|
|
||||||
|
# if defined(POLL_IN)
|
||||||
|
# undef POLL_IN
|
||||||
|
# endif
|
||||||
|
# if defined(POLL_OUT)
|
||||||
|
# undef POLL_OUT
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# define POLL_IN 1
|
||||||
|
# define POLL_OUT 2
|
||||||
|
# define POLL_EXCEPT 4
|
||||||
|
|
||||||
|
extern int rpoll_policy;
|
||||||
|
extern int rpoll_trace;
|
||||||
|
|
||||||
|
# ifdef __cplusplus
|
||||||
|
}
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# endif
|
188
contrib/libbegemot/rpoll.man
Normal file
188
contrib/libbegemot/rpoll.man
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
'\"
|
||||||
|
'\" Copyright (c)1996-2002 by Hartmut Brandt
|
||||||
|
'\" All rights reserved.
|
||||||
|
'\"
|
||||||
|
'\" Author: Hartmut Brandt
|
||||||
|
'\"
|
||||||
|
'\" Redistribution of this software and documentation and use in source and
|
||||||
|
'\" binary forms, with or without modification, are permitted provided that
|
||||||
|
'\" the following conditions are met:
|
||||||
|
'\"
|
||||||
|
'\" 1. Redistributions of source code or documentation must retain the above
|
||||||
|
'\" copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
'\" 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
'\" notice, this list of conditions and the following disclaimer in the
|
||||||
|
'\" documentation and/or other materials provided with the distribution.
|
||||||
|
'\"
|
||||||
|
'\" THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE AUTHOR
|
||||||
|
'\" AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
'\" INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
'\" FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||||
|
'\" THE AUTHOR OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
'\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
'\" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||||
|
'\" OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
'\" LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
'\" NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
|
'\" EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
'\"
|
||||||
|
'\" $Begemot: libbegemot/rpoll.man,v 1.4 2004/09/21 15:59:00 brandt Exp $
|
||||||
|
'\"
|
||||||
|
.TH rpoll l "21 Oct 1996" "BEGEMOT" "BEGEMOT Library"
|
||||||
|
.SH NAME
|
||||||
|
rpoll - callback functions for file descriptors and timers
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.LP
|
||||||
|
.B "# include <rpoll.h>"
|
||||||
|
.LP
|
||||||
|
.BR "typedef void (*poll_f)(int " "fd" ", int " "mask" ", void *" "arg);"
|
||||||
|
.br
|
||||||
|
.BR "typedef void (*timer_f)(int " "tid" ", void *" "arg);"
|
||||||
|
.LP
|
||||||
|
.BR "int poll_register(int " "fd" ", poll_f "
|
||||||
|
.RB "func" ", void *" "arg" ", int " "mask" ");"
|
||||||
|
.LP
|
||||||
|
.BR "void poll_unregister(int " "handle" ");"
|
||||||
|
.LP
|
||||||
|
.BR "int poll_start_timer(u_int " "msecs" ", int " "repeat" ", timer_f " "func,"
|
||||||
|
.if n .ti +.5i
|
||||||
|
.BR "void *" "arg);"
|
||||||
|
.LP
|
||||||
|
.BR "void poll_stop_timer(int " "handle" ");"
|
||||||
|
.LP
|
||||||
|
.BR "void poll_dispatch(int " "wait" ");"
|
||||||
|
.SH DESCRIPTION
|
||||||
|
Many programs need to read from several file descriptors at the same time.
|
||||||
|
Typically in these programs one of
|
||||||
|
.BR select (3c)
|
||||||
|
or
|
||||||
|
.BR poll (2)
|
||||||
|
is used.
|
||||||
|
These calls are however clumsy to use and the usage of one of these calls is
|
||||||
|
probably not portable to other systems - not all systems support both calls.
|
||||||
|
.LP
|
||||||
|
The
|
||||||
|
.BR rpoll (l)
|
||||||
|
family of functions is designed to overcome these restrictions.
|
||||||
|
They support the well known and understood technique of event driven
|
||||||
|
programing and, in addition to
|
||||||
|
.BR select (3c)
|
||||||
|
and
|
||||||
|
.BR poll (2)
|
||||||
|
also support timers.
|
||||||
|
.LP
|
||||||
|
Each event on a file descriptor or each timer event is translated into a call to a user
|
||||||
|
defined callback function. These functions need to be registered.
|
||||||
|
A file descriptor is registered with
|
||||||
|
.BR poll_register .
|
||||||
|
.I fd
|
||||||
|
is the file descriptor to watch,
|
||||||
|
.I mask
|
||||||
|
is an event mask.
|
||||||
|
It may be any combination of
|
||||||
|
.B POLL_IN
|
||||||
|
to get informed, when input on the file descriptor is possible,
|
||||||
|
.B POLL_OUT
|
||||||
|
to get informed, when output is possible or
|
||||||
|
.B POLL_EXCEPT
|
||||||
|
to get informed, when an exceptional condition occures.
|
||||||
|
An example of an exceptional condition is the arrival of urgent data.
|
||||||
|
(Note, that an end of file condition is signaled via POLL_IN).
|
||||||
|
.I func
|
||||||
|
is the user function to be called and
|
||||||
|
.I arg
|
||||||
|
is a user supplied argument for this function.
|
||||||
|
The callback functions is called with the file descriptor, a mask
|
||||||
|
describing the actual events (from the set supplied in the registration) and
|
||||||
|
the user argument.
|
||||||
|
.B poll_register
|
||||||
|
returns a handle, which may be used later to de-register the file descriptor.
|
||||||
|
A file descriptor may be registered more than once, if the function, the user arguments
|
||||||
|
or both differ in the call to
|
||||||
|
.BR poll_register .
|
||||||
|
If
|
||||||
|
.I func
|
||||||
|
and
|
||||||
|
.I arg
|
||||||
|
are the same, then no new registration is done, instead the event mask of the registration
|
||||||
|
is changed to reflect the new mask.
|
||||||
|
.LP
|
||||||
|
A registered file descriptor may be de-registered by calling
|
||||||
|
.B poll_unregister
|
||||||
|
with the handle returned by
|
||||||
|
.BR poll_register .
|
||||||
|
.LP
|
||||||
|
A timer is created with
|
||||||
|
.BR poll_start_timer .
|
||||||
|
.I msecs
|
||||||
|
is the number of milliseconds, after which the timer event will be generated.
|
||||||
|
.I repeat
|
||||||
|
selects one-short behavior (if 0) or a repeatable timer (if not 0). A one-short timer
|
||||||
|
will automatically unregistered after expiry.
|
||||||
|
.I func
|
||||||
|
is the user function which will be called with a timer id and the user supplied
|
||||||
|
.IR arg .
|
||||||
|
.B poll_start_timer
|
||||||
|
returnes a timer id, which may be used to cancel the timer with
|
||||||
|
.BR poll_stop_timer .
|
||||||
|
A one-short timer should be canceled only if it has not yet fired.
|
||||||
|
.LP
|
||||||
|
.B poll_dispatch
|
||||||
|
must be called to actually dispatch events.
|
||||||
|
.I wait
|
||||||
|
is a flag, which should be 0, if only a poll should be done. In this case, the function returns,
|
||||||
|
after polling the registered file descriptors and timers. If
|
||||||
|
.I wait
|
||||||
|
is not 0,
|
||||||
|
.B poll_dispatch
|
||||||
|
waits until an event occures. All events are dispatch (i.e. callback functions called) and
|
||||||
|
.B poll_dispatch returns.
|
||||||
|
.LP
|
||||||
|
Typical use is:
|
||||||
|
.LP
|
||||||
|
.RS
|
||||||
|
.nf
|
||||||
|
.ft 3
|
||||||
|
while(1)
|
||||||
|
poll_dispatch(1);
|
||||||
|
.ft 1
|
||||||
|
.fi
|
||||||
|
.RE
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
.BR poll (2), select (3C)
|
||||||
|
.SH "RETURN VALUES"
|
||||||
|
.B poll_register
|
||||||
|
and
|
||||||
|
.B poll_start_timer
|
||||||
|
return a handle which may be used to unregister the file descriptor or cancel the timer.
|
||||||
|
.LP
|
||||||
|
Both functions and
|
||||||
|
.B poll_dispatch
|
||||||
|
call
|
||||||
|
.BR xrealloc (l)
|
||||||
|
and can end in
|
||||||
|
.BR panic (l).
|
||||||
|
.SH "ERRORS"
|
||||||
|
System call or memory allocation errors are fatal and are handle by calling
|
||||||
|
.BR panic (l).
|
||||||
|
The one exception is a return of EINTR from
|
||||||
|
.BR select (3c)
|
||||||
|
or
|
||||||
|
.BR poll (2)
|
||||||
|
in
|
||||||
|
.BR poll_dispatch .
|
||||||
|
In this case
|
||||||
|
.B poll_dispatch
|
||||||
|
simply returns.
|
||||||
|
.SH "BUGS"
|
||||||
|
Obscure sequences of
|
||||||
|
.B poll_start_timer
|
||||||
|
and
|
||||||
|
.B poll_stop_timer
|
||||||
|
in callback functions may probably break the code.
|
||||||
|
.LP
|
||||||
|
The semantics of
|
||||||
|
.B POLL_EXCEPT
|
||||||
|
are not clear.
|
||||||
|
.SH AUTHORS
|
||||||
|
Hartmut Brandt, harti@freebsd.org
|
Loading…
Reference in New Issue
Block a user