diff options
author | Poul-Henning Kamp <phk@FreeBSD.org> | 1997-07-25 19:27:55 +0000 |
---|---|---|
committer | Poul-Henning Kamp <phk@FreeBSD.org> | 1997-07-25 19:27:55 +0000 |
commit | 3d33409926539d866dcea9fc5cb14113b312adf0 (patch) | |
tree | d2f88b3e9ffa79ffb2cc1a0699dd3ee96c47c3e5 /contrib/tcl/generic/tclEvent.c | |
parent | 8569730d6bc2e4cb5e784997313325b13518e066 (diff) |
Notes
Diffstat (limited to 'contrib/tcl/generic/tclEvent.c')
-rw-r--r-- | contrib/tcl/generic/tclEvent.c | 1731 |
1 files changed, 118 insertions, 1613 deletions
diff --git a/contrib/tcl/generic/tclEvent.c b/contrib/tcl/generic/tclEvent.c index 7a081c714bc9..a503df7f841b 100644 --- a/contrib/tcl/generic/tclEvent.c +++ b/contrib/tcl/generic/tclEvent.c @@ -1,177 +1,23 @@ /* * tclEvent.c -- * - * This file provides basic event-managing facilities for Tcl, - * including an event queue, and mechanisms for attaching - * callbacks to certain events. - * - * It also contains the command procedures for the commands - * "after", "vwait", and "update". + * This file implements some general event related interfaces including + * background errors, exit handlers, and the "vwait" and "update" + * command procedures. * * Copyright (c) 1990-1994 The Regents of the University of California. - * Copyright (c) 1994-1995 Sun Microsystems, Inc. + * Copyright (c) 1994-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: @(#) tclEvent.c 1.128 96/07/23 16:12:34 + * SCCS: @(#) tclEvent.c 1.152 97/05/21 07:06:19 */ #include "tclInt.h" #include "tclPort.h" /* - * For each file registered in a call to Tcl_CreateFileHandler, - * there is one record of the following type. All of these records - * are chained together into a single list. - */ - -typedef struct FileHandler { - Tcl_File file; /* Generic file handle for file. */ - int mask; /* Mask of desired events: TCL_READABLE, etc. */ - int readyMask; /* Events that were ready the last time that - * FileHandlerCheckProc checked this file. */ - Tcl_FileProc *proc; /* Procedure to call, in the style of - * Tcl_CreateFileHandler. This is NULL - * if the handler was created by - * Tcl_CreateFileHandler2. */ - ClientData clientData; /* Argument to pass to proc. */ - struct FileHandler *nextPtr;/* Next in list of all files we care - * about (NULL for end of list). */ -} FileHandler; - -static FileHandler *firstFileHandlerPtr = (FileHandler *) NULL; - /* List of all file handlers. */ -static int fileEventSourceCreated = 0; - /* Non-zero means that the file event source - * hasn't been registerd with the Tcl - * notifier yet. */ - -/* - * 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. */ - Tcl_File file; /* 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; - -/* - * For each timer callback that's pending (either regular or "modal"), - * there is one record of the following type. The normal handlers - * (created by Tcl_CreateTimerHandler) are chained together in a - * list sorted by time (earliest event first). - */ - -typedef struct TimerHandler { - Tcl_Time time; /* When timer is to fire. */ - Tcl_TimerProc *proc; /* Procedure to call. */ - ClientData clientData; /* Argument to pass to proc. */ - Tcl_TimerToken token; /* Identifies event so it can be - * deleted. Not used in modal - * timeouts. */ - struct TimerHandler *nextPtr; /* Next event in queue, or NULL for - * end of queue. */ -} TimerHandler; - -static TimerHandler *firstTimerHandlerPtr = NULL; - /* First event in queue. */ -static int timerEventSourceCreated = 0; /* 0 means that the timer event source - * hasn't yet been registered with the - * Tcl notifier. */ - -/* - * The information below describes a stack of modal timeouts managed by - * Tcl_CreateModalTimer and Tcl_DeleteModalTimer. Only the first element - * in the list is used at any given time. - */ - -static TimerHandler *firstModalHandlerPtr = NULL; - -/* - * The following structure is what's added to the Tcl event queue when - * timer handlers are ready to fire. - */ - -typedef struct TimerEvent { - Tcl_Event header; /* Information that is standard for - * all events. */ - Tcl_Time time; /* All timer events that specify this - * time or earlier are ready - * to fire. */ -} TimerEvent; - -/* - * There is one of the following structures for each of the - * handlers declared in a call to Tcl_DoWhenIdle. All of the - * currently-active handlers are linked together into a list. - */ - -typedef struct IdleHandler { - Tcl_IdleProc (*proc); /* Procedure to call. */ - ClientData clientData; /* Value to pass to proc. */ - int generation; /* Used to distinguish older handlers from - * recently-created ones. */ - struct IdleHandler *nextPtr;/* Next in list of active handlers. */ -} IdleHandler; - -static IdleHandler *idleList = NULL; - /* First in list of all idle handlers. */ -static IdleHandler *lastIdlePtr = NULL; - /* Last in list (or NULL for empty list). */ -static int idleGeneration = 0; /* Used to fill in the "generation" fields - * of IdleHandler structures. Increments - * each time Tcl_DoOneEvent starts calling - * idle handlers, so that all old handlers - * can be called without calling any of the - * new ones created by old ones. */ - -/* - * The data structure below is used by the "after" command to remember - * the command to be executed later. All of the pending "after" commands - * for an interpreter are linked together in a list. - */ - -typedef struct AfterInfo { - struct AfterAssocData *assocPtr; - /* Pointer to the "tclAfter" assocData for - * the interp in which command will be - * executed. */ - char *command; /* Command to execute. Malloc'ed, so must - * be freed when structure is deallocated. */ - int id; /* Integer identifier for command; used to - * cancel it. */ - Tcl_TimerToken token; /* Used to cancel the "after" command. NULL - * means that the command is run as an - * idle handler rather than as a timer - * handler. NULL means this is an "after - * idle" handler rather than a - * timer handler. */ - struct AfterInfo *nextPtr; /* Next in list of all "after" commands for - * this interpreter. */ -} AfterInfo; - -/* - * One of the following structures is associated with each interpreter - * for which an "after" command has ever been invoked. A pointer to - * this structure is stored in the AssocData for the "tclAfter" key. - */ - -typedef struct AfterAssocData { - Tcl_Interp *interp; /* The interpreter for which this data is - * registered. */ - AfterInfo *firstAfterPtr; /* First in list of all "after" commands - * still pending for this interpreter, or - * NULL if none. */ -} AfterAssocData; - -/* * The data structure below is used to report background errors. One * such structure is allocated for each error; it holds information * about the interpreter and the error until bgerror can be invoked @@ -225,25 +71,6 @@ static ExitHandler *firstExitPtr = NULL; * application. */ /* - * Structures of the following type are used during the execution - * of Tcl_WaitForFile, to keep track of the file and timeout. - */ - -typedef struct FileWait { - Tcl_File file; /* File to wait on. */ - int mask; /* Conditions to wait for (TCL_READABLE, - * etc.) */ - int timeout; /* Original "timeout" argument to - * Tcl_WaitForFile. */ - Tcl_Time abortTime; /* Time at which to abort the wait. */ - int present; /* Conditions present on the file during - * the last time through the event loop. */ - int done; /* Non-zero means we're done: either one of - * the desired conditions is present or the - * timeout period has elapsed. */ -} FileWait; - -/* * The following variable is a "secret" indication to Tcl_Exit that * it should dump out the state of memory before exiting. If the * value is non-NULL, it gives the name of the file in which to @@ -253,969 +80,26 @@ typedef struct FileWait { char *tclMemDumpFileName = NULL; /* + * This variable is set to 1 when Tcl_Exit is called, and at the end of + * its work, it is reset to 0. The variable is checked by TclInExit() to + * allow different behavior for exit-time processing, e.g. in closing of + * files and pipes. + */ + +static int tclInExit = 0; + +/* * Prototypes for procedures referenced only in this file: */ -static void AfterCleanupProc _ANSI_ARGS_((ClientData clientData, - Tcl_Interp *interp)); -static void AfterProc _ANSI_ARGS_((ClientData clientData)); static void BgErrorDeleteProc _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp)); -static void FileHandlerCheckProc _ANSI_ARGS_(( - ClientData clientData, int flags)); -static int FileHandlerEventProc _ANSI_ARGS_((Tcl_Event *evPtr, - int flags)); -static void FileHandlerExitProc _ANSI_ARGS_((ClientData data)); -static void FileHandlerSetupProc _ANSI_ARGS_(( - ClientData clientData, int flags)); -static void FreeAfterPtr _ANSI_ARGS_((AfterInfo *afterPtr)); -static AfterInfo * GetAfterEvent _ANSI_ARGS_((AfterAssocData *assocPtr, - char *string)); static void HandleBgErrors _ANSI_ARGS_((ClientData clientData)); -static void TimerHandlerCheckProc _ANSI_ARGS_(( - ClientData clientData, int flags)); -static int TimerHandlerEventProc _ANSI_ARGS_((Tcl_Event *evPtr, - int flags)); -static void TimerHandlerExitProc _ANSI_ARGS_((ClientData data)); -static void TimerHandlerSetupProc _ANSI_ARGS_(( - ClientData clientData, int flags)); static char * VwaitVarProc _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp, char *name1, char *name2, int flags)); /* - *-------------------------------------------------------------- - * - * Tcl_CreateFileHandler -- - * - * Arrange for a given procedure to be invoked whenever - * a given file becomes readable or writable. - * - * Results: - * None. - * - * Side effects: - * From now on, whenever the I/O channel given by file becomes - * ready in the way indicated by mask, proc will be invoked. - * See the manual entry for details on the calling sequence - * to proc. If file is already registered then the old mask - * and proc and clientData values will be replaced with - * new ones. - * - *-------------------------------------------------------------- - */ - -void -Tcl_CreateFileHandler(file, mask, proc, clientData) - Tcl_File file; /* 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. */ -{ - register FileHandler *filePtr; - - if (!fileEventSourceCreated) { - fileEventSourceCreated = 1; - Tcl_CreateEventSource(FileHandlerSetupProc, FileHandlerCheckProc, - (ClientData) NULL); - Tcl_CreateExitHandler(FileHandlerExitProc, (ClientData) NULL); - } - - /* - * Make sure the file isn't already registered. Create a - * new record in the normal case where there's no existing - * record. - */ - - for (filePtr = firstFileHandlerPtr; filePtr != NULL; - filePtr = filePtr->nextPtr) { - if (filePtr->file == file) { - break; - } - } - if (filePtr == NULL) { - filePtr = (FileHandler *) ckalloc(sizeof(FileHandler)); - filePtr->file = file; - filePtr->nextPtr = firstFileHandlerPtr; - firstFileHandlerPtr = filePtr; - } - - /* - * The remainder of the initialization below is done regardless - * of whether or not this is a new record or a modification of - * an old one. - */ - - filePtr->mask = mask; - filePtr->readyMask = 0; - filePtr->proc = proc; - filePtr->clientData = clientData; -} - -/* - *-------------------------------------------------------------- - * - * 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(file) - Tcl_File file; /* Stream id for which to remove - * callback procedure. */ -{ - FileHandler *filePtr, *prevPtr; - - /* - * Find the entry for the given file (and return if there - * isn't one). - */ - - for (prevPtr = NULL, filePtr = firstFileHandlerPtr; ; - prevPtr = filePtr, filePtr = filePtr->nextPtr) { - if (filePtr == NULL) { - return; - } - if (filePtr->file == file) { - break; - } - } - - /* - * Clean up information in the callback record. - */ - - if (prevPtr == NULL) { - firstFileHandlerPtr = filePtr->nextPtr; - } else { - prevPtr->nextPtr = filePtr->nextPtr; - } - ckfree((char *) filePtr); -} - -/* - *---------------------------------------------------------------------- - * - * FileHandlerExitProc -- - * - * Cleanup procedure to delete the file event source during exit - * cleanup. - * - * Results: - * None. - * - * Side effects: - * Destroys the file event source. - * - *---------------------------------------------------------------------- - */ - - /* ARGSUSED */ -static void -FileHandlerExitProc(clientData) - ClientData clientData; /* Not used. */ -{ - Tcl_DeleteEventSource(FileHandlerSetupProc, FileHandlerCheckProc, - (ClientData) NULL); -} - -/* - *---------------------------------------------------------------------- - * - * FileHandlerSetupProc -- - * - * This procedure is part of the "event source" for file handlers. - * It is invoked by Tcl_DoOneEvent before it calls select (or - * whatever it uses to wait). - * - * Results: - * None. - * - * Side effects: - * Tells the notifier which files should be waited for. - * - *---------------------------------------------------------------------- - */ - -static void -FileHandlerSetupProc(clientData, flags) - ClientData clientData; /* Not used. */ - int flags; /* Flags passed to Tk_DoOneEvent: - * if it doesn't include - * TCL_FILE_EVENTS then we do - * nothing. */ -{ - FileHandler *filePtr; - - if (!(flags & TCL_FILE_EVENTS)) { - return; - } - for (filePtr = firstFileHandlerPtr; filePtr != NULL; - filePtr = filePtr->nextPtr) { - if (filePtr->mask != 0) { - Tcl_WatchFile(filePtr->file, filePtr->mask); - } - } -} - -/* - *---------------------------------------------------------------------- - * - * FileHandlerCheckProc -- - * - * This procedure is the second part of the "event source" for - * file handlers. It is invoked by Tcl_DoOneEvent after it calls - * select (or whatever it uses to wait for events). - * - * Results: - * None. - * - * Side effects: - * Makes entries on the Tcl event queue for each file that is - * now ready. - * - *---------------------------------------------------------------------- - */ - -static void -FileHandlerCheckProc(clientData, flags) - ClientData clientData; /* Not used. */ - int flags; /* Flags passed to Tk_DoOneEvent: - * if it doesn't include - * TCL_FILE_EVENTS then we do - * nothing. */ -{ - FileHandler *filePtr; - FileHandlerEvent *fileEvPtr; - - if (!(flags & TCL_FILE_EVENTS)) { - return; - } - for (filePtr = firstFileHandlerPtr; filePtr != NULL; - filePtr = filePtr->nextPtr) { - if (filePtr->mask != 0) { - filePtr->readyMask = Tcl_FileReady(filePtr->file, filePtr->mask); - if (filePtr->readyMask != 0) { - fileEvPtr = (FileHandlerEvent *) ckalloc( - sizeof(FileHandlerEvent)); - fileEvPtr->header.proc = FileHandlerEventProc; - fileEvPtr->file = filePtr->file; - Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL); - } - } - } -} - -/* - *---------------------------------------------------------------------- - * - * FileHandlerEventProc -- - * - * This procedure is called by Tcl_DoOneEvent 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 = firstFileHandlerPtr; filePtr != NULL; - filePtr = filePtr->nextPtr) { - if (filePtr->file != fileEvPtr->file) { - 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_CreateTimerHandler -- - * - * Arrange for a given procedure to be invoked at a particular - * time in the future. - * - * Results: - * The return value is a token for the timer event, which - * may be used to delete the event before it fires. - * - * Side effects: - * When milliseconds have elapsed, proc will be invoked - * exactly once. - * - *-------------------------------------------------------------- - */ - -Tcl_TimerToken -Tcl_CreateTimerHandler(milliseconds, proc, clientData) - int milliseconds; /* How many milliseconds to wait - * before invoking proc. */ - Tcl_TimerProc *proc; /* Procedure to invoke. */ - ClientData clientData; /* Arbitrary data to pass to proc. */ -{ - register TimerHandler *timerHandlerPtr, *tPtr2, *prevPtr; - static int id = 0; - - if (!timerEventSourceCreated) { - timerEventSourceCreated = 1; - Tcl_CreateEventSource(TimerHandlerSetupProc, TimerHandlerCheckProc, - (ClientData) NULL); - Tcl_CreateExitHandler(TimerHandlerExitProc, (ClientData) NULL); - } - - timerHandlerPtr = (TimerHandler *) ckalloc(sizeof(TimerHandler)); - - /* - * Compute when the event should fire. - */ - - TclpGetTime(&timerHandlerPtr->time); - timerHandlerPtr->time.sec += milliseconds/1000; - timerHandlerPtr->time.usec += (milliseconds%1000)*1000; - if (timerHandlerPtr->time.usec >= 1000000) { - timerHandlerPtr->time.usec -= 1000000; - timerHandlerPtr->time.sec += 1; - } - - /* - * Fill in other fields for the event. - */ - - timerHandlerPtr->proc = proc; - timerHandlerPtr->clientData = clientData; - id++; - timerHandlerPtr->token = (Tcl_TimerToken) id; - - /* - * Add the event to the queue in the correct position - * (ordered by event firing time). - */ - - for (tPtr2 = firstTimerHandlerPtr, prevPtr = NULL; tPtr2 != NULL; - prevPtr = tPtr2, tPtr2 = tPtr2->nextPtr) { - if ((tPtr2->time.sec > timerHandlerPtr->time.sec) - || ((tPtr2->time.sec == timerHandlerPtr->time.sec) - && (tPtr2->time.usec > timerHandlerPtr->time.usec))) { - break; - } - } - timerHandlerPtr->nextPtr = tPtr2; - if (prevPtr == NULL) { - firstTimerHandlerPtr = timerHandlerPtr; - } else { - prevPtr->nextPtr = timerHandlerPtr; - } - return timerHandlerPtr->token; -} - -/* - *-------------------------------------------------------------- - * - * Tcl_DeleteTimerHandler -- - * - * Delete a previously-registered timer handler. - * - * Results: - * None. - * - * Side effects: - * Destroy the timer callback identified by TimerToken, - * so that its associated procedure will not be called. - * If the callback has already fired, or if the given - * token doesn't exist, then nothing happens. - * - *-------------------------------------------------------------- - */ - -void -Tcl_DeleteTimerHandler(token) - Tcl_TimerToken token; /* Result previously returned by - * Tcl_DeleteTimerHandler. */ -{ - register TimerHandler *timerHandlerPtr, *prevPtr; - - for (timerHandlerPtr = firstTimerHandlerPtr, prevPtr = NULL; - timerHandlerPtr != NULL; prevPtr = timerHandlerPtr, - timerHandlerPtr = timerHandlerPtr->nextPtr) { - if (timerHandlerPtr->token != token) { - continue; - } - if (prevPtr == NULL) { - firstTimerHandlerPtr = timerHandlerPtr->nextPtr; - } else { - prevPtr->nextPtr = timerHandlerPtr->nextPtr; - } - ckfree((char *) timerHandlerPtr); - return; - } -} - -/* - *-------------------------------------------------------------- - * - * Tcl_CreateModalTimeout -- - * - * Arrange for a given procedure to be invoked at a particular - * time in the future, independently of all other timer events. - * - * Results: - * None. - * - * Side effects: - * When milliseconds have elapsed, proc will be invoked - * exactly once. - * - *-------------------------------------------------------------- - */ - -void -Tcl_CreateModalTimeout(milliseconds, proc, clientData) - int milliseconds; /* How many milliseconds to wait - * before invoking proc. */ - Tcl_TimerProc *proc; /* Procedure to invoke. */ - ClientData clientData; /* Arbitrary data to pass to proc. */ -{ - TimerHandler *timerHandlerPtr; - - if (!timerEventSourceCreated) { - timerEventSourceCreated = 1; - Tcl_CreateEventSource(TimerHandlerSetupProc, TimerHandlerCheckProc, - (ClientData) NULL); - Tcl_CreateExitHandler(TimerHandlerExitProc, (ClientData) NULL); - } - - timerHandlerPtr = (TimerHandler *) ckalloc(sizeof(TimerHandler)); - - /* - * Compute when the timeout should fire and fill in the other fields - * of the handler. - */ - - TclpGetTime(&timerHandlerPtr->time); - timerHandlerPtr->time.sec += milliseconds/1000; - timerHandlerPtr->time.usec += (milliseconds%1000)*1000; - if (timerHandlerPtr->time.usec >= 1000000) { - timerHandlerPtr->time.usec -= 1000000; - timerHandlerPtr->time.sec += 1; - } - timerHandlerPtr->proc = proc; - timerHandlerPtr->clientData = clientData; - - /* - * Push the handler on the top of the modal stack. - */ - - timerHandlerPtr->nextPtr = firstModalHandlerPtr; - firstModalHandlerPtr = timerHandlerPtr; -} - -/* - *-------------------------------------------------------------- - * - * Tcl_DeleteModalTimeout -- - * - * Remove the topmost modal timer handler from the stack of - * modal handlers. - * - * Results: - * None. - * - * Side effects: - * Destroys the topmost modal timeout handler, which must - * match proc and clientData. - * - *-------------------------------------------------------------- - */ - -void -Tcl_DeleteModalTimeout(proc, clientData) - Tcl_TimerProc *proc; /* Callback procedure for the timeout. */ - ClientData clientData; /* Arbitrary data to pass to proc. */ -{ - TimerHandler *timerHandlerPtr; - - timerHandlerPtr = firstModalHandlerPtr; - firstModalHandlerPtr = timerHandlerPtr->nextPtr; - if ((timerHandlerPtr->proc != proc) - || (timerHandlerPtr->clientData != clientData)) { - panic("Tcl_DeleteModalTimeout found timeout stack corrupted"); - } - ckfree((char *) timerHandlerPtr); -} - -/* - *---------------------------------------------------------------------- - * - * TimerHandlerSetupProc -- - * - * This procedure is part of the "event source" for timers. - * It is invoked by Tcl_DoOneEvent before it calls select (or - * whatever it uses to wait). - * - * Results: - * None. - * - * Side effects: - * Tells the notifier how long to sleep if it decides to block. - * - *---------------------------------------------------------------------- - */ - -static void -TimerHandlerSetupProc(clientData, flags) - ClientData clientData; /* Not used. */ - int flags; /* Flags passed to Tk_DoOneEvent: - * if it doesn't include - * TCL_TIMER_EVENTS then we only - * consider modal timers. */ -{ - TimerHandler *timerHandlerPtr, *tPtr2; - Tcl_Time blockTime; - - /* - * Find the timer handler (regular or modal) that fires first. - */ - - timerHandlerPtr = firstTimerHandlerPtr; - if (!(flags & TCL_TIMER_EVENTS)) { - timerHandlerPtr = NULL; - } - if (timerHandlerPtr != NULL) { - tPtr2 = firstModalHandlerPtr; - if (tPtr2 != NULL) { - if ((timerHandlerPtr->time.sec > tPtr2->time.sec) - || ((timerHandlerPtr->time.sec == tPtr2->time.sec) - && (timerHandlerPtr->time.usec > tPtr2->time.usec))) { - timerHandlerPtr = tPtr2; - } - } - } else { - timerHandlerPtr = firstModalHandlerPtr; - } - if (timerHandlerPtr == NULL) { - return; - } - - TclpGetTime(&blockTime); - blockTime.sec = timerHandlerPtr->time.sec - blockTime.sec; - blockTime.usec = timerHandlerPtr->time.usec - blockTime.usec; - if (blockTime.usec < 0) { - blockTime.sec -= 1; - blockTime.usec += 1000000; - } - if (blockTime.sec < 0) { - blockTime.sec = 0; - blockTime.usec = 0; - } - Tcl_SetMaxBlockTime(&blockTime); -} - -/* - *---------------------------------------------------------------------- - * - * TimerHandlerCheckProc -- - * - * This procedure is the second part of the "event source" for - * file handlers. It is invoked by Tcl_DoOneEvent after it calls - * select (or whatever it uses to wait for events). - * - * Results: - * None. - * - * Side effects: - * Makes entries on the Tcl event queue for each file that is - * now ready. - * - *---------------------------------------------------------------------- - */ - -static void -TimerHandlerCheckProc(clientData, flags) - ClientData clientData; /* Not used. */ - int flags; /* Flags passed to Tk_DoOneEvent: - * if it doesn't include - * TCL_TIMER_EVENTS then we only - * consider modal timeouts. */ -{ - TimerHandler *timerHandlerPtr; - TimerEvent *timerEvPtr; - int triggered, gotTime; - Tcl_Time curTime; - - triggered = 0; - gotTime = 0; - timerHandlerPtr = firstTimerHandlerPtr; - if ((flags & TCL_TIMER_EVENTS) && (timerHandlerPtr != NULL)) { - TclpGetTime(&curTime); - gotTime = 1; - if ((timerHandlerPtr->time.sec < curTime.sec) - || ((timerHandlerPtr->time.sec == curTime.sec) - && (timerHandlerPtr->time.usec <= curTime.usec))) { - triggered = 1; - } - } - timerHandlerPtr = firstModalHandlerPtr; - if (timerHandlerPtr != NULL) { - if (!gotTime) { - TclpGetTime(&curTime); - } - if ((timerHandlerPtr->time.sec < curTime.sec) - || ((timerHandlerPtr->time.sec == curTime.sec) - && (timerHandlerPtr->time.usec <= curTime.usec))) { - triggered = 1; - } - } - if (triggered) { - timerEvPtr = (TimerEvent *) ckalloc(sizeof(TimerEvent)); - timerEvPtr->header.proc = TimerHandlerEventProc; - timerEvPtr->time.sec = curTime.sec; - timerEvPtr->time.usec = curTime.usec; - Tcl_QueueEvent((Tcl_Event *) timerEvPtr, TCL_QUEUE_TAIL); - } -} - -/* - *---------------------------------------------------------------------- - * - * TimerHandlerExitProc -- - * - * Callback invoked during exit cleanup to destroy the timer event - * source. - * - * Results: - * None. - * - * Side effects: - * Destroys the timer event source. - * - *---------------------------------------------------------------------- - */ - - /* ARGSUSED */ -static void -TimerHandlerExitProc(clientData) - ClientData clientData; /* Not used. */ -{ - Tcl_DeleteEventSource(TimerHandlerSetupProc, TimerHandlerCheckProc, - (ClientData) NULL); -} - -/* - *---------------------------------------------------------------------- - * - * TimerHandlerEventProc -- - * - * This procedure is called by Tcl_DoOneEvent when a timer event - * reaches the front of the event queue. This procedure handles - * the event by invoking the callbacks for all timers that are - * ready. - * - * 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_TIMER_EVENTS flag bit isn't set. - * - * Side effects: - * Whatever the timer handler callback procedures do. - * - *---------------------------------------------------------------------- - */ - -static int -TimerHandlerEventProc(evPtr, flags) - Tcl_Event *evPtr; /* Event to service. */ - int flags; /* Flags that indicate what events to - * handle, such as TCL_FILE_EVENTS. */ -{ - TimerHandler *timerHandlerPtr; - TimerEvent *timerEvPtr = (TimerEvent *) evPtr; - - /* - * Invoke the current modal timeout first, if there is one and - * it has triggered. - */ - - timerHandlerPtr = firstModalHandlerPtr; - if (firstModalHandlerPtr != NULL) { - if ((timerHandlerPtr->time.sec < timerEvPtr->time.sec) - || ((timerHandlerPtr->time.sec == timerEvPtr->time.sec) - && (timerHandlerPtr->time.usec <= timerEvPtr->time.usec))) { - (*timerHandlerPtr->proc)(timerHandlerPtr->clientData); - } - } - - /* - * Invoke any normal timers that have fired. - */ - - if (!(flags & TCL_TIMER_EVENTS)) { - return 1; - } - - while (1) { - timerHandlerPtr = firstTimerHandlerPtr; - if (timerHandlerPtr == NULL) { - break; - } - if ((timerHandlerPtr->time.sec > timerEvPtr->time.sec) - || ((timerHandlerPtr->time.sec == timerEvPtr->time.sec) - && (timerHandlerPtr->time.usec >= timerEvPtr->time.usec))) { - break; - } - - /* - * Remove the handler from the queue before invoking it, - * to avoid potential reentrancy problems. - */ - - firstTimerHandlerPtr = timerHandlerPtr->nextPtr; - (*timerHandlerPtr->proc)(timerHandlerPtr->clientData); - ckfree((char *) timerHandlerPtr); - } - return 1; -} - -/* - *-------------------------------------------------------------- - * - * Tcl_DoWhenIdle -- - * - * Arrange for proc to be invoked the next time the system is - * idle (i.e., just before the next time that Tcl_DoOneEvent - * would have to wait for something to happen). - * - * Results: - * None. - * - * Side effects: - * Proc will eventually be called, with clientData as argument. - * See the manual entry for details. - * - *-------------------------------------------------------------- - */ - -void -Tcl_DoWhenIdle(proc, clientData) - Tcl_IdleProc *proc; /* Procedure to invoke. */ - ClientData clientData; /* Arbitrary value to pass to proc. */ -{ - register IdleHandler *idlePtr; - - idlePtr = (IdleHandler *) ckalloc(sizeof(IdleHandler)); - idlePtr->proc = proc; - idlePtr->clientData = clientData; - idlePtr->generation = idleGeneration; - idlePtr->nextPtr = NULL; - if (lastIdlePtr == NULL) { - idleList = idlePtr; - } else { - lastIdlePtr->nextPtr = idlePtr; - } - lastIdlePtr = idlePtr; -} - -/* - *---------------------------------------------------------------------- - * - * Tcl_CancelIdleCall -- - * - * If there are any when-idle calls requested to a given procedure - * with given clientData, cancel all of them. - * - * Results: - * None. - * - * Side effects: - * If the proc/clientData combination were on the when-idle list, - * they are removed so that they will never be called. - * - *---------------------------------------------------------------------- - */ - -void -Tcl_CancelIdleCall(proc, clientData) - Tcl_IdleProc *proc; /* Procedure that was previously registered. */ - ClientData clientData; /* Arbitrary value to pass to proc. */ -{ - register IdleHandler *idlePtr, *prevPtr; - IdleHandler *nextPtr; - - for (prevPtr = NULL, idlePtr = idleList; idlePtr != NULL; - prevPtr = idlePtr, idlePtr = idlePtr->nextPtr) { - while ((idlePtr->proc == proc) - && (idlePtr->clientData == clientData)) { - nextPtr = idlePtr->nextPtr; - ckfree((char *) idlePtr); - idlePtr = nextPtr; - if (prevPtr == NULL) { - idleList = idlePtr; - } else { - prevPtr->nextPtr = idlePtr; - } - if (idlePtr == NULL) { - lastIdlePtr = prevPtr; - return; - } - } - } -} - -/* - *---------------------------------------------------------------------- - * - * TclIdlePending -- - * - * This function is called by the notifier subsystem to determine - * whether there are any idle handlers currently scheduled. - * - * Results: - * Returns 0 if the idle list is empty, otherwise it returns 1. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -int -TclIdlePending() -{ - return (idleList == NULL) ? 0 : 1; -} - -/* - *---------------------------------------------------------------------- - * - * TclServiceIdle -- - * - * This procedure is invoked by the notifier when it becomes idle. - * - * Results: - * The return value is 1 if the procedure actually found an idle - * handler to invoke. If no handler was found then 0 is returned. - * - * Side effects: - * Invokes all pending idle handlers. - * - *---------------------------------------------------------------------- - */ - -int -TclServiceIdle() -{ - IdleHandler *idlePtr; - int oldGeneration; - int foundIdle; - - if (idleList == NULL) { - return 0; - } - - foundIdle = 0; - oldGeneration = idleGeneration; - idleGeneration++; - - /* - * The code below is trickier than it may look, for the following - * reasons: - * - * 1. New handlers can get added to the list while the current - * one is being processed. If new ones get added, we don't - * want to process them during this pass through the list (want - * to check for other work to do first). This is implemented - * using the generation number in the handler: new handlers - * will have a different generation than any of the ones currently - * on the list. - * 2. The handler can call Tcl_DoOneEvent, so we have to remove - * the handler from the list before calling it. Otherwise an - * infinite loop could result. - * 3. Tcl_CancelIdleCall can be called to remove an element from - * the list while a handler is executing, so the list could - * change structure during the call. - */ - - for (idlePtr = idleList; - ((idlePtr != NULL) - && ((oldGeneration - idlePtr->generation) >= 0)); - idlePtr = idleList) { - idleList = idlePtr->nextPtr; - if (idleList == NULL) { - lastIdlePtr = NULL; - } - foundIdle = 1; - (*idlePtr->proc)(idlePtr->clientData); - ckfree((char *) idlePtr); - } - - return foundIdle; -} - -/* *---------------------------------------------------------------------- * * Tcl_BackgroundError -- @@ -1241,7 +125,7 @@ Tcl_BackgroundError(interp) * occurred. */ { BgError *errPtr; - char *varValue; + char *errResult, *varValue; ErrAssocData *assocPtr; /* @@ -1253,11 +137,13 @@ Tcl_BackgroundError(interp) */ Tcl_AddErrorInfo(interp, ""); + + errResult = TclGetStringFromObj(Tcl_GetObjResult(interp), (int *) NULL); + errPtr = (BgError *) ckalloc(sizeof(BgError)); errPtr->interp = interp; - errPtr->errorMsg = (char *) ckalloc((unsigned) (strlen(interp->result) - + 1)); - strcpy(errPtr->errorMsg, interp->result); + errPtr->errorMsg = (char *) ckalloc((unsigned) (strlen(errResult) + 1)); + strcpy(errPtr->errorMsg, errResult); varValue = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY); if (varValue == NULL) { varValue = errPtr->errorMsg; @@ -1327,10 +213,12 @@ HandleBgErrors(clientData) ErrAssocData *assocPtr = (ErrAssocData *) clientData; Tcl_Channel errChannel; + Tcl_Preserve((ClientData) assocPtr); + while (assocPtr->firstBgPtr != NULL) { interp = assocPtr->firstBgPtr->interp; if (interp == NULL) { - goto doneWithReport; + goto doneWithInterp; } /* @@ -1357,6 +245,45 @@ HandleBgErrors(clientData) if (code == TCL_ERROR) { /* + * If the interpreter is safe, we look for a hidden command + * named "bgerror" and call that with the error information. + * Otherwise, simply ignore the error. The rationale is that + * this could be an error caused by a malicious applet trying + * to cause an infinite barrage of error messages. The hidden + * "bgerror" command can be used by a security policy to + * interpose on such attacks and e.g. kill the applet after a + * few attempts. + */ + + if (Tcl_IsSafe(interp)) { + Tcl_HashTable *hTblPtr; + Tcl_HashEntry *hPtr; + + hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp, + "tclHiddenCmds", NULL); + if (hTblPtr == (Tcl_HashTable *) NULL) { + goto doneWithInterp; + } + hPtr = Tcl_FindHashEntry(hTblPtr, "bgerror"); + if (hPtr == (Tcl_HashEntry *) NULL) { + goto doneWithInterp; + } + + /* + * OK, the hidden command "bgerror" exists, invoke it. + */ + + argv[0] = "bgerror"; + argv[1] = ckalloc((unsigned) + strlen(assocPtr->firstBgPtr->errorMsg)); + strcpy(argv[1], assocPtr->firstBgPtr->errorMsg); + (void) TclInvoke(interp, 2, argv, TCL_INVOKE_HIDDEN); + ckfree(argv[1]); + + goto doneWithInterp; + } + + /* * We have to get the error output channel at the latest possible * time, because the eval (above) might have changed the channel. */ @@ -1397,21 +324,28 @@ HandleBgErrors(clientData) } } - Tcl_Release((ClientData) interp); - /* * Discard the command and the information about the error report. */ - doneWithReport: - ckfree(assocPtr->firstBgPtr->errorMsg); - ckfree(assocPtr->firstBgPtr->errorInfo); - ckfree(assocPtr->firstBgPtr->errorCode); - errPtr = assocPtr->firstBgPtr->nextPtr; - ckfree((char *) assocPtr->firstBgPtr); - assocPtr->firstBgPtr = errPtr; +doneWithInterp: + + if (assocPtr->firstBgPtr) { + ckfree(assocPtr->firstBgPtr->errorMsg); + ckfree(assocPtr->firstBgPtr->errorInfo); + ckfree(assocPtr->firstBgPtr->errorCode); + errPtr = assocPtr->firstBgPtr->nextPtr; + ckfree((char *) assocPtr->firstBgPtr); + assocPtr->firstBgPtr = errPtr; + } + + if (interp != NULL) { + Tcl_Release((ClientData) interp); + } } assocPtr->lastBgPtr = NULL; + + Tcl_Release((ClientData) assocPtr); } /* @@ -1450,8 +384,8 @@ BgErrorDeleteProc(clientData, interp) ckfree(errPtr->errorCode); ckfree((char *) errPtr); } - ckfree((char *) assocPtr); Tcl_CancelIdleCall(HandleBgErrors, (ClientData) assocPtr); + Tcl_EventuallyFree((ClientData) assocPtr, TCL_DYNAMIC); } /* @@ -1549,415 +483,82 @@ Tcl_Exit(status) int status; /* Exit status for application; typically * 0 for normal return, 1 for error return. */ { - ExitHandler *exitPtr; - - for (exitPtr = firstExitPtr; exitPtr != NULL; exitPtr = firstExitPtr) { - /* - * Be careful to remove the handler from the list before invoking - * its callback. This protects us against double-freeing if the - * callback should call Tcl_DeleteExitHandler on itself. - */ - - firstExitPtr = exitPtr->nextPtr; - (*exitPtr->proc)(exitPtr->clientData); - ckfree((char *) exitPtr); - } + Tcl_Finalize(); #ifdef TCL_MEM_DEBUG if (tclMemDumpFileName != NULL) { Tcl_DumpActiveMemory(tclMemDumpFileName); } #endif - TclPlatformExit(status); } /* *---------------------------------------------------------------------- * - * Tcl_AfterCmd -- + * Tcl_Finalize -- * - * This procedure is invoked to process the "after" Tcl command. - * See the user documentation for details on what it does. - * - * Results: - * A standard Tcl result. - * - * Side effects: - * See the user documentation. - * - *---------------------------------------------------------------------- - */ - - /* ARGSUSED */ -int -Tcl_AfterCmd(clientData, interp, argc, argv) - ClientData clientData; /* Points to the "tclAfter" assocData for - * this interpreter, or NULL if the assocData - * hasn't been created yet.*/ - Tcl_Interp *interp; /* Current interpreter. */ - int argc; /* Number of arguments. */ - char **argv; /* Argument strings. */ -{ - /* - * The variable below is used to generate unique identifiers for - * after commands. This id can wrap around, which can potentially - * cause problems. However, there are not likely to be problems - * in practice, because after commands can only be requested to - * about a month in the future, and wrap-around is unlikely to - * occur in less than about 1-10 years. Thus it's unlikely that - * any old ids will still be around when wrap-around occurs. - */ - - static int nextId = 1; - int ms; - AfterInfo *afterPtr; - AfterAssocData *assocPtr = (AfterAssocData *) clientData; - Tcl_CmdInfo cmdInfo; - size_t length; - - if (argc < 2) { - Tcl_AppendResult(interp, "wrong # args: should be \"", - argv[0], " option ?arg arg ...?\"", (char *) NULL); - return TCL_ERROR; - } - - /* - * Create the "after" information associated for this interpreter, - * if it doesn't already exist. Associate it with the command too, - * so that it will be passed in as the ClientData argument in the - * future. - */ - - if (assocPtr == NULL) { - assocPtr = (AfterAssocData *) ckalloc(sizeof(AfterAssocData)); - assocPtr->interp = interp; - assocPtr->firstAfterPtr = NULL; - Tcl_SetAssocData(interp, "tclAfter", AfterCleanupProc, - (ClientData) assocPtr); - cmdInfo.proc = Tcl_AfterCmd; - cmdInfo.clientData = (ClientData) assocPtr; - cmdInfo.deleteProc = NULL; - cmdInfo.deleteData = (ClientData) assocPtr; - Tcl_SetCommandInfo(interp, argv[0], &cmdInfo); - } - - /* - * Parse the command. - */ - - length = strlen(argv[1]); - if (isdigit(UCHAR(argv[1][0]))) { - if (Tcl_GetInt(interp, argv[1], &ms) != TCL_OK) { - return TCL_ERROR; - } - if (ms < 0) { - ms = 0; - } - if (argc == 2) { - Tcl_Sleep(ms); - return TCL_OK; - } - afterPtr = (AfterInfo *) ckalloc((unsigned) (sizeof(AfterInfo))); - afterPtr->assocPtr = assocPtr; - if (argc == 3) { - afterPtr->command = (char *) ckalloc((unsigned) - (strlen(argv[2]) + 1)); - strcpy(afterPtr->command, argv[2]); - } else { - afterPtr->command = Tcl_Concat(argc-2, argv+2); - } - afterPtr->id = nextId; - nextId += 1; - afterPtr->token = Tcl_CreateTimerHandler(ms, AfterProc, - (ClientData) afterPtr); - afterPtr->nextPtr = assocPtr->firstAfterPtr; - assocPtr->firstAfterPtr = afterPtr; - sprintf(interp->result, "after#%d", afterPtr->id); - } else if (strncmp(argv[1], "cancel", length) == 0) { - char *arg; - - if (argc < 3) { - Tcl_AppendResult(interp, "wrong # args: should be \"", - argv[0], " cancel id|command\"", (char *) NULL); - return TCL_ERROR; - } - if (argc == 3) { - arg = argv[2]; - } else { - arg = Tcl_Concat(argc-2, argv+2); - } - for (afterPtr = assocPtr->firstAfterPtr; afterPtr != NULL; - afterPtr = afterPtr->nextPtr) { - if (strcmp(afterPtr->command, arg) == 0) { - break; - } - } - if (afterPtr == NULL) { - afterPtr = GetAfterEvent(assocPtr, arg); - } - if (arg != argv[2]) { - ckfree(arg); - } - if (afterPtr != NULL) { - if (afterPtr->token != NULL) { - Tcl_DeleteTimerHandler(afterPtr->token); - } else { - Tcl_CancelIdleCall(AfterProc, (ClientData) afterPtr); - } - FreeAfterPtr(afterPtr); - } - } else if ((strncmp(argv[1], "idle", length) == 0) - && (length >= 2)) { - if (argc < 3) { - Tcl_AppendResult(interp, "wrong # args: should be \"", - argv[0], " idle script script ...\"", (char *) NULL); - return TCL_ERROR; - } - afterPtr = (AfterInfo *) ckalloc((unsigned) (sizeof(AfterInfo))); - afterPtr->assocPtr = assocPtr; - if (argc == 3) { - afterPtr->command = (char *) ckalloc((unsigned) - (strlen(argv[2]) + 1)); - strcpy(afterPtr->command, argv[2]); - } else { - afterPtr->command = Tcl_Concat(argc-2, argv+2); - } - afterPtr->id = nextId; - nextId += 1; - afterPtr->token = NULL; - afterPtr->nextPtr = assocPtr->firstAfterPtr; - assocPtr->firstAfterPtr = afterPtr; - Tcl_DoWhenIdle(AfterProc, (ClientData) afterPtr); - sprintf(interp->result, "after#%d", afterPtr->id); - } else if ((strncmp(argv[1], "info", length) == 0) - && (length >= 2)) { - if (argc == 2) { - char buffer[30]; - - for (afterPtr = assocPtr->firstAfterPtr; afterPtr != NULL; - afterPtr = afterPtr->nextPtr) { - if (assocPtr->interp == interp) { - sprintf(buffer, "after#%d", afterPtr->id); - Tcl_AppendElement(interp, buffer); - } - } - return TCL_OK; - } - if (argc != 3) { - Tcl_AppendResult(interp, "wrong # args: should be \"", - argv[0], " info ?id?\"", (char *) NULL); - return TCL_ERROR; - } - afterPtr = GetAfterEvent(assocPtr, argv[2]); - if (afterPtr == NULL) { - Tcl_AppendResult(interp, "event \"", argv[2], - "\" doesn't exist", (char *) NULL); - return TCL_ERROR; - } - Tcl_AppendElement(interp, afterPtr->command); - Tcl_AppendElement(interp, - (afterPtr->token == NULL) ? "idle" : "timer"); - } else { - Tcl_AppendResult(interp, "bad argument \"", argv[1], - "\": must be cancel, idle, info, or a number", - (char *) NULL); - return TCL_ERROR; - } - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * GetAfterEvent -- - * - * This procedure parses an "after" id such as "after#4" and - * returns a pointer to the AfterInfo structure. - * - * Results: - * The return value is either a pointer to an AfterInfo structure, - * if one is found that corresponds to "string" and is for interp, - * or NULL if no corresponding after event can be found. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -static AfterInfo * -GetAfterEvent(assocPtr, string) - AfterAssocData *assocPtr; /* Points to "after"-related information for - * this interpreter. */ - char *string; /* Textual identifier for after event, such - * as "after#6". */ -{ - AfterInfo *afterPtr; - int id; - char *end; - - if (strncmp(string, "after#", 6) != 0) { - return NULL; - } - string += 6; - id = strtoul(string, &end, 10); - if ((end == string) || (*end != 0)) { - return NULL; - } - for (afterPtr = assocPtr->firstAfterPtr; afterPtr != NULL; - afterPtr = afterPtr->nextPtr) { - if (afterPtr->id == id) { - return afterPtr; - } - } - return NULL; -} - -/* - *---------------------------------------------------------------------- - * - * AfterProc -- - * - * Timer callback to execute commands registered with the - * "after" command. + * Runs the exit handlers to allow Tcl to clean up its state prior + * to being unloaded. Called by Tcl_Exit and when Tcl was dynamically + * loaded and is now being unloaded. * * Results: * None. * * Side effects: - * Executes whatever command was specified. If the command - * returns an error, then the command "bgerror" is invoked - * to process the error; if bgerror fails then information - * about the error is output on stderr. + * Whatever the exit handlers do. Also frees up storage associated + * with the Tcl object type table. * *---------------------------------------------------------------------- */ -static void -AfterProc(clientData) - ClientData clientData; /* Describes command to execute. */ +void +Tcl_Finalize() { - AfterInfo *afterPtr = (AfterInfo *) clientData; - AfterAssocData *assocPtr = afterPtr->assocPtr; - AfterInfo *prevPtr; - int result; - Tcl_Interp *interp; - - /* - * First remove the callback from our list of callbacks; otherwise - * someone could delete the callback while it's being executed, which - * could cause a core dump. - */ + ExitHandler *exitPtr; + + tclInExit = 1; + for (exitPtr = firstExitPtr; exitPtr != NULL; exitPtr = firstExitPtr) { + /* + * Be careful to remove the handler from the list before invoking + * its callback. This protects us against double-freeing if the + * callback should call Tcl_DeleteExitHandler on itself. + */ - if (assocPtr->firstAfterPtr == afterPtr) { - assocPtr->firstAfterPtr = afterPtr->nextPtr; - } else { - for (prevPtr = assocPtr->firstAfterPtr; prevPtr->nextPtr != afterPtr; - prevPtr = prevPtr->nextPtr) { - /* Empty loop body. */ - } - prevPtr->nextPtr = afterPtr->nextPtr; + firstExitPtr = exitPtr->nextPtr; + (*exitPtr->proc)(exitPtr->clientData); + ckfree((char *) exitPtr); } /* - * Execute the callback. + * Uninitialize everything associated with the compile and execute + * environment. This *must* be done at the latest possible time. */ - - interp = assocPtr->interp; - Tcl_Preserve((ClientData) interp); - result = Tcl_GlobalEval(interp, afterPtr->command); - if (result != TCL_OK) { - Tcl_AddErrorInfo(interp, "\n (\"after\" script)"); - Tcl_BackgroundError(interp); - } - Tcl_Release((ClientData) interp); - /* - * Free the memory for the callback. - */ - - ckfree(afterPtr->command); - ckfree((char *) afterPtr); + TclFinalizeCompExecEnv(); + firstExitPtr = NULL; + tclInExit = 0; } /* *---------------------------------------------------------------------- * - * FreeAfterPtr -- + * TclInExit -- * - * This procedure removes an "after" command from the list of - * those that are pending and frees its resources. This procedure - * does *not* cancel the timer handler; if that's needed, the - * caller must do it. + * Determines if we are in the middle of exit-time cleanup. * * Results: - * None. + * If we are in the middle of exiting, 1, otherwise 0. * * Side effects: - * The memory associated with afterPtr is released. - * - *---------------------------------------------------------------------- - */ - -static void -FreeAfterPtr(afterPtr) - AfterInfo *afterPtr; /* Command to be deleted. */ -{ - AfterInfo *prevPtr; - AfterAssocData *assocPtr = afterPtr->assocPtr; - - if (assocPtr->firstAfterPtr == afterPtr) { - assocPtr->firstAfterPtr = afterPtr->nextPtr; - } else { - for (prevPtr = assocPtr->firstAfterPtr; prevPtr->nextPtr != afterPtr; - prevPtr = prevPtr->nextPtr) { - /* Empty loop body. */ - } - prevPtr->nextPtr = afterPtr->nextPtr; - } - ckfree(afterPtr->command); - ckfree((char *) afterPtr); -} - -/* - *---------------------------------------------------------------------- - * - * AfterCleanupProc -- - * - * This procedure is invoked whenever an interpreter is deleted - * to cleanup the AssocData for "tclAfter". - * - * Results: * None. * - * Side effects: - * After commands are removed. - * *---------------------------------------------------------------------- */ - /* ARGSUSED */ -static void -AfterCleanupProc(clientData, interp) - ClientData clientData; /* Points to AfterAssocData for the - * interpreter. */ - Tcl_Interp *interp; /* Interpreter that is being deleted. */ +int +TclInExit() { - AfterAssocData *assocPtr = (AfterAssocData *) clientData; - AfterInfo *afterPtr; - - while (assocPtr->firstAfterPtr != NULL) { - afterPtr = assocPtr->firstAfterPtr; - assocPtr->firstAfterPtr = afterPtr->nextPtr; - if (afterPtr->token != NULL) { - Tcl_DeleteTimerHandler(afterPtr->token); - } else { - Tcl_CancelIdleCall(AfterProc, (ClientData) afterPtr); - } - ckfree(afterPtr->command); - ckfree((char *) afterPtr); - } - ckfree((char *) assocPtr); + return tclInExit; } /* @@ -1992,13 +593,15 @@ Tcl_VwaitCmd(clientData, interp, argc, argv) argv[0], " name\"", (char *) NULL); return TCL_ERROR; } - Tcl_TraceVar(interp, argv[1], + if (Tcl_TraceVar(interp, argv[1], TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, - VwaitVarProc, (ClientData) &done); + VwaitVarProc, (ClientData) &done) != TCL_OK) { + return TCL_ERROR; + }; done = 0; foundEvent = 1; while (!done && foundEvent) { - foundEvent = Tcl_DoOneEvent(0); + foundEvent = Tcl_DoOneEvent(TCL_ALL_EVENTS); } Tcl_UntraceVar(interp, argv[1], TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, @@ -2058,8 +661,7 @@ Tcl_UpdateCmd(clientData, interp, argc, argv) int argc; /* Number of arguments. */ char **argv; /* Argument strings. */ { - int flags = 0; /* Initialization needed only to stop - * compiler warnings. */ + int flags; if (argc == 1) { flags = TCL_ALL_EVENTS|TCL_DONT_WAIT; @@ -2069,7 +671,7 @@ Tcl_UpdateCmd(clientData, interp, argc, argv) "\": must be idletasks", (char *) NULL); return TCL_ERROR; } - flags = TCL_IDLE_EVENTS|TCL_DONT_WAIT; + flags = TCL_WINDOW_EVENTS|TCL_IDLE_EVENTS|TCL_DONT_WAIT; } else { Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " ?idletasks?\"", (char *) NULL); @@ -2088,100 +690,3 @@ Tcl_UpdateCmd(clientData, interp, argc, argv) Tcl_ResetResult(interp); return TCL_OK; } - -/* - *---------------------------------------------------------------------- - * - * TclWaitForFile -- - * - * This procedure waits synchronously for a file to become readable - * or writable, with an optional timeout. - * - * Results: - * The return value is an OR'ed combination of TCL_READABLE, - * TCL_WRITABLE, and TCL_EXCEPTION, indicating the conditions - * that are present on file at the time of the return. This - * procedure will not return until either "timeout" milliseconds - * have elapsed or at least one of the conditions given by mask - * has occurred for file (a return value of 0 means that a timeout - * occurred). No normal events will be serviced during the - * execution of this procedure. - * - * Side effects: - * Time passes. - * - *---------------------------------------------------------------------- - */ - -int -TclWaitForFile(file, mask, timeout) - Tcl_File file; /* Handle for file on which to wait. */ - int mask; /* What to wait for: OR'ed combination of - * TCL_READABLE, TCL_WRITABLE, and - * TCL_EXCEPTION. */ - int timeout; /* Maximum amount of time to wait for one - * of the conditions in mask to occur, in - * milliseconds. A value of 0 means don't - * wait at all, and a value of -1 means - * wait forever. */ -{ - Tcl_Time abortTime, now, blockTime; - int present; - - /* - * If there is a non-zero finite timeout, compute the time when - * we give up. - */ - - if (timeout > 0) { - TclpGetTime(&now); - abortTime.sec = now.sec + timeout/1000; - abortTime.usec = now.usec + (timeout%1000)*1000; - if (abortTime.usec >= 1000000) { - abortTime.usec -= 1000000; - abortTime.sec += 1; - } - } - - /* - * Loop in a mini-event loop of our own, waiting for either the - * file to become ready or a timeout to occur. - */ - - while (1) { - Tcl_WatchFile(file, mask); - if (timeout > 0) { - blockTime.sec = abortTime.sec - now.sec; - blockTime.usec = abortTime.usec - now.usec; - if (blockTime.usec < 0) { - blockTime.sec -= 1; - blockTime.usec += 1000000; - } - if (blockTime.sec < 0) { - blockTime.sec = 0; - blockTime.usec = 0; - } - Tcl_WaitForEvent(&blockTime); - } else if (timeout == 0) { - blockTime.sec = 0; - blockTime.usec = 0; - Tcl_WaitForEvent(&blockTime); - } else { - Tcl_WaitForEvent((Tcl_Time *) NULL); - } - present = Tcl_FileReady(file, mask); - if (present != 0) { - break; - } - if (timeout == 0) { - break; - } - TclpGetTime(&now); - if ((abortTime.sec < now.sec) - || ((abortTime.sec == now.sec) - && (abortTime.usec <= now.usec))) { - break; - } - } - return present; -} |