tcltimer.c

来自「tcl是工具命令语言」· C语言 代码 · 共 1,143 行 · 第 1/3 页

C
1,143
字号
}/* *---------------------------------------------------------------------- * * TimerCheckProc -- * *	This function is called by Tcl_DoOneEvent to check the timer *	event source for events.  This routine checks both the *	idle and after timer lists. * * Results: *	None. * * Side effects: *	May queue an event and update the maximum notifier block time. * *---------------------------------------------------------------------- */static voidTimerCheckProc(data, flags)    ClientData data;		/* Not used. */    int flags;			/* Event flags as passed to Tcl_DoOneEvent. */{    Tcl_Event *timerEvPtr;    Tcl_Time blockTime;    ThreadSpecificData *tsdPtr = InitTimer();    if ((flags & TCL_TIMER_EVENTS) && tsdPtr->firstTimerHandlerPtr) {	/*	 * Compute the timeout for the next timer on the list.	 */	Tcl_GetTime(&blockTime);	blockTime.sec = tsdPtr->firstTimerHandlerPtr->time.sec - blockTime.sec;	blockTime.usec = tsdPtr->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;	}	/*	 * If the first timer has expired, stick an event on the queue.	 */	if (blockTime.sec == 0 && blockTime.usec == 0 &&		!tsdPtr->timerPending) {	    tsdPtr->timerPending = 1;	    timerEvPtr = (Tcl_Event *) ckalloc(sizeof(Tcl_Event));	    timerEvPtr->proc = TimerHandlerEventProc;	    Tcl_QueueEvent(timerEvPtr, TCL_QUEUE_TAIL);	}    }}/* *---------------------------------------------------------------------- * * TimerHandlerEventProc -- * *	This procedure is called by Tcl_ServiceEvent 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 intTimerHandlerEventProc(evPtr, flags)    Tcl_Event *evPtr;		/* Event to service. */    int flags;			/* Flags that indicate what events to				 * handle, such as TCL_FILE_EVENTS. */{    TimerHandler *timerHandlerPtr, **nextPtrPtr;    Tcl_Time time;    int currentTimerId;    ThreadSpecificData *tsdPtr = InitTimer();    /*     * Do nothing if timers aren't enabled.  This leaves the event on the     * queue, so we will get to it as soon as ServiceEvents() is called     * with timers enabled.     */    if (!(flags & TCL_TIMER_EVENTS)) {	return 0;    }    /*     * 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 to avoid     *	  starving other event sources.  This is implemented using the     *	  token number in the handler:  new handlers will have a     *    newer token 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_DeleteTimerHandler can be called to remove an element from     *    the list while a handler is executing, so the list could     *    change structure during the call.     * 4. Because we only fetch the current time before entering the loop,     *    the only way a new timer will even be considered runnable is if     *	  its expiration time is within the same millisecond as the     *	  current time.  This is fairly likely on Windows, since it has     *	  a course granularity clock.  Since timers are placed     *	  on the queue in time order with the most recently created     *    handler appearing after earlier ones with the same expiration     *	  time, we don't have to worry about newer generation timers     *	  appearing before later ones.     */    tsdPtr->timerPending = 0;    currentTimerId = tsdPtr->lastTimerId;    Tcl_GetTime(&time);    while (1) {	nextPtrPtr = &tsdPtr->firstTimerHandlerPtr;	timerHandlerPtr = tsdPtr->firstTimerHandlerPtr;	if (timerHandlerPtr == NULL) {	    break;	}	    	if ((timerHandlerPtr->time.sec > time.sec)		|| ((timerHandlerPtr->time.sec == time.sec)			&& (timerHandlerPtr->time.usec > time.usec))) {	    break;	}	/*	 * Bail out if the next timer is of a newer generation.	 */	if ((currentTimerId - (int)timerHandlerPtr->token) < 0) {	    break;	}	/*	 * Remove the handler from the queue before invoking it,	 * to avoid potential reentrancy problems.	 */	(*nextPtrPtr) = timerHandlerPtr->nextPtr;	(*timerHandlerPtr->proc)(timerHandlerPtr->clientData);	ckfree((char *) timerHandlerPtr);    }    TimerSetupProc(NULL, TCL_TIMER_EVENTS);    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. * *-------------------------------------------------------------- */voidTcl_DoWhenIdle(proc, clientData)    Tcl_IdleProc *proc;		/* Procedure to invoke. */    ClientData clientData;	/* Arbitrary value to pass to proc. */{    register IdleHandler *idlePtr;    Tcl_Time blockTime;    ThreadSpecificData *tsdPtr = InitTimer();    idlePtr = (IdleHandler *) ckalloc(sizeof(IdleHandler));    idlePtr->proc = proc;    idlePtr->clientData = clientData;    idlePtr->generation = tsdPtr->idleGeneration;    idlePtr->nextPtr = NULL;    if (tsdPtr->lastIdlePtr == NULL) {	tsdPtr->idleList = idlePtr;    } else {	tsdPtr->lastIdlePtr->nextPtr = idlePtr;    }    tsdPtr->lastIdlePtr = idlePtr;    blockTime.sec = 0;    blockTime.usec = 0;    Tcl_SetMaxBlockTime(&blockTime);}/* *---------------------------------------------------------------------- * * 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. * *---------------------------------------------------------------------- */voidTcl_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;    ThreadSpecificData *tsdPtr = InitTimer();    for (prevPtr = NULL, idlePtr = tsdPtr->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) {		tsdPtr->idleList = idlePtr;	    } else {		prevPtr->nextPtr = idlePtr;	    }	    if (idlePtr == NULL) {		tsdPtr->lastIdlePtr = prevPtr;		return;	    }	}    }}/* *---------------------------------------------------------------------- * * TclServiceIdle -- * *	This procedure is invoked by the notifier when it becomes *	idle.  It will invoke all idle handlers that are present at *	the time the call is invoked, but not those added during idle *	processing. * * Results: *	The return value is 1 if TclServiceIdle found something to *	do, otherwise return value is 0. * * Side effects: *	Invokes all pending idle handlers. * *---------------------------------------------------------------------- */intTclServiceIdle(){    IdleHandler *idlePtr;    int oldGeneration;    Tcl_Time blockTime;    ThreadSpecificData *tsdPtr = InitTimer();    if (tsdPtr->idleList == NULL) {	return 0;    }    oldGeneration = tsdPtr->idleGeneration;    tsdPtr->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 = tsdPtr->idleList;	    ((idlePtr != NULL)		    && ((oldGeneration - idlePtr->generation) >= 0));	    idlePtr = tsdPtr->idleList) {	tsdPtr->idleList = idlePtr->nextPtr;	if (tsdPtr->idleList == NULL) {	    tsdPtr->lastIdlePtr = NULL;	}	(*idlePtr->proc)(idlePtr->clientData);	ckfree((char *) idlePtr);    }    if (tsdPtr->idleList) {	blockTime.sec = 0;	blockTime.usec = 0;	Tcl_SetMaxBlockTime(&blockTime);    }    return 1;}/* *---------------------------------------------------------------------- * * Tcl_AfterObjCmd -- * *	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 */intTcl_AfterObjCmd(clientData, interp, objc, objv)    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 objc;			/* Number of arguments. */    Tcl_Obj *CONST objv[];	/* Argument objects. */{    int ms;    AfterInfo *afterPtr;    AfterAssocData *assocPtr = (AfterAssocData *) clientData;    Tcl_CmdInfo cmdInfo;    int length;    char *argString;    int index;    char buf[16 + TCL_INTEGER_SPACE];    static CONST char *afterSubCmds[] = {	"cancel", "idle", "info", (char *) NULL    };    enum afterSubCmds {AFTER_CANCEL, AFTER_IDLE, AFTER_INFO};    ThreadSpecificData *tsdPtr = InitTimer();    if (objc < 2) {	Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?");	return TCL_ERROR;    }    /*     * Create the "after" information associated for this interpreter,

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?