| /* | |
| * Generic async UNIX file IO handling | |
| * | |
| * Copyright 1996,1997 Alex Korobka | |
| * Copyright 1998 Marcus Meissner | |
| */ | |
| /* | |
| * This file handles asynchronous signaling for UNIX filedescriptors. | |
| * The passed handler gets called when input arrived for the filedescriptor. | |
| * | |
| * This is done either by the kernel or (in the WINSOCK case) by the pipe | |
| * handler, since pipes do not support asynchronous signaling. | |
| * (Not all possible filedescriptors support async IO. Generic files do not | |
| * for instance, sockets do, ptys don't.) | |
| * | |
| * To make this a bit better, we would need an additional thread doing select() | |
| */ | |
| #include <assert.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <unistd.h> | |
| #include <sys/types.h> | |
| #include <sys/time.h> | |
| #include <sys/stat.h> | |
| #include <fcntl.h> | |
| #ifdef HAVE_SYS_PARAM_H | |
| # include <sys/param.h> | |
| #endif | |
| #ifdef HAVE_SYS_FILIO_H | |
| # include <sys/filio.h> | |
| #endif | |
| #ifdef HAVE_SYS_FILE_H | |
| # include <sys/file.h> | |
| #endif | |
| #include "xmalloc.h" | |
| #include "windef.h" | |
| #include "miscemu.h" | |
| #include "selectors.h" | |
| #include "sig_context.h" | |
| #include "async.h" | |
| #include "debug.h" | |
| typedef struct _async_fd { | |
| int unixfd; | |
| void (*handler)(int fd,void *private); | |
| void *private; | |
| } ASYNC_FD; | |
| static ASYNC_FD *asyncfds = NULL; | |
| static int nrofasyncfds = 0; | |
| /*************************************************************************** | |
| * ASYNC_sigio [internal] | |
| * | |
| * Signal handler for asynchronous IO. | |
| * | |
| * Note: This handler and the function it calls may not block. Neither they | |
| * are allowed to use blocking IO (write/read). No memory management. | |
| * No possible blocking synchronization of any kind. | |
| */ | |
| HANDLER_DEF(ASYNC_sigio) { | |
| struct timeval timeout; | |
| fd_set rset,wset; | |
| int i,maxfd=0; | |
| HANDLER_INIT(); | |
| if (!nrofasyncfds) | |
| return; | |
| FD_ZERO(&rset); | |
| FD_ZERO(&wset); | |
| for (i=nrofasyncfds;i--;) { | |
| if (asyncfds[i].unixfd == -1) | |
| continue; | |
| FD_SET(asyncfds[i].unixfd,&rset); | |
| FD_SET(asyncfds[i].unixfd,&wset); | |
| if (maxfd<asyncfds[i].unixfd) | |
| maxfd=asyncfds[i].unixfd; | |
| } | |
| /* select() with timeout values set to 0 is nonblocking. */ | |
| memset(&timeout,0,sizeof(timeout)); | |
| if (select(maxfd+1,&rset,&wset,NULL,&timeout)<=0) | |
| return; /* Can't be. hmm */ | |
| for (i=nrofasyncfds;i--;) | |
| if ( (FD_ISSET(asyncfds[i].unixfd,&rset)) || | |
| (FD_ISSET(asyncfds[i].unixfd,&wset)) | |
| ) | |
| asyncfds[i].handler(asyncfds[i].unixfd,asyncfds[i].private); | |
| } | |
| /*************************************************************************** | |
| * ASYNC_MakeFDAsync [internal] | |
| * | |
| * Makes the passed filedescriptor async (or not) depending on flag. | |
| */ | |
| static BOOL ASYNC_MakeFDAsync(int unixfd,int async) { | |
| int flags; | |
| #if !defined(FASYNC) && defined(FIOASYNC) | |
| #define FASYNC FIOASYNC | |
| #endif | |
| #ifdef F_SETOWN | |
| if (-1==fcntl(unixfd,F_SETOWN,getpid())) | |
| perror("fcntl F_SETOWN <pid>"); | |
| #endif | |
| #ifdef FASYNC | |
| if (-1==fcntl(unixfd,F_GETFL,&flags)) { | |
| perror("fcntl F_GETFL"); | |
| return FALSE; | |
| } | |
| if (async) | |
| flags|=FASYNC; | |
| else | |
| flags&=~FASYNC; | |
| if (-1==fcntl(unixfd,F_SETFL,&flags)) { | |
| perror("fcntl F_SETFL FASYNC"); | |
| return FALSE; | |
| } | |
| return TRUE; | |
| #else | |
| return FALSE; | |
| #endif | |
| } | |
| /*************************************************************************** | |
| * ASYNC_RegisterFD [internal] | |
| * | |
| * Register a UNIX filedescriptor with handler and private data pointer. | |
| * this function is _NOT_ safe to be called from a signal handler. | |
| * | |
| * Additional Constraint: The handler passed to this function _MUST_ adhere | |
| * to the same signalsafeness as ASYNC_sigio itself. (nonblocking, no thread/ | |
| * signal unsafe operations, no blocking synchronization) | |
| */ | |
| void ASYNC_RegisterFD(int unixfd,void (*handler)(int fd,void *private),void *private) { | |
| int i; | |
| SIGNAL_MaskAsyncEvents( TRUE ); | |
| for (i=0;i<nrofasyncfds;i++) { | |
| if (asyncfds[i].unixfd==unixfd) { | |
| /* Might be a leftover entry. Make fd async anyway... */ | |
| if (asyncfds[i].handler==handler) { | |
| ASYNC_MakeFDAsync(unixfd,1); | |
| SIGNAL_MaskAsyncEvents( FALSE ); | |
| return; | |
| } | |
| } | |
| } | |
| for (i=0;i<nrofasyncfds;i++) | |
| if (asyncfds[i].unixfd == -1) | |
| break; | |
| if (i==nrofasyncfds) { | |
| if (nrofasyncfds) | |
| asyncfds=(ASYNC_FD*)xrealloc(asyncfds,sizeof(ASYNC_FD)*(nrofasyncfds+1)); | |
| else | |
| asyncfds=(ASYNC_FD*)xmalloc(sizeof(ASYNC_FD)*1); | |
| nrofasyncfds++; | |
| } | |
| asyncfds[i].unixfd = unixfd; | |
| asyncfds[i].handler = handler; | |
| asyncfds[i].private = private; | |
| ASYNC_MakeFDAsync(unixfd,1); | |
| SIGNAL_MaskAsyncEvents( FALSE ); | |
| } | |
| /*************************************************************************** | |
| * ASYNC_UnregisterFD [internal] | |
| * | |
| * Unregister a UNIX filedescriptor with handler. This function is basically | |
| * signal safe, but try to not call it in the signal handler anyway. | |
| */ | |
| void ASYNC_UnregisterFD(int unixfd,void (*handler)(int fd,void *private)) { | |
| int i; | |
| for (i=nrofasyncfds;i--;) | |
| if ((asyncfds[i].unixfd==unixfd)||(asyncfds[i].handler==handler)) | |
| break; | |
| if (i==nrofasyncfds) | |
| return; | |
| asyncfds[i].unixfd = -1; | |
| asyncfds[i].handler = NULL; | |
| asyncfds[i].private = NULL; | |
| return; | |
| } |