chiark / gitweb /
New version number.
[mLib] / sel.c
1 /* -*-c-*-
2  *
3  * $Id: sel.c,v 1.2 1999/05/15 10:33:32 mdw Exp $
4  *
5  * I/O multiplexing support
6  *
7  * (c) 1999 Straylight/Edgeware
8  */
9
10 /*----- Licensing notice --------------------------------------------------* 
11  *
12  * This file is part of the mLib utilities library.
13  *
14  * mLib is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU Library General Public License as
16  * published by the Free Software Foundation; either version 2 of the
17  * License, or (at your option) any later version.
18  * 
19  * mLib is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU Library General Public License for more details.
23  * 
24  * You should have received a copy of the GNU Library General Public
25  * License along with mLib; if not, write to the Free
26  * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
27  * MA 02111-1307, USA.
28  */
29
30 /*----- Revision history --------------------------------------------------* 
31  *
32  * $Log: sel.c,v $
33  * Revision 1.2  1999/05/15 10:33:32  mdw
34  * Fix copyright notices.
35  *
36  * Revision 1.1  1999/05/14 21:01:14  mdw
37  * Integrated `select' handling bits from the background resolver project.
38  *
39  */
40
41 /*----- Header files ------------------------------------------------------*/
42
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46
47 #include <sys/types.h>
48 #include <sys/time.h>
49 #include <unistd.h>
50
51 #include "sel.h"
52 #include "tv.h"
53
54 /*----- Main code ---------------------------------------------------------*/
55
56 /* --- @sel_init@ --- *
57  *
58  * Arguments:   @sel_state *s@ = pointer to a state block to initialize
59  *
60  * Returns:     ---
61  *
62  * Use:         Initializes a select state block.
63  */
64
65 void sel_init(sel_state *s)
66 {
67   s->files = 0;
68   s->timers = 0;
69   FD_ZERO(&s->fd[SEL_READ]);
70   FD_ZERO(&s->fd[SEL_WRITE]);
71   FD_ZERO(&s->fd[SEL_EXC]);
72 }
73
74 /* --- @sel_initfile@ --- *
75  *
76  * Arguments:   @sel_state *s@ = select state to attach to
77  *              @sel_file *f@ = pointer to a file block to initialize
78  *              @int fd@ = the file descriptor to listen to
79  *              @unsigned mode@ = what to listen for
80  *              @void (*func)(int fd, unsigned mode, void *p)@ = handler
81  *              @void *p@ = argument to pass to handler
82  *
83  * Returns:     ---
84  *
85  * Use:         Initializes a file block ready for use.  The file block
86  *              isn't added to the list of things to do until a call to
87  *              @sel_addfile@.
88  */
89
90 void sel_initfile(sel_state *s, sel_file *f,
91                   int fd, unsigned mode,
92                   void (*func)(int /*fd*/, unsigned /*mode*/, void */*p*/),
93                   void *p)
94 {
95   f->s = s;
96   f->fd = fd;
97   f->mode = mode;
98   f->func = func;
99   f->p = p;
100 }
101
102 /* --- @sel_addfile@ --- *
103  *
104  * Arguments:   @sel_file *f@ = pointer to a file block
105  *
106  * Returns:     ---
107  *
108  * Use:         Adds a file block into the list of things to listen to.
109  */
110
111 void sel_addfile(sel_file *f)
112 {
113   sel_file **ff = &f->s->files;
114
115   /* --- This little dance looks like line-noise, but it does the job --- */
116
117   while (*ff && (*ff)->fd > f->fd)
118     ff = &(*ff)->next;
119   f->next = *ff;
120   f->prev = (sel_file *)ff;
121   if (*ff)
122     (*ff)->prev = f;
123   *ff = f;
124   FD_SET(f->fd, f->s->fd + f->mode);
125 }
126
127 /* --- @sel_rmfile@ --- *
128  *
129  * Arguments:   @sel_file *f@ = pointer to a file block
130  *
131  * Returns:     ---
132  *
133  * Use:         Removes a file block from the list of things to listen to.
134  */
135
136 void sel_rmfile(sel_file *f)
137 {
138   f->prev->next = f->next;
139   if (f->next)
140     f->next->prev = f->prev;
141   FD_CLR(f->fd, f->s->fd + f->mode);
142 }
143
144 /* --- @sel_addtimer@ --- *
145  *
146  * Arguments:   @sel_state *s@ = pointer to a state block
147  *              @sel_timer *t@ = pointer to a timer block
148  *              @struct timeval *tv@ = pointer to time to activate
149  *              @void (*func)(struct timeval *tv, void *p)@ = handler
150  *              @void *p@ = argument for handler function
151  *
152  * Returns:     ---
153  *
154  * Use:         Registers and sets up a timer.
155  */
156
157 void sel_addtimer(sel_state *s, sel_timer *t,
158                   struct timeval *tv,
159                   void (*func)(struct timeval */*tv*/, void */*p*/),
160                   void *p)
161 {
162   sel_timer **tt = &s->timers;
163
164   /* --- Set up the timer block --- */
165
166   t->tv = *tv;
167   t->func = func;
168   t->p = p;
169
170   /* --- More line noise --- */
171   
172   while (*tt && tv_cmp(&(*tt)->tv, tv) > 0)
173     tt = &(*tt)->next;
174   t->next = *tt;
175   t->prev = (sel_timer *)tt;
176   if (*tt)
177     (*tt)->prev = t;
178   *tt = t;
179 }
180
181 /* --- @sel_rmtimer@ --- *
182  *
183  * Arguments:   @sel_timer *t@ = pointer to timer block
184  *
185  * Returns:     ---
186  *
187  * Use:         Removes a timer from the list of timers.
188  */
189
190 void sel_rmtimer(sel_timer *t)
191 {
192   t->prev->next = t->next;
193   if (t->next)
194     t->next->prev = t->prev;
195 }
196
197 /* --- @sel_select@ --- *
198  *
199  * Arguments:   @sel_state *s@ = pointer to state block
200  *
201  * Returns:     Zero if all OK, -1 on error.
202  *
203  * Use:         Does a @select@ call (or equivalent @poll@).
204  */
205
206 int sel_select(sel_state *s)
207 {
208   fd_set fd[SEL_MODES];
209   struct timeval tv;
210   int err;
211
212   memcpy(fd, s->fd, sizeof(s->fd));
213   if (s->timers) {
214     struct timeval now;
215     gettimeofday(&now, 0);
216     tv_sub(&tv, &now, &s->timers->tv);
217     err = select(s->files ? s->files->fd + 1 : 0,
218                  fd + SEL_READ, fd + SEL_WRITE, fd + SEL_EXC,
219                  &tv);
220     gettimeofday(&tv, 0);
221   } else
222     err = select(s->files ? s->files->fd + 1 : 0,
223                  fd + SEL_READ, fd + SEL_WRITE, fd + SEL_EXC,
224                  0);
225
226   if (err < 0)
227     return (err);
228
229   {
230     sel_timer *t, *tt;
231     for (t = s->timers; t && tv_cmp(&t->tv, &tv) <= 0; t = tt) {
232       tt = t->next;
233       t->next = t->prev = t;
234       t->func(&tv, t->p);
235     }
236     s->timers = t;
237     if (t)
238       t->prev = (sel_timer *)&s->timers;
239   }
240
241   {
242     sel_file *f, *ff;
243     for (f = s->files; f; f = ff) {
244       ff = f->next;
245       if (FD_ISSET(f->fd, fd + f->mode))
246         f->func(f->fd, f->mode, f->p);
247     }
248   }
249
250   return (0);
251 }
252
253 /*----- That's all, folks -------------------------------------------------*/