/* $Id: dispatcher.c,v 1.3 2001/03/14 19:33:10 steve Exp $ */ /*- * Copyright (c) 2001 Steve C. Woodford. * All rights reserved. * * Redistribution 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 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Steve C. Woodford. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. */ #include #include #include #include #include #include #include #include #include "context.h" #include "dispatcher.h" static struct context_qhead contexts; static struct client_ctx **fd2ctx; static struct pollfd *pfdlist; static int dtablesize; struct dispatch_timeout { TAILQ_ENTRY(dispatch_timeout) dt_qent; int dt_when; int dt_onq; void (*dt_func)(void *, void *); void *dt_arg; }; TAILQ_HEAD(dispatch_timeout_qhead, dispatch_timeout); static struct dispatch_timeout_qhead timeouts; int dispatcher_init(void) { if (pfdlist != NULL) return(0); TAILQ_INIT(&contexts); TAILQ_INIT(&timeouts); dtablesize = getdtablesize(); assert(dtablesize > 0); if ((pfdlist = calloc((size_t)dtablesize, sizeof(*pfdlist))) == NULL) return (-1); if ((fd2ctx = calloc((size_t)dtablesize, sizeof(*fd2ctx))) == NULL) { (void) free(pfdlist); pfdlist = NULL; return (-1); } signal(SIGPIPE, SIG_IGN); return (0); } void dispatcher_destroy(void) { struct context *ctx, *nctx; struct dispatch_timeout *dt, *ndt; if (pfdlist == NULL) return; for (ctx = TAILQ_FIRST(&contexts); ctx != NULL; ctx = nctx) { nctx = TAILQ_NEXT(ctx, c_qent); context_destroy(ctx); } for (dt = TAILQ_FIRST(&timeouts); dt != NULL; dt = ndt) { ndt = TAILQ_NEXT(dt, dt_qent); (void) free(dt); } (void) free(fd2ctx); (void) free(pfdlist); } int dispatcher_add_context(struct context *ctx) { TAILQ_INSERT_TAIL(&contexts, ctx, c_qent); return (0); } void dispatcher_del_context(struct context *ctx) { TAILQ_REMOVE(&contexts, ctx, c_qent); } int dispatcher_add_client(struct client_ctx *cc) { int fd = client_getfd(cc); assert(fd >= 0 && fd < dtablesize); assert(fd2ctx[fd] == NULL); fd2ctx[fd] = cc; return (0); } void dispatcher_del_client(struct client_ctx *cc) { int fd = client_getfd(cc); assert(fd >= 0 && fd < dtablesize); assert(fd2ctx[fd] != NULL); fd2ctx[fd] = NULL; } void * dispatcher_init_timeout(void (*fp)(void *, void *), void *arg) { struct dispatch_timeout *dt; if ((dt = calloc(1, sizeof(*dt))) == NULL) return (NULL); dt->dt_func = fp; dt->dt_arg = arg; dt->dt_onq = 0; return ((void *)dt); } void dispatcher_destroy_timeout(void *dtp) { struct dispatch_timeout *dt = dtp; if (dt->dt_onq) TAILQ_REMOVE(&timeouts, dt, dt_qent); (void) free(dt); } void dispatcher_sched_timeout(void *dtp, int when) { struct dispatch_timeout *dt, *ldt; assert(when > 0); dt = dtp; if (dt->dt_onq) TAILQ_REMOVE(&timeouts, dt, dt_qent); for (ldt = TAILQ_FIRST(&timeouts); ldt != NULL; ldt = TAILQ_NEXT(ldt, dt_qent)) { if (when < ldt->dt_when) break; when -= ldt->dt_when; } dt->dt_when = when; dt->dt_onq = 1; if (ldt != NULL) TAILQ_INSERT_BEFORE(ldt, dt, dt_qent); else TAILQ_INSERT_HEAD(&timeouts, dt, dt_qent); } void dispatcher_cancel_timeout(void *dtp) { struct dispatch_timeout *dt = dtp, *ndt; if (dt->dt_onq) { TAILQ_REMOVE(&timeouts, dt, dt_qent); dt->dt_onq = 0; if ((ndt = TAILQ_NEXT(dt, dt_qent)) != NULL) ndt->dt_when += dt->dt_when; } } static void process_timeouts(void) { struct dispatch_timeout *dt = TAILQ_FIRST(&timeouts); int delta; assert(dt != NULL); for (delta = dt->dt_when; dt && (dt->dt_when -= delta) <= 0; dt = TAILQ_FIRST((volatile struct dispatch_timeout_qhead *)&timeouts)) { TAILQ_REMOVE(&timeouts, dt, dt_qent); dt->dt_onq = 0; (dt->dt_func)((void *)dt, dt->dt_arg); } } int dispatcher_mainloop(void) { struct dispatch_timeout *dt; struct context *ctx; struct client_ctx *cc; struct pollfd *pf; nfds_t pfds; int nfds, errcnt = 0; int timeout; time_t delta; while (!TAILQ_EMPTY(&contexts)) { for (pfds = 0, ctx = TAILQ_FIRST(&contexts); ctx != NULL; ctx = TAILQ_NEXT(ctx, c_qent)) { pfds += context_setup_pollfds(ctx, &pfdlist[pfds]); } if ((dt = TAILQ_FIRST(&timeouts)) != NULL) { timeout = dt->dt_when * 1000; delta = time(NULL); } else timeout = INFTIM; while ((nfds = poll(pfdlist, pfds, timeout)) < 0 && errno == EINTR) ; if (dt != NULL) { dt->dt_when -= (int)(time(NULL) - delta); if (dt->dt_when <= 0) process_timeouts(); } if (nfds < 0) { if (++errcnt > 4) return (-1); continue; } else errcnt = 0; for (pf = pfdlist; nfds; pf++) { if (pf->revents == 0) continue; nfds--; if ((cc = fd2ctx[pf->fd]) == NULL) continue; if (client_event(cc, pf->revents) < 0) { ctx = cc->cc_ctx; if (context_del_client(ctx, cc) < 0) context_destroy(ctx); } } } return (0); }