+++ /dev/null
-/* -*-c-*-
- *
- * I/O multiplexing support
- *
- * (c) 1999 Straylight/Edgeware
- */
-
-/*----- Licensing notice --------------------------------------------------*
- *
- * This file is part of the mLib utilities library.
- *
- * mLib is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Library General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * mLib is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with mLib; if not, write to the Free
- * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- * MA 02111-1307, USA.
- */
-
-/*----- Header files ------------------------------------------------------*/
-
-#include <assert.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <sys/types.h>
-#include <sys/time.h>
-#include <unistd.h>
-
-#include "sel.h"
-#include "sub.h"
-#include "tv.h"
-
-/*----- Data structures ---------------------------------------------------*/
-
-typedef struct sel_pendfile {
- struct sel_pendfile *next;
- sel_file *f;
-} pfile;
-
-typedef struct sel_pendtimer {
- struct sel_pendtimer *next;
- sel_timer *t;
-} ptimer;
-
-/*----- Main code ---------------------------------------------------------*/
-
-/* --- @sel_init@ --- *
- *
- * Arguments: @sel_state *s@ = pointer to a state block to initialize
- *
- * Returns: ---
- *
- * Use: Initializes a select state block.
- */
-
-void sel_init(sel_state *s)
-{
- int i;
-
- for (i = 0; i < SEL_MODES; i++) {
- s->files[i] = 0;
- FD_ZERO(&s->fd[i]);
- }
- s->timers = 0;
- s->hooks = 0;
- s->args = 0;
-}
-
-/* --- @sel_initfile@ --- *
- *
- * Arguments: @sel_state *s@ = select state to attach to
- * @sel_file *f@ = pointer to a file block to initialize
- * @int fd@ = the file descriptor to listen to
- * @unsigned mode@ = what to listen for
- * @void (*func)(int fd, unsigned mode, void *p)@ = handler
- * @void *p@ = argument to pass to handler
- *
- * Returns: ---
- *
- * Use: Initializes a file block ready for use. The file block
- * isn't added to the list of things to do until a call to
- * @sel_addfile@.
- */
-
-void sel_initfile(sel_state *s, sel_file *f,
- int fd, unsigned mode,
- void (*func)(int /*fd*/, unsigned /*mode*/, void */*p*/),
- void *p)
-{
- f->s = s;
- f->fd = fd;
- f->mode = mode;
- f->func = func;
- f->p = p;
- f->pend = 0;
-}
-
-/* --- @sel_addfile@ --- *
- *
- * Arguments: @sel_file *f@ = pointer to a file block
- *
- * Returns: ---
- *
- * Use: Adds a file block into the list of things to listen to.
- */
-
-void sel_addfile(sel_file *f)
-{
- sel_file **ff = &f->s->files[f->mode];
-
- /* --- This little dance looks like line-noise, but it does the job --- */
-
- while (*ff && (*ff)->fd > f->fd)
- ff = &(*ff)->next;
- f->next = *ff;
- f->prev = ff;
- if (*ff)
- (*ff)->prev = &f->next;
- *ff = f;
- FD_SET(f->fd, f->s->fd + f->mode);
-}
-
-/* --- @sel_force@ --- *
- *
- * Arguments: @sel_file *f@ = pointer to file selector
- *
- * Returns: ---
- *
- * Use: Forces a file selector to be considered ready. This is only
- * useful during a call to @sel_select@. Of particular use is
- * forcing a write selector when there's something interesting
- * ready for it.
- */
-
-void sel_force(sel_file *f)
-{
- if (f->s->args)
- FD_SET(f->fd, &f->s->args->fd[f->mode]);
-}
-
-/* --- @sel_rmfile@ --- *
- *
- * Arguments: @sel_file *f@ = pointer to a file block
- *
- * Returns: ---
- *
- * Use: Removes a file block from the list of things to listen to.
- */
-
-void sel_rmfile(sel_file *f)
-{
- *f->prev = f->next;
- if (f->next)
- f->next->prev = f->prev;
- FD_CLR(f->fd, f->s->fd + f->mode);
- if (f->pend) {
- f->pend->f = 0;
- f->pend = 0;
- }
-}
-
-/* --- @sel_addtimer@ --- *
- *
- * Arguments: @sel_state *s@ = pointer to a state block
- * @sel_timer *t@ = pointer to a timer block
- * @struct timeval *tv@ = pointer to time to activate
- * @void (*func)(struct timeval *tv, void *p)@ = handler
- * @void *p@ = argument for handler function
- *
- * Returns: ---
- *
- * Use: Registers and sets up a timer.
- */
-
-void sel_addtimer(sel_state *s, sel_timer *t,
- struct timeval *tv,
- void (*func)(struct timeval */*tv*/, void */*p*/),
- void *p)
-{
- sel_timer **tt = &s->timers;
- { sel_timer *q; for (q = s->timers; q; q = q->next) assert(q != t); }
-
- /* --- Set up the timer block --- */
-
- t->tv = *tv;
- t->func = func;
- t->p = p;
- t->pend = 0;
-
- /* --- More line noise --- */
-
- while (*tt && TV_CMP(&(*tt)->tv, <, tv))
- tt = &(*tt)->next;
- t->next = *tt;
- t->prev = tt;
- if (*tt)
- (*tt)->prev = &t->next;
- *tt = t;
-}
-
-/* --- @sel_rmtimer@ --- *
- *
- * Arguments: @sel_timer *t@ = pointer to timer block
- *
- * Returns: ---
- *
- * Use: Removes a timer from the list of timers.
- */
-
-void sel_rmtimer(sel_timer *t)
-{
- if (t->pend) {
- t->pend->t = 0;
- t->pend = 0;
- } else {
- *t->prev = t->next;
- if (t->next)
- t->next->prev = t->prev;
- }
-}
-
-/* --- @sel_addhook@ --- *
- *
- * Arguments: @sel_state *s@ = pointer to state block
- * @sel_hook *h@ = pointer to hook block
- * @sel_hookfn before, after@ = hook functions
- * @void *p@ = pointer argument to pass to hook functions
- *
- * Returns: ---
- *
- * Use: Registers hook functions to be called on each select call.
- */
-
-void sel_addhook(sel_state *s, sel_hook *h,
- sel_hookfn before, sel_hookfn after,
- void *p)
-{
- h->before = before;
- h->after = after;
- h->p = p;
- h->next = s->hooks;
- h->prev = &s->hooks;
- if (s->hooks)
- s->hooks->prev = &h->next;
- s->hooks = h;
-}
-
-/* --- @sel_rmhook@ --- *
- *
- * Arguments: @sel_hook *h@ = pointer to hook block
- *
- * Returns: ---
- *
- * Use: Removes hook functions.
- */
-
-void sel_rmhook(sel_hook *h)
-{
- if (h->next)
- h->next->prev = h->prev;
- *h->prev = h->next;
-}
-
-/* --- @sel_fdmerge@ --- *
- *
- * Arguments: @fd_set *dest@ = destination FD set
- * @fd_set *fd@ = pointer to set to merge
- * @int maxfd@ = highest numbered descriptor in @fd@ + 1
- *
- * Returns: Actual highest numbered descriptor.
- *
- * Use: Merges file descriptor sets, and returns an accurate @maxfd@
- * value.
- */
-
-int sel_fdmerge(fd_set *dest, fd_set *fd, int maxfd)
-{
- int i, m = -1;
-
- for (i = 0; i < maxfd; i++) {
- if (FD_ISSET(i, fd)) {
- FD_SET(i, dest);
- m = i;
- }
- }
-
- return (m + 1);
-}
-
-/* --- @sel_select@ --- *
- *
- * Arguments: @sel_state *s@ = pointer to state block
- *
- * Returns: Zero if all OK, -1 on error.
- *
- * Use: Does a @select@ call (or equivalent @poll@).
- */
-
-int sel_select(sel_state *s)
-{
- sel_args a;
- int err;
-
- /* --- Initialize the argument block --- */
-
- {
- int i;
- a.maxfd = 0;
- for (i = 0; i < SEL_MODES; i++) {
- if (s->files[i] && s->files[i]->fd >= a.maxfd)
- a.maxfd = s->files[i]->fd + 1;
- }
- }
-
- memcpy(a.fd, s->fd, sizeof(a.fd));
- if (s->timers || s->hooks)
- gettimeofday(&a.now, 0);
- if (!s->timers)
- a.tvp = 0;
- else {
- if (TV_CMP(&s->timers->tv, >, &a.now))
- TV_SUB(&a.tv, &s->timers->tv, &a.now);
- else {
- a.tv.tv_sec = 0;
- a.tv.tv_usec = 0;
- }
- a.tvp = &a.tv;
- }
- s->args = &a;
-
- /* --- Grind through the pre hooks --- */
-
- {
- sel_hook *h = s->hooks;
- while (h) {
- sel_hook *hh = h;
- h = h->next;
- if (hh->before)
- hh->before(s, &a, hh->p);
- }
- }
-
- /* --- Run the @select@ call --- */
-
- if ((err = select(a.maxfd,
- &a.fd[SEL_READ], &a.fd[SEL_WRITE], &a.fd[SEL_EXC],
- a.tvp)) < 0) {
- s->args = 0;
- return (err);
- }
-
- if (a.tvp)
- gettimeofday(&a.now, 0);
-
- /* --- Run through the hooks again --- */
-
- {
- sel_hook *h = s->hooks;
- while (h) {
- sel_hook *hh = h;
- h = h->next;
- if (hh->after)
- hh->after(s, &a, hh->p);
- }
- }
-
- /* --- Run through the timers --- */
-
- if (s->timers) {
- ptimer *pthead, *pt, **ptt = &pthead;
- sel_timer *t;
-
- for (t = s->timers; t && TV_CMP(&t->tv, <=, &a.now); t = t->next) {
- pt = CREATE(ptimer);
- pt->t = t;
- t->pend = pt;
- *ptt = pt;
- ptt = &pt->next;
- }
- *ptt = 0;
- if (t) {
- *t->prev = 0;
- t->prev = &s->timers;
- }
- s->timers = t;
- while (pthead) {
- pt = pthead;
- pthead = pt->next;
- t = pt->t;
- if (t) {
- t->pend = 0;
- t->next = 0;
- t->prev = &t->next;
- t->func(&a.now, t->p);
- }
- DESTROY(pt);
- }
- }
-
- /* --- And finally run through the files --- *
- *
- * Do reads first. It's quite possible that a read might prompt a write,
- * but the other way around is less likely. Fortunately, the modes are
- * in the right order for this.
- */
-
- {
- int i;
-
- for (i = 0; i < SEL_MODES; i++) {
- pfile *pfhead, *pf, **pff = &pfhead;
- sel_file *f;
-
- for (f = s->files[i]; f; f = f->next) {
- if (!FD_ISSET(f->fd, &a.fd[i]))
- continue;
- pf = CREATE(pfile);
- pf->f = f;
- f->pend = pf;
- *pff = pf;
- pff = &pf->next;
- }
- *pff = 0;
- while (pfhead) {
- pf = pfhead;
- pfhead = pf->next;
- f = pf->f;
- if (f) {
- f->pend = 0;
- f->func(f->fd, i, f->p);
- }
- DESTROY(pf);
- }
- }
- }
-
- s->args = 0;
- return (0);
-}
-
-/*----- That's all, folks -------------------------------------------------*/