1 /* aio.c -- asynchronous file i/o
2  *
3  *   Copyright (C) 1996-2006 by Ian Piumarta and other authors/contributors
4  *                              listed elsewhere in this file.
5  *   All rights reserved.
6  *
7  *   This file is part of Unix Squeak.
8  *
9  *   Permission is hereby granted, free of charge, to any person obtaining a copy
10  *   of this software and associated documentation files (the "Software"), to deal
11  *   in the Software without restriction, including without limitation the rights
12  *   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13  *   copies of the Software, and to permit persons to whom the Software is
14  *   furnished to do so, subject to the following conditions:
15  *
16  *   The above copyright notice and this permission notice shall be included in
17  *   all copies or substantial portions of the Software.
18  *
19  *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20  *   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21  *   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22  *   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23  *   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24  *   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25  *   SOFTWARE.
26  */
27 
28 /* Author: Ian.Piumarta@squeakland.org
29  *
30  * Last edited: 2006-10-18 10:07:06 by piumarta on emilia.local
31  */
32 
33 #include "sqaio.h"
34 
35 #ifdef HAVE_CONFIG_H
36 
37 # include "config.h"
38 
39 # ifdef HAVE_UNISTD_H
40 #   include <sys/types.h>
41 #   include <unistd.h>
42 # endif /* HAVE_UNISTD_H */
43 
44 # ifdef NEED_GETHOSTNAME_P
45     extern int gethostname();
46 # endif
47 
48 # include <stdio.h>
49 # include <signal.h>
50 # include <errno.h>
51 # include <fcntl.h>
52 # include <sys/file.h>
53 # include <sys/ioctl.h>
54 
55 # ifdef HAVE_SYS_TIME_H
56 #   include <sys/time.h>
57 # else
58 #   include <time.h>
59 # endif
60 
61 # ifdef HAS_SYS_SELECT_H
62 #   include <sys/select.h>
63 # endif
64 
65 # ifndef FIONBIO
66 #   ifdef HAVE_SYS_FILIO_H
67 #     include <sys/filio.h>
68 #   endif
69 #   ifndef FIONBIO
70 #     ifdef FIOSNBIO
71 #       define FIONBIO FIOSNBIO
72 #     else
73 #       error: FIONBIO is not defined
74 #     endif
75 #   endif
76 # endif
77 
78 # if !defined(O_NONBLOCK) && defined(O_NDELAY)
79 #   define O_NONBLOCK O_NDELAY
80 # endif
81 # if !defined(FASYNC) && defined(O_ASYNC)
82 #   define FASYNC O_ASYNC
83 # endif
84 
85 #else /* !HAVE_CONFIG_H -- assume lowest common demoninator */
86 
87 # include <stdio.h>
88 # include <stdlib.h>
89 # include <unistd.h>
90 # include <errno.h>
91 # include <signal.h>
92 # include <sys/types.h>
93 # include <sys/time.h>
94 # include <sys/select.h>
95 # include <sys/ioctl.h>
96 # include <fcntl.h>
97 
98 #endif
99 
100 
101 #undef	DEBUG
102 #undef	DEBUG_TICKER
103 
104 #if defined(DEBUG)
105   int aioLastTick= 0;
106   int aioThisTick= 0;
107 # define FPRINTF(X) { aioThisTick= ioLowResMSecs();  fprintf(stderr, "%8d %8d ", aioThisTick, aioThisTick - aioLastTick);  aioLastTick= aioThisTick;  fprintf X; }
108 #else
109 # define FPRINTF(X)
110 #endif
111 
112 #if defined(DEBUG_TICKER)
113   static char *ticks= "-\\|/";
114   static char *ticker= "";
115   #define DO_TICK()				\
116   {						\
117     fprintf(stderr, "\r%c\r", *ticker);		\
118     if (!*ticker++) ticker= ticks;		\
119   }
120 #else
121 # define DO_TICK()
122 #endif
123 
124 #define _DO_FLAG_TYPE()	_DO(AIO_R, rd) _DO(AIO_W, wr) _DO(AIO_X, ex)
125 
126 static int one= 1;
127 
128 static aioHandler  rdHandler[FD_SETSIZE];
129 static aioHandler  wrHandler[FD_SETSIZE];
130 static aioHandler  exHandler[FD_SETSIZE];
131 
132 static void       *clientData[FD_SETSIZE];
133 
134 static int	maxFd;
135 static fd_set	fdMask;	/* handled by aio	*/
136 static fd_set	rdMask; /* handle read		*/
137 static fd_set	wrMask; /* handle write		*/
138 static fd_set	exMask; /* handle exception	*/
139 static fd_set	xdMask; /* external descriptor	*/
140 
141 
undefinedHandler(int fd,void * clientData,int flags)142 static void undefinedHandler(int fd, void *clientData, int flags)
143 {
144   fprintf(stderr, "undefined handler called (fd %d, flags %x)\n", fd, flags);
145 }
146 
147 #ifdef DEBUG
handlerName(aioHandler h)148 static char *handlerName(aioHandler h)
149 {
150   if (h == undefinedHandler) return "undefinedHandler";
151 #ifdef DEBUG_SOCKETS
152  {
153    extern char *socketHandlerName(aioHandler);
154    return socketHandlerName(h);
155  }
156 #endif
157  return "***unknown***";
158 }
159 #endif
160 
161 /* initialise asynchronous i/o */
162 
aioInit(void)163 void aioInit(void)
164 {
165   extern void forceInterruptCheck(int);	/* not really, but hey */
166 
167   FD_ZERO(&fdMask);
168   FD_ZERO(&rdMask);
169   FD_ZERO(&wrMask);
170   FD_ZERO(&exMask);
171   FD_ZERO(&xdMask);
172   maxFd= 0;
173   signal(SIGPIPE, SIG_IGN);
174   signal(SIGIO,   forceInterruptCheck);
175 }
176 
177 
178 /* disable handlers and close all handled non-exteral descriptors */
179 
aioFini(void)180 void aioFini(void)
181 {
182   int fd;
183   for (fd= 0;  fd < maxFd;  fd++)
184     if (FD_ISSET(fd, &fdMask) && !(FD_ISSET(fd, &xdMask)))
185       {
186 	aioDisable(fd);
187 	close(fd);
188 	FD_CLR(fd, &fdMask);
189 	FD_CLR(fd, &rdMask);
190 	FD_CLR(fd, &wrMask);
191 	FD_CLR(fd, &exMask);
192       }
193   while (maxFd && !FD_ISSET(maxFd - 1, &fdMask))
194     --maxFd;
195   signal(SIGPIPE, SIG_DFL);
196 }
197 
198 
199 /* answer whether i/o becomes possible within the given number of microSeconds */
200 
aioPoll(int microSeconds)201 int aioPoll(int microSeconds)
202 {
203   int	 fd, ms;
204   fd_set rd, wr, ex;
205 
206   FPRINTF((stderr, "aioPoll(%d)\n", microSeconds));
207   DO_TICK();
208 
209   /* get out early if there is no pending i/o and no need to relinquish cpu */
210 
211   if ((maxFd == 0) && (microSeconds == 0))
212     return 0;
213 
214   rd= rdMask;
215   wr= wrMask;
216   ex= exMask;
217   ms= ioMSecs();
218 
219   for (;;)
220     {
221       struct timeval tv;
222       int n, now;
223       tv.tv_sec=  microSeconds / 1000000;
224       tv.tv_usec= microSeconds % 1000000;
225       n= select(maxFd, &rd, &wr, &ex, &tv);
226       if (n  > 0) break;
227       if (n == 0) return 0;
228       if (errno && (EINTR != errno))
229 	{
230 	  fprintf(stderr, "errno %d\n", errno);
231 	  perror("select");
232 	  return 0;
233 	}
234       now= ioMSecs();
235       microSeconds -= (now - ms) * 1000;
236       if (microSeconds <= 0)
237 	return 0;
238       ms= now;
239     }
240 
241   for (fd= 0; fd < maxFd; ++fd)
242     {
243 #     define _DO(FLAG, TYPE)				\
244       {							\
245 	if (FD_ISSET(fd, &TYPE))			\
246 	  {						\
247 	    aioHandler handler= TYPE##Handler[fd];	\
248 	    FD_CLR(fd, &TYPE##Mask);			\
249 	    TYPE##Handler[fd]= undefinedHandler;	\
250 	    handler(fd, clientData[fd], FLAG);		\
251 	  }						\
252       }
253       _DO_FLAG_TYPE();
254 #     undef _DO
255     }
256   return 1;
257 }
258 
259 
260 /* sleep for microSeconds or until i/o becomes possible, avoiding
261    sleeping in select() is timeout too small */
262 
aioSleep(int microSeconds)263 int aioSleep(int microSeconds)
264 {
265 #if defined(HAVE_NANOSLEEP)
266   if (microSeconds < (1000000/60))	/* < 1 timeslice? */
267     {
268       if (!aioPoll(0))
269 	{
270 	  struct timespec rqtp= { 0, microSeconds * 1000 };
271 	  struct timespec rmtp;
272 	  nanosleep(&rqtp, &rmtp);
273 	  microSeconds= 0;			/* poll but don't block */
274 	}
275     }
276 #endif
277   return aioPoll(microSeconds);
278 }
279 
280 
281 /* enable asynchronous notification for a descriptor */
282 
aioEnable(int fd,void * data,int flags)283 void aioEnable(int fd, void *data, int flags)
284 {
285   FPRINTF((stderr, "aioEnable(%d)\n", fd));
286   if (fd < 0)
287     {
288       FPRINTF((stderr, "aioEnable(%d): IGNORED\n", fd));
289       return;
290     }
291   if (FD_ISSET(fd, &fdMask))
292     {
293       fprintf(stderr, "aioEnable: descriptor %d already enabled\n", fd);
294       return;
295     }
296   clientData[fd]= data;
297   rdHandler[fd]= wrHandler[fd]= exHandler[fd]= undefinedHandler;
298   FD_SET(fd, &fdMask);
299   FD_CLR(fd, &rdMask);
300   FD_CLR(fd, &wrMask);
301   FD_CLR(fd, &exMask);
302   if (fd >= maxFd)
303     maxFd= fd + 1;
304   if (flags & AIO_EXT)
305     {
306       FD_SET(fd, &xdMask);
307       /* we should not set NBIO ourselves on external descriptors! */
308     }
309   else
310     {
311       /* enable non-blocking asynchronous i/o and delivery of SIGIO to the active process */
312       int arg;
313       FD_CLR(fd, &xdMask);
314 #    if defined(O_ASYNC)
315       if (      fcntl(fd, F_SETOWN, getpid()                  )  < 0)	perror("fcntl(F_SETOWN, getpid())");
316       if ((arg= fcntl(fd, F_GETFL,  0                         )) < 0)	perror("fcntl(F_GETFL)");
317       if (      fcntl(fd, F_SETFL,  arg | O_NONBLOCK | O_ASYNC)  < 0)	perror("fcntl(F_SETFL, O_ASYNC)");
318 #    elif defined(FASYNC)
319       if (      fcntl(fd, F_SETOWN, getpid()                  )  < 0)	perror("fcntl(F_SETOWN, getpid())");
320       if ((arg= fcntl(fd, F_GETFL,  0                         )) < 0)	perror("fcntl(F_GETFL)");
321       if (      fcntl(fd, F_SETFL,  arg | O_NONBLOCK | FASYNC )  < 0)	perror("fcntl(F_SETFL, FASYNC)");
322 #    elif defined(FIOASYNC)
323       arg= getpid();	if (ioctl(fd, SIOCSPGRP, &arg) < 0)		perror("ioctl(SIOCSPGRP, getpid())");
324       arg= 1;		if (ioctl(fd, FIOASYNC,  &arg) < 0)		perror("ioctl(FIOASYNC, 1)");
325 #    endif
326     }
327 }
328 
329 
330 /* install/change the handler for a descriptor */
331 
aioHandle(int fd,aioHandler handlerFn,int mask)332 void aioHandle(int fd, aioHandler handlerFn, int mask)
333 {
334   FPRINTF((stderr, "aioHandle(%d, %s, %d)\n", fd, handlerName(handlerFn), mask));
335   if (fd < 0)
336     {
337       FPRINTF((stderr, "aioHandle(%d): IGNORED\n", fd));
338       return;
339     }
340 # define _DO(FLAG, TYPE)			\
341     if (mask & FLAG) {				\
342       FD_SET(fd, &TYPE##Mask);			\
343       TYPE##Handler[fd]= handlerFn;		\
344     }
345   _DO_FLAG_TYPE();
346 # undef _DO
347 }
348 
349 
350 /* temporarily suspend asynchronous notification for a descriptor */
351 
aioSuspend(int fd,int mask)352 void aioSuspend(int fd, int mask)
353 {
354   if (fd < 0)
355     {
356       FPRINTF((stderr, "aioSuspend(%d): IGNORED\n", fd));
357       return;
358     }
359   FPRINTF((stderr, "aioSuspend(%d)\n", fd));
360 # define _DO(FLAG, TYPE)			\
361   {						\
362     if (mask & FLAG)				\
363       {						\
364 	FD_CLR(fd, &TYPE##Mask);		\
365 	TYPE##Handler[fd]= undefinedHandler;	\
366       }						\
367   }
368   _DO_FLAG_TYPE();
369 # undef _DO
370 }
371 
372 
373 /* definitively disable asynchronous notification for a descriptor */
374 
aioDisable(int fd)375 void aioDisable(int fd)
376 {
377   if (fd < 0)
378     {
379       FPRINTF((stderr, "aioDisable(%d): IGNORED\n", fd));
380       return;
381     }
382   FPRINTF((stderr, "aioDisable(%d)\n", fd));
383   aioSuspend(fd, AIO_RWX);
384   FD_CLR(fd, &xdMask);
385   FD_CLR(fd, &fdMask);
386   rdHandler[fd]= wrHandler[fd]= exHandler[fd]= 0;
387   clientData[fd]= 0;
388   /* keep maxFd accurate (drops to zero if no more sockets) */
389   while (maxFd && !FD_ISSET(maxFd - 1, &fdMask))
390     --maxFd;
391 }
392