/*1* tclUnixNotify.c --2*3* This file contains Unix-specific procedures for the notifier,4* which is the lowest-level part of the Tcl event loop. This file5* works together with ../generic/tclNotify.c.6*7* Copyright (c) 1995 Sun Microsystems, Inc.8*9* See the file "license.terms" for information on usage and redistribution10* of this file, and for a DISCLAIMER OF ALL WARRANTIES.11*12* SCCS: @(#) tclUnixNotfy.c 1.31 96/07/23 16:17:2913*/1415#include "tclInt.h"16#include "tclPort.h"17#include <signal.h>1819#ifdef WIN3220#undef _DLL21#undef VOID22#include <windows.h>23#define _DLL 124#endif2526#if HAVE_SYS_SELECT_H && !defined(NFDBITS)27#include <sys/select.h>28#endif2930/*31* The information below is used to provide read, write, and32* exception masks to select during calls to Tcl_DoOneEvent.33*/3435static fd_mask checkMasks[3*MASK_SIZE];36/* This array is used to build up the masks37* to be used in the next call to select.38* Bits are set in response to calls to39* Tcl_WatchFile. */40static fd_mask readyMasks[3*MASK_SIZE];41/* This array reflects the readable/writable42* conditions that were found to exist by the43* last call to select. */44static int numFdBits; /* Number of valid bits in checkMasks45* (one more than highest fd for which46* Tcl_WatchFile has been called). */4748#if 149static int eventsFound;50static int ignoreEvents;51#endif5253/*54* Static routines in this file:55*/5657static int MaskEmpty _ANSI_ARGS_((long *maskPtr));5859extern void TclWinFlushEvents()60{61ignoreEvents = 1;62while (Tcl_DoOneEvent(TCL_DONT_WAIT|TCL_WINDOW_EVENTS|TCL_IDLE_EVENTS)) {63}64ignoreEvents = 0;65}6667/*68*----------------------------------------------------------------------69*70* Tcl_WatchFile --71*72* Arrange for Tcl_DoOneEvent to include this file in the masks73* for the next call to select. This procedure is invoked by74* event sources, which are in turn invoked by Tcl_DoOneEvent75* before it invokes select.76*77* Results:78* None.79*80* Side effects:81*82* The notifier will generate a file event when the I/O channel83* given by fd next becomes ready in the way indicated by mask.84* If fd is already registered then the old mask will be replaced85* with the new one. Once the event is sent, the notifier will86* not send any more events about the fd until the next call to87* Tcl_NotifyFile.88*89*----------------------------------------------------------------------90*/9192void93Tcl_WatchFile(file, mask)94Tcl_File file; /* Generic file handle for a stream. */95int mask; /* OR'ed combination of TCL_READABLE,96* TCL_WRITABLE, and TCL_EXCEPTION:97* indicates conditions to wait for98* in select. */99{100int fd, type, index;101fd_mask bit;102103fd = (int) Tcl_GetFileInfo(file, &type);104105if (type != TCL_UNIX_FD) {106panic("Tcl_WatchFile: unexpected file type");107}108109if (fd >= FD_SETSIZE) {110panic("Tcl_WatchFile can't handle file id %d", fd);111}112113index = fd/(NBBY*sizeof(fd_mask));114bit = 1 << (fd%(NBBY*sizeof(fd_mask)));115if (mask & TCL_READABLE) {116checkMasks[index] |= bit;117}118if (mask & TCL_WRITABLE) {119(checkMasks+MASK_SIZE)[index] |= bit;120}121if (mask & TCL_EXCEPTION) {122(checkMasks+2*(MASK_SIZE))[index] |= bit;123}124if (numFdBits <= fd) {125numFdBits = fd+1;126}127}128129#if 1130extern int Tcl_NumEventsFound()131{132return eventsFound;133}134#endif135136/*137*----------------------------------------------------------------------138*139* Tcl_FileReady --140*141* Indicates what conditions (readable, writable, etc.) were142* present on a file the last time the notifier invoked select.143* This procedure is typically invoked by event sources to see144* if they should queue events.145*146* Results:147* The return value is 0 if none of the conditions specified by mask148* was true for fd the last time the system checked. If any of the149* conditions were true, then the return value is a mask of those150* that were true.151*152* Side effects:153* None.154*155*----------------------------------------------------------------------156*/157158int159Tcl_FileReady(file, mask)160Tcl_File file; /* Generic file handle for a stream. */161int mask; /* OR'ed combination of TCL_READABLE,162* TCL_WRITABLE, and TCL_EXCEPTION:163* indicates conditions caller cares about. */164{165int index, result, type, fd;166fd_mask bit;167168fd = (int) Tcl_GetFileInfo(file, &type);169if (type != TCL_UNIX_FD) {170panic("Tcl_FileReady: unexpected file type");171}172173index = fd/(NBBY*sizeof(fd_mask));174bit = 1 << (fd%(NBBY*sizeof(fd_mask)));175result = 0;176if ((mask & TCL_READABLE) && (readyMasks[index] & bit)) {177result |= TCL_READABLE;178}179if ((mask & TCL_WRITABLE) && ((readyMasks+MASK_SIZE)[index] & bit)) {180result |= TCL_WRITABLE;181}182if ((mask & TCL_EXCEPTION) && ((readyMasks+(2*MASK_SIZE))[index] & bit)) {183result |= TCL_EXCEPTION;184}185return result;186}187188/*189*----------------------------------------------------------------------190*191* MaskEmpty --192*193* Returns nonzero if mask is empty (has no bits set).194*195* Results:196* Nonzero if the mask is empty, zero otherwise.197*198* Side effects:199* None200*201*----------------------------------------------------------------------202*/203204static int205MaskEmpty(maskPtr)206long *maskPtr;207{208long *runPtr, *tailPtr;209int found, sz;210211sz = 3 * ((MASK_SIZE) / sizeof(long)) * sizeof(fd_mask);212for (runPtr = maskPtr, tailPtr = maskPtr + sz, found = 0;213runPtr < tailPtr;214runPtr++) {215if (*runPtr != 0) {216found = 1;217break;218}219}220return !found;221}222223/*224*----------------------------------------------------------------------225*226* Tcl_WaitForEvent --227*228* This procedure does the lowest level wait for events in a229* platform-specific manner. It uses information provided by230* previous calls to Tcl_WatchFile, plus the timePtr argument,231* to determine what to wait for and how long to wait.232*233* Results:234* The return value is normally TCL_OK. However, if there are235* no events to wait for (e.g. no files and no timers) so that236* the procedure would block forever, then it returns TCL_ERROR.237*238* Side effects:239* May put the process to sleep for a while, depending on timePtr.240* When this procedure returns, an event of interest to the application241* has probably, but not necessarily, occurred.242*243*----------------------------------------------------------------------244*/245246int247Tcl_WaitForEvent(timePtr)248Tcl_Time *timePtr; /* Specifies the maximum amount of time249* that this procedure should block before250* returning. The time is given as an251* interval, not an absolute wakeup time.252* NULL means block forever. */253{254struct timeval timeout, *timeoutPtr;255int numFound;256#ifdef WIN32257static int winFd = -1;258if (ignoreEvents)259return TCL_OK;260if (winFd < 0)261winFd = open("/dev/windows", 0);262Tcl_WatchFile(Tcl_GetFile((ClientData)winFd,TCL_UNIX_FD),TCL_READABLE);263#endif264265memcpy((VOID *) readyMasks, (VOID *) checkMasks,2663*MASK_SIZE*sizeof(fd_mask));267if (timePtr == NULL) {268if ((numFdBits == 0) || (MaskEmpty((long *) readyMasks))) {269return TCL_ERROR;270}271timeoutPtr = NULL;272} else {273timeoutPtr = &timeout;274timeout.tv_sec = timePtr->sec;275timeout.tv_usec = timePtr->usec;276}277numFound = select(numFdBits, (SELECT_MASK *) &readyMasks[0],278(SELECT_MASK *) &readyMasks[MASK_SIZE],279(SELECT_MASK *) &readyMasks[2*MASK_SIZE], timeoutPtr);280281#ifdef WIN32282if (FD_ISSET(winFd, ((SELECT_MASK *) &readyMasks[0])))283{284MSG msg;285GetMessage(&msg, NULL, 0, 0);286TranslateMessage(&msg);287DispatchMessage(&msg);288}289#endif290291/*292* Some systems don't clear the masks after an error, so293* we have to do it here.294*/295296eventsFound = numFound;297if (numFound == -1) {298memset((VOID *) readyMasks, 0, 3*MASK_SIZE*sizeof(fd_mask));299}300301/*302* Reset the check masks in preparation for the next call to303* select.304*/305306numFdBits = 0;307memset((VOID *) checkMasks, 0, 3*MASK_SIZE*sizeof(fd_mask));308return TCL_OK;309}310311/*312*----------------------------------------------------------------------313*314* Tcl_Sleep --315*316* Delay execution for the specified number of milliseconds.317*318* Results:319* None.320*321* Side effects:322* Time passes.323*324*----------------------------------------------------------------------325*/326327void328Tcl_Sleep(ms)329int ms; /* Number of milliseconds to sleep. */330{331static struct timeval delay;332Tcl_Time before, after;333334/*335* The only trick here is that select appears to return early336* under some conditions, so we have to check to make sure that337* the right amount of time really has elapsed. If it's too338* early, go back to sleep again.339*/340341TclpGetTime(&before);342after = before;343after.sec += ms/1000;344after.usec += (ms%1000)*1000;345if (after.usec > 1000000) {346after.usec -= 1000000;347after.sec += 1;348}349while (1) {350delay.tv_sec = after.sec - before.sec;351delay.tv_usec = after.usec - before.usec;352if (delay.tv_usec < 0) {353delay.tv_usec += 1000000;354delay.tv_sec -= 1;355}356357/*358* Special note: must convert delay.tv_sec to int before comparing359* to zero, since delay.tv_usec is unsigned on some platforms.360*/361362if ((((int) delay.tv_sec) < 0)363|| ((delay.tv_usec == 0) && (delay.tv_sec == 0))) {364break;365}366(void) select(0, (SELECT_MASK *) 0, (SELECT_MASK *) 0,367(SELECT_MASK *) 0, &delay);368TclpGetTime(&before);369}370}371372373374