mirror of
				https://github.com/python/cpython.git
				synced 2025-11-03 23:21:29 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			507 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			507 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						||
 * tclSelectNotify.c --
 | 
						||
 *
 | 
						||
 * Partial even handling, select only. This file is adapted from TclUnixNotify.c, and
 | 
						||
 * meant as an add-in for Mac (and possibly Windows) environments where select *is* available.
 | 
						||
 * TclMacNotify.c works together with this file.
 | 
						||
 *
 | 
						||
 * Copyright (c) 1995-1997 Sun Microsystems, Inc.
 | 
						||
 *
 | 
						||
 * See the file "license.terms" for information on usage and redistribution
 | 
						||
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 | 
						||
 *
 | 
						||
 * SCCS: @(#) tclUnixNotfy.c 1.42 97/07/02 20:55:44
 | 
						||
 */
 | 
						||
 | 
						||
#if defined(__CFM68K__) && !defined(__USING_STATIC_LIBS__)
 | 
						||
	#pragma import on
 | 
						||
#endif
 | 
						||
 | 
						||
#include <unistd.h>
 | 
						||
 | 
						||
#if defined(__CFM68K__) && !defined(__USING_STATIC_LIBS__)
 | 
						||
	#pragma import reset
 | 
						||
#endif
 | 
						||
 | 
						||
#ifdef USE_GUSI
 | 
						||
/* Move this include up otherwise tclPort.h tried to redefine signals */
 | 
						||
#include <sys/signal.h>
 | 
						||
#endif
 | 
						||
#include "tclInt.h"
 | 
						||
#include "tclPort.h"
 | 
						||
#include <signal.h> 
 | 
						||
 | 
						||
#ifndef MASK_SIZE
 | 
						||
#define MASK_SIZE howmany(FD_SETSIZE, NFDBITS)
 | 
						||
#endif
 | 
						||
#ifndef SELECT_MASK
 | 
						||
#define SELECT_MASK fd_set
 | 
						||
#endif
 | 
						||
 | 
						||
/* Prototype (too lazy to create new .h) */
 | 
						||
int Tcl_PollSelectEvent(void);
 | 
						||
 | 
						||
/*
 | 
						||
 * This structure is used to keep track of the notifier info for a 
 | 
						||
 * a registered file.
 | 
						||
 */
 | 
						||
 | 
						||
typedef struct FileHandler {
 | 
						||
    int fd;
 | 
						||
    int mask;			/* Mask of desired events: TCL_READABLE,
 | 
						||
				 * etc. */
 | 
						||
    int readyMask;		/* Mask of events that have been seen since the
 | 
						||
				 * last time file handlers were invoked for
 | 
						||
				 * this file. */
 | 
						||
    Tcl_FileProc *proc;		/* Procedure to call, in the style of
 | 
						||
				 * Tcl_CreateFileHandler. */
 | 
						||
    ClientData clientData;	/* Argument to pass to proc. */
 | 
						||
    struct FileHandler *nextPtr;/* Next in list of all files we care about. */
 | 
						||
} FileHandler;
 | 
						||
 | 
						||
/*
 | 
						||
 * The following structure is what is added to the Tcl event queue when
 | 
						||
 * file handlers are ready to fire.
 | 
						||
 */
 | 
						||
 | 
						||
typedef struct FileHandlerEvent {
 | 
						||
    Tcl_Event header;		/* Information that is standard for
 | 
						||
				 * all events. */
 | 
						||
    int fd;			/* File descriptor that is ready.  Used
 | 
						||
				 * to find the FileHandler structure for
 | 
						||
				 * the file (can't point directly to the
 | 
						||
				 * FileHandler structure because it could
 | 
						||
				 * go away while the event is queued). */
 | 
						||
} FileHandlerEvent;
 | 
						||
 | 
						||
/*
 | 
						||
 * The following static structure contains the state information for the
 | 
						||
 * select based implementation of the Tcl notifier.
 | 
						||
 */
 | 
						||
 | 
						||
static struct {
 | 
						||
    FileHandler *firstFileHandlerPtr;
 | 
						||
				/* Pointer to head of file handler list. */
 | 
						||
    fd_mask checkMasks[3*MASK_SIZE];
 | 
						||
				/* This array is used to build up the masks
 | 
						||
				 * to be used in the next call to select.
 | 
						||
				 * Bits are set in response to calls to
 | 
						||
				 * Tcl_CreateFileHandler. */
 | 
						||
    fd_mask readyMasks[3*MASK_SIZE];
 | 
						||
				/* This array reflects the readable/writable
 | 
						||
				 * conditions that were found to exist by the
 | 
						||
				 * last call to select. */
 | 
						||
    int numFdBits;		/* Number of valid bits in checkMasks
 | 
						||
				 * (one more than highest fd for which
 | 
						||
				 * Tcl_WatchFile has been called). */
 | 
						||
} notifier;
 | 
						||
 | 
						||
/*
 | 
						||
 * The following static indicates whether this module has been initialized.
 | 
						||
 */
 | 
						||
 | 
						||
static int initialized = 0;
 | 
						||
 | 
						||
/*
 | 
						||
 * Static routines defined in this file.
 | 
						||
 */
 | 
						||
 | 
						||
static void		InitNotifier _ANSI_ARGS_((void));
 | 
						||
static void		NotifierExitHandler _ANSI_ARGS_((
 | 
						||
			    ClientData clientData));
 | 
						||
static int		FileHandlerEventProc _ANSI_ARGS_((Tcl_Event *evPtr,
 | 
						||
			    int flags));
 | 
						||
 | 
						||
/*
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 *
 | 
						||
 * InitNotifier --
 | 
						||
 *
 | 
						||
 *	Initializes the notifier state.
 | 
						||
 *
 | 
						||
 * Results:
 | 
						||
 *	None.
 | 
						||
 *
 | 
						||
 * Side effects:
 | 
						||
 *	Creates a new exit handler.
 | 
						||
 *
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 */
 | 
						||
 | 
						||
static void
 | 
						||
InitNotifier()
 | 
						||
{
 | 
						||
    initialized = 1;
 | 
						||
    memset(¬ifier, 0, sizeof(notifier));
 | 
						||
    Tcl_CreateExitHandler(NotifierExitHandler, NULL);
 | 
						||
}
 | 
						||
 | 
						||
/*
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 *
 | 
						||
 * NotifierExitHandler --
 | 
						||
 *
 | 
						||
 *	This function is called to cleanup the notifier state before
 | 
						||
 *	Tcl is unloaded.
 | 
						||
 *
 | 
						||
 * Results:
 | 
						||
 *	None.
 | 
						||
 *
 | 
						||
 * Side effects:
 | 
						||
 *	Destroys the notifier window.
 | 
						||
 *
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 */
 | 
						||
 | 
						||
static void
 | 
						||
NotifierExitHandler(clientData)
 | 
						||
    ClientData clientData;		/* Not used. */
 | 
						||
{
 | 
						||
    initialized = 0;
 | 
						||
}
 | 
						||
 | 
						||
/*
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 *
 | 
						||
 * Tcl_CreateFileHandler --
 | 
						||
 *
 | 
						||
 *	This procedure registers a file handler with the Xt notifier.
 | 
						||
 *
 | 
						||
 * Results:
 | 
						||
 *	None.
 | 
						||
 *
 | 
						||
 * Side effects:
 | 
						||
 *	Creates a new file handler structure and registers one or more
 | 
						||
 *	input procedures with Xt.
 | 
						||
 *
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 */
 | 
						||
 | 
						||
void
 | 
						||
Tcl_CreateFileHandler(fd, mask, proc, clientData)
 | 
						||
    int fd;			/* Handle of stream to watch. */
 | 
						||
    int mask;			/* OR'ed combination of TCL_READABLE,
 | 
						||
				 * TCL_WRITABLE, and TCL_EXCEPTION:
 | 
						||
				 * indicates conditions under which
 | 
						||
				 * proc should be called. */
 | 
						||
    Tcl_FileProc *proc;		/* Procedure to call for each
 | 
						||
				 * selected event. */
 | 
						||
    ClientData clientData;	/* Arbitrary data to pass to proc. */
 | 
						||
{
 | 
						||
    FileHandler *filePtr;
 | 
						||
    int index, bit;
 | 
						||
    
 | 
						||
    if (!initialized) {
 | 
						||
	InitNotifier();
 | 
						||
    }
 | 
						||
 | 
						||
    for (filePtr = notifier.firstFileHandlerPtr; filePtr != NULL;
 | 
						||
	    filePtr = filePtr->nextPtr) {
 | 
						||
	if (filePtr->fd == fd) {
 | 
						||
	    break;
 | 
						||
	}
 | 
						||
    }
 | 
						||
    if (filePtr == NULL) {
 | 
						||
	filePtr = (FileHandler*) ckalloc(sizeof(FileHandler)); /* MLK */
 | 
						||
	filePtr->fd = fd;
 | 
						||
	filePtr->readyMask = 0;
 | 
						||
	filePtr->nextPtr = notifier.firstFileHandlerPtr;
 | 
						||
	notifier.firstFileHandlerPtr = filePtr;
 | 
						||
    }
 | 
						||
    filePtr->proc = proc;
 | 
						||
    filePtr->clientData = clientData;
 | 
						||
    filePtr->mask = mask;
 | 
						||
 | 
						||
    /*
 | 
						||
     * Update the check masks for this file.
 | 
						||
     */
 | 
						||
 | 
						||
    index = fd/(NBBY*sizeof(fd_mask));
 | 
						||
    bit = 1 << (fd%(NBBY*sizeof(fd_mask)));
 | 
						||
    if (mask & TCL_READABLE) {
 | 
						||
	notifier.checkMasks[index] |= bit;
 | 
						||
    } else {
 | 
						||
	notifier.checkMasks[index] &= ~bit;
 | 
						||
    } 
 | 
						||
    if (mask & TCL_WRITABLE) {
 | 
						||
	(notifier.checkMasks+MASK_SIZE)[index] |= bit;
 | 
						||
    } else {
 | 
						||
	(notifier.checkMasks+MASK_SIZE)[index] &= ~bit;
 | 
						||
    }
 | 
						||
    if (mask & TCL_EXCEPTION) {
 | 
						||
	(notifier.checkMasks+2*(MASK_SIZE))[index] |= bit;
 | 
						||
    } else {
 | 
						||
	(notifier.checkMasks+2*(MASK_SIZE))[index] &= ~bit;
 | 
						||
    }
 | 
						||
    if (notifier.numFdBits <= fd) {
 | 
						||
	notifier.numFdBits = fd+1;
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
/*
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 *
 | 
						||
 * Tcl_DeleteFileHandler --
 | 
						||
 *
 | 
						||
 *	Cancel a previously-arranged callback arrangement for
 | 
						||
 *	a file.
 | 
						||
 *
 | 
						||
 * Results:
 | 
						||
 *	None.
 | 
						||
 *
 | 
						||
 * Side effects:
 | 
						||
 *	If a callback was previously registered on file, remove it.
 | 
						||
 *
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 */
 | 
						||
 | 
						||
void
 | 
						||
Tcl_DeleteFileHandler(fd)
 | 
						||
    int fd;		/* Stream id for which to remove callback procedure. */
 | 
						||
{
 | 
						||
    FileHandler *filePtr, *prevPtr;
 | 
						||
    int index, bit, mask, i;
 | 
						||
 | 
						||
    if (!initialized) {
 | 
						||
	InitNotifier();
 | 
						||
    }
 | 
						||
 | 
						||
    /*
 | 
						||
     * Find the entry for the given file (and return if there
 | 
						||
     * isn't one).
 | 
						||
     */
 | 
						||
 | 
						||
    for (prevPtr = NULL, filePtr = notifier.firstFileHandlerPtr; ;
 | 
						||
	    prevPtr = filePtr, filePtr = filePtr->nextPtr) {
 | 
						||
	if (filePtr == NULL) {
 | 
						||
	    return;
 | 
						||
	}
 | 
						||
	if (filePtr->fd == fd) {
 | 
						||
	    break;
 | 
						||
	}
 | 
						||
    }
 | 
						||
 | 
						||
    /*
 | 
						||
     * Update the check masks for this file.
 | 
						||
     */
 | 
						||
 | 
						||
    index = fd/(NBBY*sizeof(fd_mask));
 | 
						||
    bit = 1 << (fd%(NBBY*sizeof(fd_mask)));
 | 
						||
 | 
						||
    if (filePtr->mask & TCL_READABLE) {
 | 
						||
	notifier.checkMasks[index] &= ~bit;
 | 
						||
    }
 | 
						||
    if (filePtr->mask & TCL_WRITABLE) {
 | 
						||
	(notifier.checkMasks+MASK_SIZE)[index] &= ~bit;
 | 
						||
    }
 | 
						||
    if (filePtr->mask & TCL_EXCEPTION) {
 | 
						||
	(notifier.checkMasks+2*(MASK_SIZE))[index] &= ~bit;
 | 
						||
    }
 | 
						||
 | 
						||
    /*
 | 
						||
     * Find current max fd.
 | 
						||
     */
 | 
						||
 | 
						||
    if (fd+1 == notifier.numFdBits) {
 | 
						||
	for (notifier.numFdBits = 0; index >= 0; index--) {
 | 
						||
	    mask = notifier.checkMasks[index]
 | 
						||
		| (notifier.checkMasks+MASK_SIZE)[index]
 | 
						||
		| (notifier.checkMasks+2*(MASK_SIZE))[index];
 | 
						||
	    if (mask) {
 | 
						||
		for (i = (NBBY*sizeof(fd_mask)); i > 0; i--) {
 | 
						||
		    if (mask & (1 << (i-1))) {
 | 
						||
			break;
 | 
						||
		    }
 | 
						||
		}
 | 
						||
		notifier.numFdBits = index * (NBBY*sizeof(fd_mask)) + i;
 | 
						||
		break;
 | 
						||
	    }
 | 
						||
	}
 | 
						||
    }
 | 
						||
 | 
						||
    /*
 | 
						||
     * Clean up information in the callback record.
 | 
						||
     */
 | 
						||
 | 
						||
    if (prevPtr == NULL) {
 | 
						||
	notifier.firstFileHandlerPtr = filePtr->nextPtr;
 | 
						||
    } else {
 | 
						||
	prevPtr->nextPtr = filePtr->nextPtr;
 | 
						||
    }
 | 
						||
    ckfree((char *) filePtr);
 | 
						||
}
 | 
						||
 | 
						||
/*
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 *
 | 
						||
 * FileHandlerEventProc --
 | 
						||
 *
 | 
						||
 *	This procedure is called by Tcl_ServiceEvent when a file event
 | 
						||
 *	reaches the front of the event queue.  This procedure is
 | 
						||
 *	responsible for actually handling the event by invoking the
 | 
						||
 *	callback for the file handler.
 | 
						||
 *
 | 
						||
 * Results:
 | 
						||
 *	Returns 1 if the event was handled, meaning it should be removed
 | 
						||
 *	from the queue.  Returns 0 if the event was not handled, meaning
 | 
						||
 *	it should stay on the queue.  The only time the event isn't
 | 
						||
 *	handled is if the TCL_FILE_EVENTS flag bit isn't set.
 | 
						||
 *
 | 
						||
 * Side effects:
 | 
						||
 *	Whatever the file handler's callback procedure does.
 | 
						||
 *
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 */
 | 
						||
 | 
						||
static int
 | 
						||
FileHandlerEventProc(evPtr, flags)
 | 
						||
    Tcl_Event *evPtr;		/* Event to service. */
 | 
						||
    int flags;			/* Flags that indicate what events to
 | 
						||
				 * handle, such as TCL_FILE_EVENTS. */
 | 
						||
{
 | 
						||
    FileHandler *filePtr;
 | 
						||
    FileHandlerEvent *fileEvPtr = (FileHandlerEvent *) evPtr;
 | 
						||
    int mask;
 | 
						||
 | 
						||
    if (!(flags & TCL_FILE_EVENTS)) {
 | 
						||
	return 0;
 | 
						||
    }
 | 
						||
 | 
						||
    /*
 | 
						||
     * Search through the file handlers to find the one whose handle matches
 | 
						||
     * the event.  We do this rather than keeping a pointer to the file
 | 
						||
     * handler directly in the event, so that the handler can be deleted
 | 
						||
     * while the event is queued without leaving a dangling pointer.
 | 
						||
     */
 | 
						||
 | 
						||
    for (filePtr = notifier.firstFileHandlerPtr; filePtr != NULL;
 | 
						||
	    filePtr = filePtr->nextPtr) {
 | 
						||
	if (filePtr->fd != fileEvPtr->fd) {
 | 
						||
	    continue;
 | 
						||
	}
 | 
						||
 | 
						||
	/*
 | 
						||
	 * The code is tricky for two reasons:
 | 
						||
	 * 1. The file handler's desired events could have changed
 | 
						||
	 *    since the time when the event was queued, so AND the
 | 
						||
	 *    ready mask with the desired mask.
 | 
						||
	 * 2. The file could have been closed and re-opened since
 | 
						||
	 *    the time when the event was queued.  This is why the
 | 
						||
	 *    ready mask is stored in the file handler rather than
 | 
						||
	 *    the queued event:  it will be zeroed when a new
 | 
						||
	 *    file handler is created for the newly opened file.
 | 
						||
	 */
 | 
						||
 | 
						||
	mask = filePtr->readyMask & filePtr->mask;
 | 
						||
	filePtr->readyMask = 0;
 | 
						||
	if (mask != 0) {
 | 
						||
	    (*filePtr->proc)(filePtr->clientData, mask);
 | 
						||
	}
 | 
						||
	break;
 | 
						||
    }
 | 
						||
    return 1;
 | 
						||
}
 | 
						||
 | 
						||
/*
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 *
 | 
						||
 * Tcl_PollSelectEvent --
 | 
						||
 *
 | 
						||
 *	This function is called by Tcl_WaitForEvent to wait for new
 | 
						||
 *	events on the message queue.  If the block time is 0, then
 | 
						||
 *	Tcl_WaitForEvent just polls without blocking.
 | 
						||
 *
 | 
						||
 * Results:
 | 
						||
 *	Returns 1 if any event handled, 0 otherwise.
 | 
						||
 *
 | 
						||
 * Side effects:
 | 
						||
 *	Queues file events that are detected by the select.
 | 
						||
 *
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 */
 | 
						||
 | 
						||
int
 | 
						||
Tcl_PollSelectEvent(void)
 | 
						||
{
 | 
						||
    FileHandler *filePtr;
 | 
						||
    FileHandlerEvent *fileEvPtr;
 | 
						||
    struct timeval timeout, *timeoutPtr;
 | 
						||
    int bit, index, mask, numFound;
 | 
						||
 | 
						||
    if (!initialized) {
 | 
						||
	InitNotifier();
 | 
						||
    }
 | 
						||
 | 
						||
    /*
 | 
						||
     * Set up the timeout structure. 
 | 
						||
     */
 | 
						||
 | 
						||
    timeout.tv_sec = 0;
 | 
						||
    timeout.tv_usec = 0;
 | 
						||
    timeoutPtr = &timeout;
 | 
						||
 | 
						||
    memcpy((VOID *) notifier.readyMasks, (VOID *) notifier.checkMasks,
 | 
						||
	    3*MASK_SIZE*sizeof(fd_mask));
 | 
						||
    numFound = select(notifier.numFdBits,
 | 
						||
	    (SELECT_MASK *) ¬ifier.readyMasks[0],
 | 
						||
	    (SELECT_MASK *) ¬ifier.readyMasks[MASK_SIZE],
 | 
						||
	    (SELECT_MASK *) ¬ifier.readyMasks[2*MASK_SIZE], timeoutPtr);
 | 
						||
 | 
						||
    /*
 | 
						||
     * Some systems don't clear the masks after an error, so
 | 
						||
     * we have to do it here.
 | 
						||
     */
 | 
						||
 | 
						||
    if (numFound == -1) {
 | 
						||
	memset((VOID *) notifier.readyMasks, 0, 3*MASK_SIZE*sizeof(fd_mask));
 | 
						||
    }
 | 
						||
    
 | 
						||
    /*
 | 
						||
     * Return if nothing to do.
 | 
						||
     */
 | 
						||
    if ( numFound == 0 )
 | 
						||
    	return 0;
 | 
						||
 | 
						||
    /*
 | 
						||
     * Queue all detected file events before returning.
 | 
						||
     */
 | 
						||
 | 
						||
    for (filePtr = notifier.firstFileHandlerPtr;
 | 
						||
	    (filePtr != NULL) && (numFound > 0);
 | 
						||
	    filePtr = filePtr->nextPtr) {
 | 
						||
	index = filePtr->fd / (NBBY*sizeof(fd_mask));
 | 
						||
	bit = 1 << (filePtr->fd % (NBBY*sizeof(fd_mask)));
 | 
						||
	mask = 0;
 | 
						||
 | 
						||
	if (notifier.readyMasks[index] & bit) {
 | 
						||
	    mask |= TCL_READABLE;
 | 
						||
	}
 | 
						||
	if ((notifier.readyMasks+MASK_SIZE)[index] & bit) {
 | 
						||
	    mask |= TCL_WRITABLE;
 | 
						||
	}
 | 
						||
	if ((notifier.readyMasks+2*(MASK_SIZE))[index] & bit) {
 | 
						||
	    mask |= TCL_EXCEPTION;
 | 
						||
	}
 | 
						||
 | 
						||
	if (!mask) {
 | 
						||
	    continue;
 | 
						||
	} else {
 | 
						||
	    numFound--;
 | 
						||
	}
 | 
						||
 | 
						||
	/*
 | 
						||
	 * Don't bother to queue an event if the mask was previously
 | 
						||
	 * non-zero since an event must still be on the queue.
 | 
						||
	 */
 | 
						||
 | 
						||
	if (filePtr->readyMask == 0) {
 | 
						||
	    fileEvPtr = (FileHandlerEvent *) ckalloc(
 | 
						||
		sizeof(FileHandlerEvent));
 | 
						||
	    fileEvPtr->header.proc = FileHandlerEventProc;
 | 
						||
	    fileEvPtr->fd = filePtr->fd;
 | 
						||
	    Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL);
 | 
						||
	}
 | 
						||
	filePtr->readyMask = mask;
 | 
						||
    }
 | 
						||
    return 1;
 | 
						||
}
 |