.\" @sel_init
.\" @sel_initfile
.\" @sel_addfile
+.\" @sel_force
.\" @sel_rmfile
.\" @sel_addtimer
.\" @sel_rmtimer
.BI " void (*" func ")(int " fd ", unsigned " mode ", void *" p ),
.BI " void *" p );
.BI "void sel_addfile(sel_file *" f );
+.BI "void sel_force(sel_file *" f );
.BI "void sel_rmfile(sel_file *" f );
.BI "void sel_addtimer(sel_state *" s ", sel_timer *" t ,
and
.BR conn (3).
.SH "PROGRAMMING INTERFACE"
+.SS "Multiplexors"
A multiplexor is represented using the type
.B sel_state
defined in the
multiplexor. It's convenient to separate addition and removal from
initialization because file selectors often get added and removed many
times over during their lifetimes.
-.PP
+.SS "File selectors"
A file selector is initialized by the
.B sel_initfile
function. This requires a large number of arguments:
file's new condition, and the pointer argument set up at initialization
time.
.PP
+The function
+.B sel_force
+will sometimes be useful while a
+.B sel_select
+call (see below) is in progress. It marks a file selector as being
+ready even if it's not really. This is most useful when dynamically
+adding a write selector: it's likely that the write will succeed
+immediately, so it's worth trying. This will only work properly if
+the write is non-blocking.
+.PP
The member
.B fd
of the
structure is exported. It contains the file descriptor in which the
selector is interested. You may not modify this value, but it's useful
to be able to read it out \- it saves having to keep a copy.
-.PP
+.SS "Timer selectors"
Timer selectors are simpler. There are only two operations provided on
timer selectors: addition and removal. Initialization is performed as
part of the addition operation.
timer selector is removed and the event can't happen again. This is
normally what you want. Removing a timer is only useful (or safe!)
before the timer event has been sent.
-.PP
+.SS "Performing I/O"
Finally, the function
.B sel_select
is passed a multiplexor object. It waits for something interesting to
returns zero. Otherwise it returns -1, and the global variable
.B errno
is set appropriately.
-.SH "SELECT HOOK FUNCTIONS"
+.SS "Hook functions"
In order to interface other I/O multiplexing systems to this one, it's
possible to register
.I hook
.SH "SEE ALSO"
.BR select (2),
.BR poll (2),
+.BR conn (3),
+.BR selbuf (3),
.BR mLib (3).
.SH AUTHOR
Mark Wooding, <mdw@nsict.org>
--- /dev/null
+.so man3/sel.3
/* -*-c-*-
*
- * $Id: sel.c,v 1.4 1999/08/19 18:30:26 mdw Exp $
+ * $Id: sel.c,v 1.5 1999/08/31 17:42:22 mdw Exp $
*
* I/O multiplexing support
*
/*----- Revision history --------------------------------------------------*
*
* $Log: sel.c,v $
+ * Revision 1.5 1999/08/31 17:42:22 mdw
+ * New function `sel_force' to force a descriptor to be `selected'.
+ *
* Revision 1.4 1999/08/19 18:30:26 mdw
* Implement hooks for foreign select-using systems (currently not well
* tested).
void sel_init(sel_state *s)
{
- s->files = 0;
+ int i;
+
+ for (i = 0; i < SEL_MODES; i++) {
+ s->files[i] = 0;
+ FD_ZERO(&s->fd[i]);
+ }
s->timers = 0;
s->hooks = 0;
- FD_ZERO(&s->fd[SEL_READ]);
- FD_ZERO(&s->fd[SEL_WRITE]);
- FD_ZERO(&s->fd[SEL_EXC]);
+ s->args = 0;
}
/* --- @sel_initfile@ --- *
void sel_addfile(sel_file *f)
{
- sel_file **ff = &f->s->files;
+ sel_file **ff = &f->s->files[f->mode];
/* --- This little dance looks like line-noise, but it does the job --- */
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
/* --- Initialize the argument block --- */
- a.maxfd = s->files ? s->files->fd + 1 : 0;
+ {
+ 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);
TV_SUB(&a.tv, &s->timers->tv, &a.now);
a.tvp = &a.tv;
}
+ s->args = &a;
/* --- Grind through the pre hooks --- */
if ((err = select(a.maxfd,
&a.fd[SEL_READ], &a.fd[SEL_WRITE], &a.fd[SEL_EXC],
- a.tvp)) < 0)
+ a.tvp)) < 0) {
+ s->args = 0;
return (err);
+ }
if (a.tvp)
gettimeofday(&a.now, 0);
t->prev = (sel_timer *)&s->timers;
}
- /* --- And finally run through the files --- */
+ /* --- 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.
+ */
{
- sel_file *f, *ff;
- for (f = s->files; f; f = ff) {
- ff = f->next;
- if (FD_ISSET(f->fd, a.fd + f->mode))
- f->func(f->fd, f->mode, f->p);
+ int i;
+
+ for (i = 0; i < SEL_MODES; i++) {
+ sel_file *f, *ff;
+ for (f = s->files[i]; f; f = ff) {
+ ff = f->next;
+ if (FD_ISSET(f->fd, a.fd + f->mode))
+ f->func(f->fd, f->mode, f->p);
+ }
}
}
+ s->args = 0;
return (0);
}
/* -*-c-*-
*
- * $Id: sel.h,v 1.5 1999/08/19 18:30:26 mdw Exp $
+ * $Id: sel.h,v 1.6 1999/08/31 17:42:22 mdw Exp $
*
* I/O multiplexing support
*
/*----- Revision history --------------------------------------------------*
*
* $Log: sel.h,v $
+ * Revision 1.6 1999/08/31 17:42:22 mdw
+ * New function `sel_force' to force a descriptor to be `selected'.
+ *
* Revision 1.5 1999/08/19 18:30:26 mdw
* Implement hooks for foreign select-using systems (currently not well
* tested).
*/
enum {
- SEL_READ,
- SEL_WRITE,
- SEL_EXC,
- SEL_MODES
+ SEL_READ, /* File is ready to read */
+ SEL_WRITE, /* File is ready to write */
+ SEL_EXC, /* Something odd has happened */
+ SEL_MODES /* Number of modes available */
};
typedef struct sel_state {
- struct sel_file *files;
- struct sel_timer *timers;
- struct sel_hook *hooks;
- fd_set fd[SEL_MODES];
- struct timeval tv;
+ struct sel_file *files[SEL_MODES]; /* Lists of interesting files */
+ struct sel_timer *timers; /* List of timers */
+ struct sel_hook *hooks; /* List of hook functions applied */
+ fd_set fd[SEL_MODES]; /* Quick reference table for files */
+ struct sel_args *args; /* Pointer to arguments */
} sel_state;
/* --- Listening for a file --- */
typedef struct sel_file {
- struct sel_file *next;
- struct sel_file *prev;
- struct sel_state *s;
- int fd;
- unsigned mode;
- void (*func)(int /*fd*/, unsigned /*mode*/, void */*p*/);
- void *p;
+ struct sel_file *next; /* Next file in the list */
+ struct sel_file *prev; /* Previous file in the list */
+ struct sel_state *s; /* Pointer to select multiplexor */
+ int fd; /* File descriptor */
+ unsigned mode; /* Interesting event for file */
+ void (*func)(int /*fd*/, unsigned /*mode*/, void */*p*/); /* Handler */
+ void *p; /* Argument for the handler */
} sel_file;
/* --- Waiting for a timeout --- */
typedef struct sel_timer {
- struct sel_timer *next;
- struct sel_timer *prev;
- struct timeval tv;
- void (*func)(struct timeval */*tv*/, void */*p*/);
- void *p;
+ struct sel_timer *next; /* Next timer in the list */
+ struct sel_timer *prev; /* Previous timer in the list */
+ struct timeval tv; /* Real time when timer should go */
+ void (*func)(struct timeval */*tv*/, void */*p*/); /* Handler function */
+ void *p; /* Argument for the handler */
} sel_timer;
/* --- A select argument block --- */
typedef struct sel_args {
- int maxfd;
- fd_set fd[SEL_MODES];
- struct timeval tv, *tvp;
- struct timeval now;
+ int maxfd; /* Highest-numbered file */
+ fd_set fd[SEL_MODES]; /* Bit flags for all the files */
+ struct timeval tv, *tvp; /* Time to return */
+ struct timeval now; /* Current time */
} sel_args;
/* --- A selector hook --- *
void */*p*/);
typedef struct sel_hook {
- struct sel_hook *next;
- struct sel_hook *prev;
- sel_hookfn before, after;
- void *p;
+ struct sel_hook *next; /* Next hook in the list */
+ struct sel_hook *prev; /* Previous hook in the list */
+ sel_hookfn before, after; /* Hook functions */
+ void *p; /* Argument for the hook functions */
} sel_hook;
/*----- Functions provided ------------------------------------------------*/
extern void sel_rmfile(sel_file */*f*/);
+/* --- @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.
+ */
+
+extern void sel_force(sel_file */*f*/);
+
/* --- @sel_addtimer@ --- *
*
* Arguments: @sel_state *s@ = pointer to a state block