/* aio.c -- asynchronous file i/o * * Copyright (C) 1996-2006 by Ian Piumarta and other authors/contributors * listed elsewhere in this file. * All rights reserved. * * This file is part of Unix Squeak. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ /* Author: Ian.Piumarta@squeakland.org * * Last edited: 2006-10-18 10:07:06 by piumarta on emilia.local */ #include "sqaio.h" #ifdef HAVE_CONFIG_H # include "config.h" # ifdef HAVE_UNISTD_H # include # include # endif /* HAVE_UNISTD_H */ # ifdef NEED_GETHOSTNAME_P extern int gethostname(); # endif # include # include # include # include # include # include # ifdef HAVE_SYS_TIME_H # include # else # include # endif # ifdef HAS_SYS_SELECT_H # include # endif # ifndef FIONBIO # ifdef HAVE_SYS_FILIO_H # include # endif # ifndef FIONBIO # ifdef FIOSNBIO # define FIONBIO FIOSNBIO # else # error: FIONBIO is not defined # endif # endif # endif # if !defined(O_NONBLOCK) && defined(O_NDELAY) # define O_NONBLOCK O_NDELAY # endif # if !defined(FASYNC) && defined(O_ASYNC) # define FASYNC O_ASYNC # endif #else /* !HAVE_CONFIG_H -- assume lowest common demoninator */ # include # include # include # include # include # include # include # include # include # include #endif #undef DEBUG #undef DEBUG_TICKER #if defined(DEBUG) int aioLastTick= 0; int aioThisTick= 0; # define FPRINTF(X) { aioThisTick= ioLowResMSecs(); fprintf(stderr, "%8d %8d ", aioThisTick, aioThisTick - aioLastTick); aioLastTick= aioThisTick; fprintf X; } #else # define FPRINTF(X) #endif #if defined(DEBUG_TICKER) static char *ticks= "-\\|/"; static char *ticker= ""; #define DO_TICK() \ { \ fprintf(stderr, "\r%c\r", *ticker); \ if (!*ticker++) ticker= ticks; \ } #else # define DO_TICK() #endif #define _DO_FLAG_TYPE() _DO(AIO_R, rd) _DO(AIO_W, wr) _DO(AIO_X, ex) static int one= 1; static aioHandler rdHandler[FD_SETSIZE]; static aioHandler wrHandler[FD_SETSIZE]; static aioHandler exHandler[FD_SETSIZE]; static void *clientData[FD_SETSIZE]; static int maxFd; static fd_set fdMask; /* handled by aio */ static fd_set rdMask; /* handle read */ static fd_set wrMask; /* handle write */ static fd_set exMask; /* handle exception */ static fd_set xdMask; /* external descriptor */ static void undefinedHandler(int fd, void *clientData, int flags) { fprintf(stderr, "undefined handler called (fd %d, flags %x)\n", fd, flags); } #ifdef DEBUG static char *handlerName(aioHandler h) { if (h == undefinedHandler) return "undefinedHandler"; #ifdef DEBUG_SOCKETS { extern char *socketHandlerName(aioHandler); return socketHandlerName(h); } #endif return "***unknown***"; } #endif /* initialise asynchronous i/o */ void aioInit(void) { extern void forceInterruptCheck(int); /* not really, but hey */ FD_ZERO(&fdMask); FD_ZERO(&rdMask); FD_ZERO(&wrMask); FD_ZERO(&exMask); FD_ZERO(&xdMask); maxFd= 0; signal(SIGPIPE, SIG_IGN); signal(SIGIO, forceInterruptCheck); } /* disable handlers and close all handled non-exteral descriptors */ void aioFini(void) { int fd; for (fd= 0; fd < maxFd; fd++) if (FD_ISSET(fd, &fdMask) && !(FD_ISSET(fd, &xdMask))) { aioDisable(fd); close(fd); FD_CLR(fd, &fdMask); FD_CLR(fd, &rdMask); FD_CLR(fd, &wrMask); FD_CLR(fd, &exMask); } while (maxFd && !FD_ISSET(maxFd - 1, &fdMask)) --maxFd; signal(SIGPIPE, SIG_DFL); } /* answer whether i/o becomes possible within the given number of microSeconds */ int aioPoll(int microSeconds) { int fd, ms; fd_set rd, wr, ex; FPRINTF((stderr, "aioPoll(%d)\n", microSeconds)); DO_TICK(); /* get out early if there is no pending i/o and no need to relinquish cpu */ if ((maxFd == 0) && (microSeconds == 0)) return 0; rd= rdMask; wr= wrMask; ex= exMask; ms= ioMSecs(); for (;;) { struct timeval tv; int n, now; tv.tv_sec= microSeconds / 1000000; tv.tv_usec= microSeconds % 1000000; n= select(maxFd, &rd, &wr, &ex, &tv); if (n > 0) break; if (n == 0) return 0; if (errno && (EINTR != errno)) { fprintf(stderr, "errno %d\n", errno); perror("select"); return 0; } now= ioMSecs(); microSeconds -= (now - ms) * 1000; if (microSeconds <= 0) return 0; ms= now; } for (fd= 0; fd < maxFd; ++fd) { # define _DO(FLAG, TYPE) \ { \ if (FD_ISSET(fd, &TYPE)) \ { \ aioHandler handler= TYPE##Handler[fd]; \ FD_CLR(fd, &TYPE##Mask); \ TYPE##Handler[fd]= undefinedHandler; \ handler(fd, clientData[fd], FLAG); \ } \ } _DO_FLAG_TYPE(); # undef _DO } return 1; } /* sleep for microSeconds or until i/o becomes possible, avoiding sleeping in select() is timeout too small */ int aioSleep(int microSeconds) { #if defined(HAVE_NANOSLEEP) if (microSeconds < (1000000/60)) /* < 1 timeslice? */ { if (!aioPoll(0)) { struct timespec rqtp= { 0, microSeconds * 1000 }; struct timespec rmtp; nanosleep(&rqtp, &rmtp); microSeconds= 0; /* poll but don't block */ } } #endif return aioPoll(microSeconds); } /* enable asynchronous notification for a descriptor */ void aioEnable(int fd, void *data, int flags) { FPRINTF((stderr, "aioEnable(%d)\n", fd)); if (fd < 0) { FPRINTF((stderr, "aioEnable(%d): IGNORED\n", fd)); return; } if (FD_ISSET(fd, &fdMask)) { fprintf(stderr, "aioEnable: descriptor %d already enabled\n", fd); return; } clientData[fd]= data; rdHandler[fd]= wrHandler[fd]= exHandler[fd]= undefinedHandler; FD_SET(fd, &fdMask); FD_CLR(fd, &rdMask); FD_CLR(fd, &wrMask); FD_CLR(fd, &exMask); if (fd >= maxFd) maxFd= fd + 1; if (flags & AIO_EXT) { FD_SET(fd, &xdMask); /* we should not set NBIO ourselves on external descriptors! */ } else { /* enable non-blocking asynchronous i/o and delivery of SIGIO to the active process */ int arg; FD_CLR(fd, &xdMask); # if defined(O_ASYNC) if ( fcntl(fd, F_SETOWN, getpid() ) < 0) perror("fcntl(F_SETOWN, getpid())"); if ((arg= fcntl(fd, F_GETFL, 0 )) < 0) perror("fcntl(F_GETFL)"); if ( fcntl(fd, F_SETFL, arg | O_NONBLOCK | O_ASYNC) < 0) perror("fcntl(F_SETFL, O_ASYNC)"); # elif defined(FASYNC) if ( fcntl(fd, F_SETOWN, getpid() ) < 0) perror("fcntl(F_SETOWN, getpid())"); if ((arg= fcntl(fd, F_GETFL, 0 )) < 0) perror("fcntl(F_GETFL)"); if ( fcntl(fd, F_SETFL, arg | O_NONBLOCK | FASYNC ) < 0) perror("fcntl(F_SETFL, FASYNC)"); # elif defined(FIOASYNC) arg= getpid(); if (ioctl(fd, SIOCSPGRP, &arg) < 0) perror("ioctl(SIOCSPGRP, getpid())"); arg= 1; if (ioctl(fd, FIOASYNC, &arg) < 0) perror("ioctl(FIOASYNC, 1)"); # endif } } /* install/change the handler for a descriptor */ void aioHandle(int fd, aioHandler handlerFn, int mask) { FPRINTF((stderr, "aioHandle(%d, %s, %d)\n", fd, handlerName(handlerFn), mask)); if (fd < 0) { FPRINTF((stderr, "aioHandle(%d): IGNORED\n", fd)); return; } # define _DO(FLAG, TYPE) \ if (mask & FLAG) { \ FD_SET(fd, &TYPE##Mask); \ TYPE##Handler[fd]= handlerFn; \ } _DO_FLAG_TYPE(); # undef _DO } /* temporarily suspend asynchronous notification for a descriptor */ void aioSuspend(int fd, int mask) { if (fd < 0) { FPRINTF((stderr, "aioSuspend(%d): IGNORED\n", fd)); return; } FPRINTF((stderr, "aioSuspend(%d)\n", fd)); # define _DO(FLAG, TYPE) \ { \ if (mask & FLAG) \ { \ FD_CLR(fd, &TYPE##Mask); \ TYPE##Handler[fd]= undefinedHandler; \ } \ } _DO_FLAG_TYPE(); # undef _DO } /* definitively disable asynchronous notification for a descriptor */ void aioDisable(int fd) { if (fd < 0) { FPRINTF((stderr, "aioDisable(%d): IGNORED\n", fd)); return; } FPRINTF((stderr, "aioDisable(%d)\n", fd)); aioSuspend(fd, AIO_RWX); FD_CLR(fd, &xdMask); FD_CLR(fd, &fdMask); rdHandler[fd]= wrHandler[fd]= exHandler[fd]= 0; clientData[fd]= 0; /* keep maxFd accurate (drops to zero if no more sockets) */ while (maxFd && !FD_ISSET(maxFd - 1, &fdMask)) --maxFd; }