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