📄 tcltimer.c
字号:
/* * tclTimer.c -- * * This file provides timer event management facilities for Tcl, * including the "after" command. * * Copyright (c) 1997 by 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: @(#) tclTimer.c 1.9 97/07/29 16:21:53 */#include "tclInt.h"#include "tclPort.h"/* * This flag indicates whether this module has been initialized. */static int initialized = 0;/* * For each timer callback that's pending 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 handler so it can be * deleted. */ struct TimerHandler *nextPtr; /* Next event in queue, or NULL for * end of queue. */} TimerHandler;static TimerHandler *firstTimerHandlerPtr = NULL; /* First event in queue. */static int lastTimerId; /* Timer identifier of most recently * created timer. */static int timerPending; /* 1 if a timer event is in the queue. *//* * 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;/* * 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; /* First in list of all idle handlers. */static IdleHandler *lastIdlePtr; /* Last in list (or NULL for empty list). */static int idleGeneration; /* 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. *//* * 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 FreeAfterPtr _ANSI_ARGS_((AfterInfo *afterPtr));static AfterInfo * GetAfterEvent _ANSI_ARGS_((AfterAssocData *assocPtr, char *string));static void InitTimer _ANSI_ARGS_((void));static void TimerExitProc _ANSI_ARGS_((ClientData clientData));static int TimerHandlerEventProc _ANSI_ARGS_((Tcl_Event *evPtr, int flags));static void TimerCheckProc _ANSI_ARGS_((ClientData clientData, int flags));static void TimerSetupProc _ANSI_ARGS_((ClientData clientData, int flags));/* *---------------------------------------------------------------------- * * InitTimer -- * * This function initializes the timer module. * * Results: * None. * * Side effects: * Registers the idle and timer event sources. * *---------------------------------------------------------------------- */static voidInitTimer(){ initialized = 1; lastTimerId = 0; timerPending = 0; idleGeneration = 0; firstTimerHandlerPtr = NULL; lastIdlePtr = NULL; idleList = NULL; Tcl_CreateEventSource(TimerSetupProc, TimerCheckProc, NULL); Tcl_CreateExitHandler(TimerExitProc, NULL);}/* *---------------------------------------------------------------------- * * TimerExitProc -- * * This function is call at exit or unload time to remove the * timer and idle event sources. * * Results: * None. * * Side effects: * Removes the timer and idle event sources. * *---------------------------------------------------------------------- */static voidTimerExitProc(clientData) ClientData clientData; /* Not used. */{ Tcl_DeleteEventSource(TimerSetupProc, TimerCheckProc, NULL); initialized = 0;}/* *-------------------------------------------------------------- * * 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_TimerTokenTcl_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; Tcl_Time time; if (!initialized) { InitTimer(); } timerHandlerPtr = (TimerHandler *) ckalloc(sizeof(TimerHandler)); /* * Compute when the event should fire. */ TclpGetTime(&time); timerHandlerPtr->time.sec = time.sec + milliseconds/1000; timerHandlerPtr->time.usec = 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; lastTimerId++; timerHandlerPtr->token = (Tcl_TimerToken) lastTimerId; /* * 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; } TimerSetupProc(NULL, TCL_ALL_EVENTS); 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. * *-------------------------------------------------------------- */voidTcl_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; }}/* *---------------------------------------------------------------------- * * TimerSetupProc -- * * This function is called by Tcl_DoOneEvent to setup the timer * event source for before blocking. This routine checks both the * idle and after timer lists. * * Results: * None. * * Side effects: * May update the maximum notifier block time. * *---------------------------------------------------------------------- */static voidTimerSetupProc(data, flags) ClientData data; /* Not used. */ int flags; /* Event flags as passed to Tcl_DoOneEvent. */{ Tcl_Time blockTime; if (((flags & TCL_IDLE_EVENTS) && idleList) || ((flags & TCL_TIMER_EVENTS) && timerPending)) { /* * There is an idle handler or a pending timer event, so just poll. */ blockTime.sec = 0; blockTime.usec = 0; } else if ((flags & TCL_TIMER_EVENTS) && firstTimerHandlerPtr) { /* * Compute the timeout for the next timer on the list. */ TclpGetTime(&blockTime); blockTime.sec = firstTimerHandlerPtr->time.sec - blockTime.sec; blockTime.usec = firstTimerHandlerPtr->time.usec - blockTime.usec; if (blockTime.usec < 0) { blockTime.sec -= 1; blockTime.usec += 1000000; } if (blockTime.sec < 0) { blockTime.sec = 0; blockTime.usec = 0; } } else { return; } Tcl_SetMaxBlockTime(&blockTime);}/* *---------------------------------------------------------------------- * * TimerCheckProc -- * * This function is called by Tcl_DoOneEvent to check the timer * event source for events. This routine checks both the
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -