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 + -
显示快捷键?