| /* |
| * 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; |
| } |