/* **************************************************************************** * Copyright IBM Corporation 1988, 1989 - All Rights Reserved * * * * Permission to use, copy, modify, and distribute this software and its * * documentation for any purpose and without fee is hereby granted, * * provided that the above copyright notice appear in all copies and * * that both that copyright notice and this permission notice appear in * * supporting documentation, and that the name of IBM not be used in * * advertising or publicity pertaining to distribution of the software * * without specific, written prior permission. * * * * IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL IBM * * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY * * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER * * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * **************************************************************************** */ /*******************************************************************\ * * * Information Technology Center * * Carnegie-Mellon University * * * * * * * \*******************************************************************/ /* * IO Manager routines & server process for VICE server. */ #ifdef HAVE_CONFIG_H #include RCSID("$arla: iomgr.c,v 1.25 2002/06/01 17:47:47 lha Exp $"); #endif #include #include #include "lwp.h" #include #include "timer.h" #include #include #include #include #include "q.h" #include #include #include /* Prototypes */ static void SignalIO(int, fd_set *, fd_set *, fd_set *) ; static void SignalTimeout(int fds, struct timeval *timeout) ; static int SignalSignals (void); int FT_GetTimeOfDay(struct timeval *, struct timezone *); /********************************\ * * * Stuff for managing IoRequests * * * \********************************/ struct IoRequest { /* Pid of process making request (for use in IOMGR_Cancel) */ PROCESS pid; /* Descriptor masks for requests */ fd_set readfds; fd_set writefds; fd_set exceptfds; fd_set *rfds; fd_set *wfds; fd_set *efds; int nfds; struct TM_Elem timeout; /* Result of select call */ int result; struct IoRequest *next; }; /********************************\ * * * Stuff for managing signals * * * \********************************/ #define badsig(signo) (((signo) <= 0) || ((signo) >= NSIG)) #define mysigmask(signo) (1 << ((signo)-1)) static long openMask; /* mask of open files on an IOMGR abort */ static long sigsHandled; /* sigmask(signo) is on if we handle signo */ static int anySigsDelivered; /* true if any have been delivered. */ #ifdef AFS_POSIX_SIGNALS static struct sigaction oldVecs[NSIG]; /* the old signal vectors */ #else static struct sigvec oldVecs[NSIG]; /* the old signal vectors */ #endif static char *sigEvents[NSIG]; /* the event to do an LWP signal on */ static int sigDelivered[NSIG]; /* * True for signals delivered so far. * This is an int array to make sure * there are no conflicts when trying * to write it */ /* software 'signals' */ #define NSOFTSIG 4 static void (*sigProc[NSOFTSIG])(); static char *sigRock[NSOFTSIG]; static struct IoRequest *iorFreeList = 0; static struct TM_Elem *Requests; /* List of requests */ static struct timeval iomgr_timeout; /* global so signal handler can zap it */ /* stuff for debugging */ static int iomgr_errno; static struct timeval iomgr_badtv; static PROCESS iomgr_badpid; static void FreeRequest(struct IoRequest *req) { req->next = iorFreeList; iorFreeList = req; } /* stuff for handling select fd_sets */ #ifndef FD_COPY #define FD_COPY(f, t) memcpy((t), (f), sizeof(*(f))) #endif /* * FD_OR - bitwise or of two fd_sets * The result goes into set1 */ static void FD_OR(fd_set *set1, fd_set *set2) { int i; #ifdef FD_SPEED_HACK unsigned long *s1 = (unsigned long *)set1; unsigned long *s2 = (unsigned long *)set2; for (i = 0; i < sizeof(fd_set)/sizeof(unsigned long); i++) s1[i] |= s2[i]; #else for (i = 0; i < FD_SETSIZE; i++) if (FD_ISSET(i, set1) || FD_ISSET(i, set2)) FD_SET(i, set1); #endif } /* * FD_AND - bitwise and of two fd_sets * The result goes into set1 */ static void FD_AND(fd_set *set1, fd_set *set2) { int i; #ifdef FD_SPEED_HACK unsigned long *s1 = (unsigned long *)set1; unsigned long *s2 = (unsigned long *)set2; for(i = 0; i < sizeof(fd_set)/sizeof(unsigned long); i++) s1[i] &= s2[i]; #else for(i = 0; i < FD_SETSIZE; i++) if (FD_ISSET(i, set1) && FD_ISSET(i, set2)) FD_SET(i, set1); #endif } /* * FD_LOGAND - "logical" and of two fd_sets * returns 0 if there are no fds that are the same in both fd_sets * otherwise it returns 1 */ static int FD_LOGAND(fd_set *set1, fd_set *set2) { #ifdef FD_SPEED_HACK int i; unsigned long *s1 = (unsigned long *)set1; unsigned long *s2 = (unsigned long *)set2; for(i = 0; i < sizeof(fd_set)/sizeof(unsigned long); i++) if ((s1[i] & s2[i]) != 0) return 1; return 0; #else fd_set tmp; FD_COPY(set1, &tmp); FD_AND(&tmp, set2); return !FD_ISZERO(&tmp); #endif } static struct IoRequest * NewRequest(void) { struct IoRequest *request; if ((request = iorFreeList) != NULL) iorFreeList = request->next; else request = (struct IoRequest *) malloc(sizeof(struct IoRequest)); return request; } #define Purge(list) FOR_ALL_ELTS(req, list, { free(req->BackPointer); }) #define MAX_FDS 32 /* The IOMGR process */ /* * Important invariant: process->iomgrRequest is null iff request not in * timer queue also, request->pid is valid while request is in queue, also, * don't signal selector while request in queue, since selector free's request. */ static void IOMGR(char *dummy) { for (;;) { int fds; int nfds; fd_set readfds, writefds, exceptfds; fd_set *rfds, *wfds, *efds; struct TM_Elem *earliest; struct timeval timeout, junk; bool woke_someone; /* * Wake up anyone who has expired or who has received a * Unix signal between executions. Keep going until we * run out. */ do { woke_someone = FALSE; /* Wake up anyone waiting on signals. */ /* Note: SignalSignals() may yield! */ if (anySigsDelivered && SignalSignals ()) woke_someone = TRUE; TM_Rescan(Requests); for (;;) { struct IoRequest *req; struct TM_Elem *expired; expired = TM_GetExpired(Requests); if (expired == NULL) break; woke_someone = TRUE; req = (struct IoRequest *) expired -> BackPointer; #ifdef DEBUG if (lwp_debug != 0) puts("[Polling SELECT]"); #endif /* DEBUG */ FD_ZERO(&req->readfds); FD_ZERO(&req->writefds); FD_ZERO(&req->exceptfds); /* no data ready */ req->nfds = 0; req->rfds = req->wfds = req->efds = NULL; req->result = 0; /* no fds ready */ TM_Remove(Requests, &req->timeout); #ifdef DEBUG req -> timeout.Next = (struct TM_Elem *) 2; req -> timeout.Prev = (struct TM_Elem *) 2; #endif /* DEBUG */ LWP_QSignal(req->pid); req->pid->iomgrRequest = 0; } if (woke_someone) LWP_DispatchProcess(); } while (woke_someone); /* Collect requests & update times */ FD_ZERO(&readfds); /* XXX - should not be needed */ FD_ZERO(&writefds); FD_ZERO(&exceptfds); nfds = 0; rfds = wfds = efds = NULL; FOR_ALL_ELTS(r, Requests, { struct IoRequest *req; req = (struct IoRequest *) r -> BackPointer; if (req->rfds) { if (rfds) FD_OR(rfds, req->rfds); else { rfds = &readfds; FD_COPY(req->rfds, rfds); } } if (req->wfds) { if (wfds) FD_OR(wfds, req->wfds); else { wfds = &writefds; FD_COPY(req->wfds, wfds); } } if (req->efds) { if (efds) FD_OR(efds, req->efds); else { efds = &exceptfds; FD_COPY(req->efds, efds); } } nfds = max(nfds, req->nfds); }) earliest = TM_GetEarliest(Requests); if (earliest != NULL) { timeout = earliest -> TimeLeft; /* Do select */ #ifdef DEBUG if (lwp_debug != 0) { printf("[select(%d, %p, %p, %p, ", nfds, rfds, wfds, efds); if (timeout.tv_sec == -1 && timeout.tv_usec == -1) puts("INFINITE)]"); else printf("<%ld, %lu>)]\n", (long)timeout.tv_sec, (unsigned long)timeout.tv_usec); } #endif /* DEBUG */ iomgr_timeout = timeout; if (timeout.tv_sec == -1 && timeout.tv_usec == -1) { /* infinite, sort of */ iomgr_timeout.tv_sec = 100000000; iomgr_timeout.tv_usec = 0; } /* * Check one last time for a signal delivery. If one comes after * this, the signal handler will set iomgr_timeout to zero, * causing the select to return immediately. The timer package * won't return a zero timeval because all of those guys were * handled above. * * I'm assuming that the kernel masks signals while it's picking up * the parameters to select. This may a bad assumption. -DN */ if (anySigsDelivered) continue; /* go to the top and handle them. */ /* * select runs much faster if NULL's are passed instead of &0s */ fds = select(nfds, rfds, wfds, efds, &iomgr_timeout); /* * For SGI and SVR4 - poll & select can return EAGAIN ... */ if (fds < 0 && errno != EINTR && errno != EAGAIN && errno != ENOMEM) { iomgr_errno = errno; for(fds = 0; fds < FD_SETSIZE; fds++) { if (fcntl(fds, F_GETFD, 0) < 0 && errno == EBADF) openMask |= (1< 0) /* Action -- wake up everyone involved */ SignalIO(fds, rfds, wfds, efds); else if (fds == 0 && (iomgr_timeout.tv_sec != 0 || iomgr_timeout.tv_usec != 0)) /* Real timeout only if signal handler hasn't set iomgr_timeout to zero. */ SignalTimeout(fds, &timeout); } LWP_DispatchProcess(); } } /************************\ * * * Signalling routines * * * \************************/ static void SignalIO(int fds, fd_set *rfds, fd_set *wfds, fd_set *efds) { /* Look at everyone who's bit mask was affected */ FOR_ALL_ELTS(r, Requests, { struct IoRequest *req; PROCESS pid; int doit = 0; req = (struct IoRequest *) r -> BackPointer; if (rfds && req->rfds && FD_LOGAND(req->rfds, rfds)) { FD_AND(req->rfds, rfds); doit = 1; } if (wfds && req->wfds && FD_LOGAND(req->wfds, wfds)) { FD_AND(req->wfds, wfds); doit = 1; } if (efds && req->efds && FD_LOGAND(req->efds, efds)) { FD_AND(req->efds, efds); doit = 1; } if (doit) { req -> result = fds; TM_Remove(Requests, &req->timeout); LWP_QSignal(pid=req->pid); pid->iomgrRequest = 0; } }) } static void SignalTimeout(int fds, struct timeval *timeout) { /* Find everyone who has specified timeout */ FOR_ALL_ELTS(r, Requests, { struct IoRequest *req; PROCESS pid; req = (struct IoRequest *) r -> BackPointer; if (TM_eql(&r->TimeLeft, timeout)) { req -> result = fds; TM_Remove(Requests, &req->timeout); LWP_QSignal(pid=req->pid); pid->iomgrRequest = 0; } else return; }) } /*****************************************************\ * * * Signal handling routine (not to be confused with * * signalling routines, above). * * * \*****************************************************/ static RETSIGTYPE SigHandler (int signo) { if (badsig(signo) || (sigsHandled & mysigmask(signo)) == 0) return; /* can't happen. */ sigDelivered[signo] = TRUE; anySigsDelivered = TRUE; /* Make sure that the IOMGR process doesn't pause on the select. */ iomgr_timeout.tv_sec = 0; iomgr_timeout.tv_usec = 0; } /* Alright, this is the signal signalling routine. It delivers LWP signals to LWPs waiting on Unix signals. NOW ALSO CAN YIELD!! */ static int SignalSignals (void) { bool gotone = FALSE; int i; void (*p)(); long stackSize; anySigsDelivered = FALSE; /* handle software signals */ stackSize = (AFS_LWP_MINSTACKSIZE < lwp_MaxStackSeen? lwp_MaxStackSeen : AFS_LWP_MINSTACKSIZE); for (i=0; i < NSOFTSIG; i++) { PROCESS pid; if ((p = sigProc[i]) != NULL) /* This yields!!! */ LWP_CreateProcess(p, stackSize, LWP_NORMAL_PRIORITY, sigRock[i], "SignalHandler", &pid); sigProc[i] = 0; } for (i = 1; i <= NSIG; ++i) /* forall !badsig(i) */ if ((sigsHandled & mysigmask(i)) && sigDelivered[i] == TRUE) { sigDelivered[i] = FALSE; LWP_NoYieldSignal (sigEvents[i]); gotone = TRUE; } return gotone; } /***************************\ * * * User-callable routines * * * \***************************/ /* Keep IOMGR process id */ static PROCESS IOMGR_Id = NULL; int IOMGR_SoftSig(void (*aproc)(), char *arock) { int i; for (i=0;i BackPointer; #ifdef DEBUG if (lwp_debug != 0) puts("[Polling SELECT]"); #endif /* DEBUG */ /* no data ready */ FD_ZERO(&req->readfds); FD_ZERO(&req->writefds); FD_ZERO(&req->exceptfds); req->nfds = 0; req->rfds = req->wfds = req->efds = NULL; req->result = 0; /* no fds ready */ TM_Remove(Requests, &req->timeout); #ifdef DEBUG req -> timeout.Next = (struct TM_Elem *) 2; req -> timeout.Prev = (struct TM_Elem *) 2; #endif /* DEBUG */ LWP_QSignal(req->pid); req->pid->iomgrRequest = 0; } /* Collect requests & update times */ FD_ZERO(&readfds); /* XXX - should not be needed */ FD_ZERO(&writefds); FD_ZERO(&exceptfds); rfds = wfds = efds = NULL; nfds = 0; FOR_ALL_ELTS(r, Requests, { struct IoRequest *req; req = (struct IoRequest *) r -> BackPointer; if (req->rfds) { if (rfds) FD_OR(rfds, req->rfds); else { rfds = &readfds; FD_COPY(req->rfds, rfds); } } if (req->wfds) { if (wfds) FD_OR(wfds, req->wfds); else { wfds = &writefds; FD_COPY(req->wfds, wfds); } } if (req->efds) { if (efds) FD_OR(efds, req->efds); else { efds = &exceptfds; FD_COPY(req->efds, efds); } } nfds = max(nfds, req->nfds); }) tv.tv_sec = 0; tv.tv_usec = 0; code = select(nfds, rfds, wfds, efds, &tv); if (code > 0) { SignalIO(code, rfds, wfds, efds); } LWP_DispatchProcess(); /* make sure others run */ LWP_DispatchProcess(); return 0; } int IOMGR_Select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) { struct IoRequest *request; int result; /* See if polling request. If so, handle right here */ if (timeout != NULL) { if (timeout->tv_sec == 0 && timeout->tv_usec == 0) { int fds; #ifdef DEBUG if (lwp_debug != 0) puts("[Polling SELECT]"); #endif /* DEBUG */ do { timeout->tv_sec = 0; timeout->tv_usec = 0; fds = select(nfds, readfds, writefds, exceptfds, timeout); } while (fds < 0 && errno == EAGAIN); return (fds > 1 ? 1 : fds); } } /* Construct request block & insert */ request = NewRequest(); if (readfds == NULL) request->rfds = NULL; else { request->rfds = &request->readfds; FD_COPY(readfds, request->rfds); } if (writefds == NULL) request->wfds = NULL; else { request->wfds = &request->writefds; FD_COPY(writefds, request->wfds); } if (exceptfds == NULL) request->efds = NULL; else { request->efds = &request->exceptfds; FD_COPY(exceptfds, request->efds); } request->nfds = nfds; if (timeout == NULL) { request -> timeout.TotalTime.tv_sec = -1; request -> timeout.TotalTime.tv_usec = -1; } else { request -> timeout.TotalTime = *timeout; /* check for bad request */ if (timeout->tv_sec < 0 || timeout->tv_usec < 0 || timeout->tv_usec > 999999) { /* invalid arg */ iomgr_badtv = *timeout; iomgr_badpid = LWP_ActiveProcess; /* now fixup request */ if(request->timeout.TotalTime.tv_sec < 0) request->timeout.TotalTime.tv_sec = 1; request->timeout.TotalTime.tv_usec = 100000; } } request -> timeout.BackPointer = (char *) request; /* Insert my PID in case of IOMGR_Cancel */ request -> pid = LWP_ActiveProcess; LWP_ActiveProcess -> iomgrRequest = request; #ifdef DEBUG request -> timeout.Next = (struct TM_Elem *) 1; request -> timeout.Prev = (struct TM_Elem *) 1; #endif /* DEBUG */ TM_Insert(Requests, &request->timeout); /* Wait for action */ LWP_QWait(); /* Update parameters & return */ if (readfds != NULL) FD_COPY(&request->readfds, readfds); if (writefds != NULL) FD_COPY(&request->writefds, writefds); if (exceptfds != NULL) FD_COPY(&request->exceptfds, exceptfds); result = request -> result; FreeRequest(request); return (result > 1 ? 1 : result); } int IOMGR_Cancel(PROCESS pid) { struct IoRequest *request; if ((request = pid->iomgrRequest) == 0) return -1; /* Pid not found */ FD_ZERO(&request->readfds); FD_ZERO(&request->writefds); FD_ZERO(&request->exceptfds); request->rfds = request->wfds = request->efds = NULL; request->nfds = 0; request -> result = -2; TM_Remove(Requests, &request->timeout); #ifdef DEBUG request -> timeout.Next = (struct TM_Elem *) 5; request -> timeout.Prev = (struct TM_Elem *) 5; #endif /* DEBUG */ LWP_QSignal(request->pid); pid->iomgrRequest = 0; return 0; } /* * Cause delivery of signal signo to result in a LWP_SignalProcess of * event. */ int IOMGR_Signal (int signo, char *event) { #ifdef AFS_POSIX_SIGNALS struct sigaction sa; #else struct sigvec sv; #endif if (badsig(signo)) return LWP_EBADSIG; if (event == NULL) return LWP_EBADEVENT; #ifdef AFS_POSIX_SIGNALS sa.sa_handler = SigHandler; sigfillset(&sa.sa_mask); sa.sa_flags=0; #else sv.sv_handler = SigHandler; sv.sv_mask = ~0; /* mask all signals */ sv.sv_onstack = 0; #endif sigsHandled |= mysigmask(signo); sigEvents[signo] = event; sigDelivered[signo] = FALSE; #ifdef AFS_POSIX_SIGNALS if (sigaction (signo, &sa, &oldVecs[signo]) == -1) return LWP_ESYSTEM; #else if (sigvec (signo, &sv, &oldVecs[signo]) == -1) return LWP_ESYSTEM; #endif return LWP_SUCCESS; } /* Stop handling occurances of signo. */ int IOMGR_CancelSignal (int signo) { if (badsig(signo) || (sigsHandled & mysigmask(signo)) == 0) return LWP_EBADSIG; #ifdef AFS_POSIX_SIGNALS sigaction (signo, &oldVecs[signo], (struct sigaction *)0); #else sigvec (signo, &oldVecs[signo], (struct sigvec *)0); #endif sigsHandled &= ~mysigmask(signo); return LWP_SUCCESS; } /* * This routine calls select is a fashion that simulates the standard * sleep routine */ void IOMGR_Sleep (unsigned int seconds) { struct timeval timeout; timeout.tv_sec = seconds; timeout.tv_usec = 0; IOMGR_Select(0, NULL, NULL, NULL, &timeout); }