]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/doscmd/AsyncIO.c
Clean up the code a bit:
[FreeBSD/FreeBSD.git] / usr.bin / doscmd / AsyncIO.c
1 /*
2  * Copyright (c) 1992, 1993, 1996
3  *      Berkeley Software Design, Inc.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by Berkeley Software
16  *      Design, Inc.
17  *
18  * THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL Berkeley Software Design, Inc. BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  *      BSDI AsyncIO.c,v 2.2 1996/04/08 19:32:10 bostic Exp
31  *
32  * $FreeBSD$
33  */
34
35 #include <sys/param.h>
36 #include <sys/types.h>
37 #include <sys/time.h>
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <limits.h>
41 #include <signal.h>
42 #include <stdio.h>
43 #include <unistd.h>
44
45 #include "doscmd.h"
46 #include "AsyncIO.h"
47
48 #define FD_ISZERO(p)    ((p)->fds_bits[0] == 0)
49
50 /*
51  * Set or Clear the Async nature of an FD
52  */
53
54 #define SETASYNC(fd)    fcntl(fd, F_SETFL, handlers[fd].flag | FASYNC)
55 #define CLRASYNC(fd)    fcntl(fd, F_SETFL, handlers[fd].flag & ~FASYNC)
56
57 /*
58  * Request that ``func'' be called everytime data is available on ``fd''
59  */
60
61 static  fd_set  fdset;          /* File Descriptors to select on */
62
63 typedef struct {
64         void    (*func)(void *, REGISTERS);
65                                         /* Function to call on data arrival */
66         void    (*failure)(void *);     /* Function to call on failure */
67         void    *arg;                   /* Argument to above functions */
68         int     lockcnt;                /* Nested level of lock */
69         fd_set  members;                /* Set of FD's to disable on SIGIO */
70         int     flag;                   /* The flag from F_GETFL (we own it) */
71 } Async;
72
73 static  Async   handlers[OPEN_MAX];
74
75 static  void    HandleIO (struct sigframe *sf);
76
77 static  int     in_handler = 0;
78
79 void
80 _RegisterIO(fd, func, arg, failure)
81 int fd;
82 void (*func)();
83 void *arg;
84 void (*failure)();
85 {
86         static int firsttime = 1;
87         Async *as;
88
89         if (fd < 0 || fd > OPEN_MAX) {
90 printf("%d: Invalid FD\n", fd);
91                 return;
92         }
93
94         as = &handlers[fd];
95
96         if ((as->flag = fcntl(fd, F_GETFL, 0)) == -1) {
97                 if (func) {
98 /*@*/                   perror("get fcntl");
99 /*@*/                   abort();
100                         return;
101                 }
102         }
103
104         if (firsttime) {
105                 firsttime = 0;
106                 setsignal(SIGIO, HandleIO);
107         }
108
109         if ((handlers[fd].func = func) != 0) {
110                 as->lockcnt = 0;
111                 as->arg = arg;
112                 as->failure = failure;
113
114                 FD_SET(fd, &fdset);
115                 FD_ZERO(&handlers[fd].members);
116                 FD_SET(fd, &handlers[fd].members);
117                 if (fcntl(fd, F_SETOWN, getpid()) < 0) {
118 /*@*/                   perror("SETOWN");
119                 }
120                 SETASYNC(fd);
121         } else {
122                 as->arg = 0;
123                 as->failure = 0;
124                 as->lockcnt = 0;
125
126                 CLRASYNC(fd);
127                 FD_CLR(fd, &fdset);
128         }
129 }
130
131 static void
132 CleanIO()
133 {
134         int x;
135         static struct timeval tv = { 0 };
136
137         /*
138          * For every file des in fd_set, we check to see if it
139          * causes a fault on select().  If so, we unregister it
140          * for the user.
141          */
142         for (x = 0; x < OPEN_MAX; ++x) {
143                 fd_set set;
144
145                 if (!FD_ISSET(x, &fdset))
146                         continue;
147
148                 FD_ZERO(&set);
149                 FD_SET(x, &set);
150                 errno = 0;
151                 if (select(FD_SETSIZE, &set, 0, 0, &tv) < 0 &&
152                     errno == EBADF) {
153                         void (*f)();
154                         void *a;
155 printf("Closed file descriptor %d\n", x);
156
157                         f = handlers[x].failure;
158                         a = handlers[x].arg;
159                         handlers[x].failure = 0;
160                         handlers[x].func = 0;
161                         handlers[x].arg = 0;
162                         handlers[x].lockcnt = 0;
163                         FD_CLR(x, &fdset);
164                         if (f)
165                                 (*f)(a);
166                 }
167         }
168 }
169
170 static void
171 HandleIO(struct sigframe *sf)
172 {
173         ++in_handler;
174
175         for (;;) {
176                 static struct timeval tv = { 0 };
177                 fd_set readset;
178                 int x;
179                 int fd;
180
181                 readset = fdset;
182                 if ((x = select(FD_SETSIZE, &readset, 0, 0, &tv)) < 0) {
183                         /*
184                          * If we failed becuase of a BADFiledes, go find
185                          * which one(s), fail them out and then try a
186                          * new select to see if any of the good ones are
187                          * okay.
188                          */
189                         if (errno == EBADF) {
190                                 CleanIO();
191                                 if (FD_ISZERO(&fdset))
192                                         break;
193                                 continue;
194                         }
195                         perror("select");
196                         break;
197                 }
198
199                 /*
200                  * If we run out of fds to look at, break out of the loop
201                  * and exit the handler.
202                  */
203                 if (!x)
204                         break;
205
206                 /*
207                  * If there is at least 1 fd saying it has something for
208                  * us, then loop through the sets looking for those
209                  * bits, stopping when we have handleed the number it has
210                  * asked for.
211                  */
212                 for (fd = 0; x && fd < OPEN_MAX; ++fd) {
213                         Async *as;
214
215                         if (!FD_ISSET(fd, &readset)) {
216                                 continue;
217                         }
218                         --x;
219
220                         /*
221                          * Is suppose it is possible that one of the previous
222                          * io requests changed the fdset.
223                          * We do know that SIGIO is turned off right now,
224                          * so it is safe to checkit.
225                          */
226                         if (!FD_ISSET(fd, &fdset)) {
227                                 continue;
228                         }
229                         as = &handlers[fd];
230
231                         /*
232                          * as in above, maybe someone locked us...
233                          * we are in dangerous water now if we are
234                          * multi-tasked
235                          */
236                         if (as->lockcnt) {
237 /*@*/                   fprintf(stderr, "Selected IO on locked %d\n",fd);
238                                 continue;
239                         }
240                         /*
241                          * Okay, now if there exists a handler, we should
242                          * call it.  We must turn back on SIGIO if there
243                          * are possibly other people waiting for it.
244                          */
245                         if (as->func) {
246                                 int afd;
247                                 Async *aas;
248
249                                 /*
250                                  * STEP 1: Lock out all "members"
251                                  */
252                                 aas = handlers;
253 if (0)
254                                 for (afd = 0; afd < OPEN_MAX; ++afd, ++aas) {
255                                     if (FD_ISSET(afd, &as->members)) {
256                                         if (aas->func) {
257                                             if (as->lockcnt++ == 0) {
258                                                 FD_CLR(afd, &fdset);
259                                                 CLRASYNC(afd);
260                                             }
261                                         
262                                         }
263                                     }
264                                 }
265
266                                 /*
267                                  * STEP 2: Renable SIGIO so other FDs can
268                                  *         use a hit.
269                                 _UnblockIO();
270                                  */
271
272                                 /*
273                                  * STEP 3: Call the handler
274                                  */
275                                 (*handlers[fd].func)(handlers[fd].arg, 
276                                     (regcontext_t*)&sf->sf_uc);
277
278                                 /*
279                                  * STEP 4: Just turn SIGIO off.  No check.
280                                 _BlockIO();
281                                  */
282
283                                 /*
284                                  * STEP 5: Unlock all "members"
285                                  */
286                                 aas = handlers;
287 if (0)
288                                 for (afd = 0; afd < OPEN_MAX; ++afd, ++aas) {
289                                     if (FD_ISSET(afd, &as->members)) {
290                                         if (aas->func) {
291                                             if (--as->lockcnt == 0) {
292                                                 FD_SET(afd, &fdset);
293                                                 SETASYNC(afd);
294                                             }
295                                         }
296                                     }
297                                 }
298                         } else {
299                                 /*
300                                  * Otherwise deregister this guy.
301                                  */
302                                 _RegisterIO(fd, 0, 0, 0);
303                         }
304                 }
305                 /*
306                  * If we did not process all the fd's, then we should
307                  * break out of the probable infinite loop.
308                  */
309                 if (x) {
310                         break;
311                 }
312         }
313
314         --in_handler;
315 }
316
317 static int stackp = 0;
318
319 void
320 _BlockIO()
321 {
322         sigset_t set;
323
324         if (stackp >= 64) {
325                 fprintf(stderr, "Signal stack count too deep\n");
326                 abort();
327         }
328         if (stackp++ == 0) {
329                 sigaddset(&set, SIGIO);
330                 sigprocmask(SIG_BLOCK, &set, 0);
331         }
332 }
333
334 void
335 _UnblockIO()
336 {
337         sigset_t set;
338
339         if (stackp <= 0) {
340                 fprintf(stderr, "Negative signal stack count\n");
341                 abort();
342         }
343         if (--stackp == 0) {
344                 sigaddset(&set, SIGIO);
345                 sigprocmask(SIG_UNBLOCK, &set, 0);
346         }
347 }