tclTimer.cGo to the documentation of this file.00001 /* 00002 * tclTimer.c -- 00003 * 00004 * This file provides timer event management facilities for Tcl, 00005 * including the "after" command. 00006 * 00007 * Copyright (c) 1997 by Sun Microsystems, Inc. 00008 * 00009 * See the file "license.terms" for information on usage and redistribution of 00010 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 00011 * 00012 * RCS: @(#) $Id: tclTimer.c,v 1.31 2008/01/22 20:52:10 dgp Exp $ 00013 */ 00014 00015 #include "tclInt.h" 00016 00017 /* 00018 * For each timer callback that's pending there is one record of the following 00019 * type. The normal handlers (created by Tcl_CreateTimerHandler) are chained 00020 * together in a list sorted by time (earliest event first). 00021 */ 00022 00023 typedef struct TimerHandler { 00024 Tcl_Time time; /* When timer is to fire. */ 00025 Tcl_TimerProc *proc; /* Function to call. */ 00026 ClientData clientData; /* Argument to pass to proc. */ 00027 Tcl_TimerToken token; /* Identifies handler so it can be deleted. */ 00028 struct TimerHandler *nextPtr; 00029 /* Next event in queue, or NULL for end of 00030 * queue. */ 00031 } TimerHandler; 00032 00033 /* 00034 * The data structure below is used by the "after" command to remember the 00035 * command to be executed later. All of the pending "after" commands for an 00036 * interpreter are linked together in a list. 00037 */ 00038 00039 typedef struct AfterInfo { 00040 struct AfterAssocData *assocPtr; 00041 /* Pointer to the "tclAfter" assocData for the 00042 * interp in which command will be 00043 * executed. */ 00044 Tcl_Obj *commandPtr; /* Command to execute. */ 00045 int id; /* Integer identifier for command; used to 00046 * cancel it. */ 00047 Tcl_TimerToken token; /* Used to cancel the "after" command. NULL 00048 * means that the command is run as an idle 00049 * handler rather than as a timer handler. 00050 * NULL means this is an "after idle" handler 00051 * rather than a timer handler. */ 00052 struct AfterInfo *nextPtr; /* Next in list of all "after" commands for 00053 * this interpreter. */ 00054 } AfterInfo; 00055 00056 /* 00057 * One of the following structures is associated with each interpreter for 00058 * which an "after" command has ever been invoked. A pointer to this structure 00059 * is stored in the AssocData for the "tclAfter" key. 00060 */ 00061 00062 typedef struct AfterAssocData { 00063 Tcl_Interp *interp; /* The interpreter for which this data is 00064 * registered. */ 00065 AfterInfo *firstAfterPtr; /* First in list of all "after" commands still 00066 * pending for this interpreter, or NULL if 00067 * none. */ 00068 } AfterAssocData; 00069 00070 /* 00071 * There is one of the following structures for each of the handlers declared 00072 * in a call to Tcl_DoWhenIdle. All of the currently-active handlers are 00073 * linked together into a list. 00074 */ 00075 00076 typedef struct IdleHandler { 00077 Tcl_IdleProc (*proc); /* Function to call. */ 00078 ClientData clientData; /* Value to pass to proc. */ 00079 int generation; /* Used to distinguish older handlers from 00080 * recently-created ones. */ 00081 struct IdleHandler *nextPtr;/* Next in list of active handlers. */ 00082 } IdleHandler; 00083 00084 /* 00085 * The timer and idle queues are per-thread because they are associated with 00086 * the notifier, which is also per-thread. 00087 * 00088 * All static variables used in this file are collected into a single instance 00089 * of the following structure. For multi-threaded implementations, there is 00090 * one instance of this structure for each thread. 00091 * 00092 * Notice that different structures with the same name appear in other files. 00093 * The structure defined below is used in this file only. 00094 */ 00095 00096 typedef struct ThreadSpecificData { 00097 TimerHandler *firstTimerHandlerPtr; /* First event in queue. */ 00098 int lastTimerId; /* Timer identifier of most recently created 00099 * timer. */ 00100 int timerPending; /* 1 if a timer event is in the queue. */ 00101 IdleHandler *idleList; /* First in list of all idle handlers. */ 00102 IdleHandler *lastIdlePtr; /* Last in list (or NULL for empty list). */ 00103 int idleGeneration; /* Used to fill in the "generation" fields of 00104 * IdleHandler structures. Increments each 00105 * time Tcl_DoOneEvent starts calling idle 00106 * handlers, so that all old handlers can be 00107 * called without calling any of the new ones 00108 * created by old ones. */ 00109 int afterId; /* For unique identifiers of after events. */ 00110 } ThreadSpecificData; 00111 00112 static Tcl_ThreadDataKey dataKey; 00113 00114 /* 00115 * Helper macros for working with times. TCL_TIME_BEFORE encodes how to write 00116 * the ordering relation on (normalized) times, and TCL_TIME_DIFF_MS computes 00117 * the number of milliseconds difference between two times. Both macros use 00118 * both of their arguments multiple times, so make sure they are cheap and 00119 * side-effect free. The "prototypes" for these macros are: 00120 * 00121 * static int TCL_TIME_BEFORE(Tcl_Time t1, Tcl_Time t2); 00122 * static long TCL_TIME_DIFF_MS(Tcl_Time t1, Tcl_Time t2); 00123 */ 00124 00125 #define TCL_TIME_BEFORE(t1, t2) \ 00126 (((t1).sec<(t2).sec) || ((t1).sec==(t2).sec && (t1).usec<(t2).usec)) 00127 00128 #define TCL_TIME_DIFF_MS(t1, t2) \ 00129 (1000*((Tcl_WideInt)(t1).sec - (Tcl_WideInt)(t2).sec) + \ 00130 ((long)(t1).usec - (long)(t2).usec)/1000) 00131 00132 /* 00133 * Prototypes for functions referenced only in this file: 00134 */ 00135 00136 static void AfterCleanupProc(ClientData clientData, 00137 Tcl_Interp *interp); 00138 static int AfterDelay(Tcl_Interp *interp, Tcl_WideInt ms); 00139 static void AfterProc(ClientData clientData); 00140 static void FreeAfterPtr(AfterInfo *afterPtr); 00141 static AfterInfo * GetAfterEvent(AfterAssocData *assocPtr, 00142 Tcl_Obj *commandPtr); 00143 static ThreadSpecificData *InitTimer(void); 00144 static void TimerExitProc(ClientData clientData); 00145 static int TimerHandlerEventProc(Tcl_Event *evPtr, int flags); 00146 static void TimerCheckProc(ClientData clientData, int flags); 00147 static void TimerSetupProc(ClientData clientData, int flags); 00148 00149 /* 00150 *---------------------------------------------------------------------- 00151 * 00152 * InitTimer -- 00153 * 00154 * This function initializes the timer module. 00155 * 00156 * Results: 00157 * A pointer to the thread specific data. 00158 * 00159 * Side effects: 00160 * Registers the idle and timer event sources. 00161 * 00162 *---------------------------------------------------------------------- 00163 */ 00164 00165 static ThreadSpecificData * 00166 InitTimer(void) 00167 { 00168 ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 00169 TclThreadDataKeyGet(&dataKey); 00170 00171 if (tsdPtr == NULL) { 00172 tsdPtr = TCL_TSD_INIT(&dataKey); 00173 Tcl_CreateEventSource(TimerSetupProc, TimerCheckProc, NULL); 00174 Tcl_CreateThreadExitHandler(TimerExitProc, NULL); 00175 } 00176 return tsdPtr; 00177 } 00178 00179 /* 00180 *---------------------------------------------------------------------- 00181 * 00182 * TimerExitProc -- 00183 * 00184 * This function is call at exit or unload time to remove the timer and 00185 * idle event sources. 00186 * 00187 * Results: 00188 * None. 00189 * 00190 * Side effects: 00191 * Removes the timer and idle event sources and remaining events. 00192 * 00193 *---------------------------------------------------------------------- 00194 */ 00195 00196 static void 00197 TimerExitProc( 00198 ClientData clientData) /* Not used. */ 00199 { 00200 ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 00201 TclThreadDataKeyGet(&dataKey); 00202 00203 Tcl_DeleteEventSource(TimerSetupProc, TimerCheckProc, NULL); 00204 if (tsdPtr != NULL) { 00205 register TimerHandler *timerHandlerPtr; 00206 00207 timerHandlerPtr = tsdPtr->firstTimerHandlerPtr; 00208 while (timerHandlerPtr != NULL) { 00209 tsdPtr->firstTimerHandlerPtr = timerHandlerPtr->nextPtr; 00210 ckfree((char *) timerHandlerPtr); 00211 timerHandlerPtr = tsdPtr->firstTimerHandlerPtr; 00212 } 00213 } 00214 } 00215 00216 /* 00217 *-------------------------------------------------------------- 00218 * 00219 * Tcl_CreateTimerHandler -- 00220 * 00221 * Arrange for a given function to be invoked at a particular time in the 00222 * future. 00223 * 00224 * Results: 00225 * The return value is a token for the timer event, which may be used to 00226 * delete the event before it fires. 00227 * 00228 * Side effects: 00229 * When milliseconds have elapsed, proc will be invoked exactly once. 00230 * 00231 *-------------------------------------------------------------- 00232 */ 00233 00234 Tcl_TimerToken 00235 Tcl_CreateTimerHandler( 00236 int milliseconds, /* How many milliseconds to wait before 00237 * invoking proc. */ 00238 Tcl_TimerProc *proc, /* Function to invoke. */ 00239 ClientData clientData) /* Arbitrary data to pass to proc. */ 00240 { 00241 Tcl_Time time; 00242 00243 /* 00244 * Compute when the event should fire. 00245 */ 00246 00247 Tcl_GetTime(&time); 00248 time.sec += milliseconds/1000; 00249 time.usec += (milliseconds%1000)*1000; 00250 if (time.usec >= 1000000) { 00251 time.usec -= 1000000; 00252 time.sec += 1; 00253 } 00254 return TclCreateAbsoluteTimerHandler(&time, proc, clientData); 00255 } 00256 00257 /* 00258 *-------------------------------------------------------------- 00259 * 00260 * TclCreateAbsoluteTimerHandler -- 00261 * 00262 * Arrange for a given function to be invoked at a particular time in the 00263 * future. 00264 * 00265 * Results: 00266 * The return value is a token for the timer event, which may be used to 00267 * delete the event before it fires. 00268 * 00269 * Side effects: 00270 * When the time in timePtr has been reached, proc will be invoked 00271 * exactly once. 00272 * 00273 *-------------------------------------------------------------- 00274 */ 00275 00276 Tcl_TimerToken 00277 TclCreateAbsoluteTimerHandler( 00278 Tcl_Time *timePtr, 00279 Tcl_TimerProc *proc, 00280 ClientData clientData) 00281 { 00282 register TimerHandler *timerHandlerPtr, *tPtr2, *prevPtr; 00283 ThreadSpecificData *tsdPtr; 00284 00285 tsdPtr = InitTimer(); 00286 timerHandlerPtr = (TimerHandler *) ckalloc(sizeof(TimerHandler)); 00287 00288 /* 00289 * Fill in fields for the event. 00290 */ 00291 00292 memcpy((void *)&timerHandlerPtr->time, (void *)timePtr, sizeof(Tcl_Time)); 00293 timerHandlerPtr->proc = proc; 00294 timerHandlerPtr->clientData = clientData; 00295 tsdPtr->lastTimerId++; 00296 timerHandlerPtr->token = (Tcl_TimerToken) INT2PTR(tsdPtr->lastTimerId); 00297 00298 /* 00299 * Add the event to the queue in the correct position 00300 * (ordered by event firing time). 00301 */ 00302 00303 for (tPtr2 = tsdPtr->firstTimerHandlerPtr, prevPtr = NULL; tPtr2 != NULL; 00304 prevPtr = tPtr2, tPtr2 = tPtr2->nextPtr) { 00305 if (TCL_TIME_BEFORE(timerHandlerPtr->time, tPtr2->time)) { 00306 break; 00307 } 00308 } 00309 timerHandlerPtr->nextPtr = tPtr2; 00310 if (prevPtr == NULL) { 00311 tsdPtr->firstTimerHandlerPtr = timerHandlerPtr; 00312 } else { 00313 prevPtr->nextPtr = timerHandlerPtr; 00314 } 00315 00316 TimerSetupProc(NULL, TCL_ALL_EVENTS); 00317 00318 return timerHandlerPtr->token; 00319 } 00320 00321 /* 00322 *-------------------------------------------------------------- 00323 * 00324 * Tcl_DeleteTimerHandler -- 00325 * 00326 * Delete a previously-registered timer handler. 00327 * 00328 * Results: 00329 * None. 00330 * 00331 * Side effects: 00332 * Destroy the timer callback identified by TimerToken, so that its 00333 * associated function will not be called. If the callback has already 00334 * fired, or if the given token doesn't exist, then nothing happens. 00335 * 00336 *-------------------------------------------------------------- 00337 */ 00338 00339 void 00340 Tcl_DeleteTimerHandler( 00341 Tcl_TimerToken token) /* Result previously returned by 00342 * Tcl_DeleteTimerHandler. */ 00343 { 00344 register TimerHandler *timerHandlerPtr, *prevPtr; 00345 ThreadSpecificData *tsdPtr = InitTimer(); 00346 00347 if (token == NULL) { 00348 return; 00349 } 00350 00351 for (timerHandlerPtr = tsdPtr->firstTimerHandlerPtr, prevPtr = NULL; 00352 timerHandlerPtr != NULL; prevPtr = timerHandlerPtr, 00353 timerHandlerPtr = timerHandlerPtr->nextPtr) { 00354 if (timerHandlerPtr->token != token) { 00355 continue; 00356 } 00357 if (prevPtr == NULL) { 00358 tsdPtr->firstTimerHandlerPtr = timerHandlerPtr->nextPtr; 00359 } else { 00360 prevPtr->nextPtr = timerHandlerPtr->nextPtr; 00361 } 00362 ckfree((char *) timerHandlerPtr); 00363 return; 00364 } 00365 } 00366 00367 /* 00368 *---------------------------------------------------------------------- 00369 * 00370 * TimerSetupProc -- 00371 * 00372 * This function is called by Tcl_DoOneEvent to setup the timer event 00373 * source for before blocking. This routine checks both the idle and 00374 * after timer lists. 00375 * 00376 * Results: 00377 * None. 00378 * 00379 * Side effects: 00380 * May update the maximum notifier block time. 00381 * 00382 *---------------------------------------------------------------------- 00383 */ 00384 00385 static void 00386 TimerSetupProc( 00387 ClientData data, /* Not used. */ 00388 int flags) /* Event flags as passed to Tcl_DoOneEvent. */ 00389 { 00390 Tcl_Time blockTime; 00391 ThreadSpecificData *tsdPtr = InitTimer(); 00392 00393 if (((flags & TCL_IDLE_EVENTS) && tsdPtr->idleList) 00394 || ((flags & TCL_TIMER_EVENTS) && tsdPtr->timerPending)) { 00395 /* 00396 * There is an idle handler or a pending timer event, so just poll. 00397 */ 00398 00399 blockTime.sec = 0; 00400 blockTime.usec = 0; 00401 00402 } else if ((flags & TCL_TIMER_EVENTS) && tsdPtr->firstTimerHandlerPtr) { 00403 /* 00404 * Compute the timeout for the next timer on the list. 00405 */ 00406 00407 Tcl_GetTime(&blockTime); 00408 blockTime.sec = tsdPtr->firstTimerHandlerPtr->time.sec - blockTime.sec; 00409 blockTime.usec = tsdPtr->firstTimerHandlerPtr->time.usec - 00410 blockTime.usec; 00411 if (blockTime.usec < 0) { 00412 blockTime.sec -= 1; 00413 blockTime.usec += 1000000; 00414 } 00415 if (blockTime.sec < 0) { 00416 blockTime.sec = 0; 00417 blockTime.usec = 0; 00418 } 00419 } else { 00420 return; 00421 } 00422 00423 Tcl_SetMaxBlockTime(&blockTime); 00424 } 00425 00426 /* 00427 *---------------------------------------------------------------------- 00428 * 00429 * TimerCheckProc -- 00430 * 00431 * This function is called by Tcl_DoOneEvent to check the timer event 00432 * source for events. This routine checks both the idle and after timer 00433 * lists. 00434 * 00435 * Results: 00436 * None. 00437 * 00438 * Side effects: 00439 * May queue an event and update the maximum notifier block time. 00440 * 00441 *---------------------------------------------------------------------- 00442 */ 00443 00444 static void 00445 TimerCheckProc( 00446 ClientData data, /* Not used. */ 00447 int flags) /* Event flags as passed to Tcl_DoOneEvent. */ 00448 { 00449 Tcl_Event *timerEvPtr; 00450 Tcl_Time blockTime; 00451 ThreadSpecificData *tsdPtr = InitTimer(); 00452 00453 if ((flags & TCL_TIMER_EVENTS) && tsdPtr->firstTimerHandlerPtr) { 00454 /* 00455 * Compute the timeout for the next timer on the list. 00456 */ 00457 00458 Tcl_GetTime(&blockTime); 00459 blockTime.sec = tsdPtr->firstTimerHandlerPtr->time.sec - blockTime.sec; 00460 blockTime.usec = tsdPtr->firstTimerHandlerPtr->time.usec - 00461 blockTime.usec; 00462 if (blockTime.usec < 0) { 00463 blockTime.sec -= 1; 00464 blockTime.usec += 1000000; 00465 } 00466 if (blockTime.sec < 0) { 00467 blockTime.sec = 0; 00468 blockTime.usec = 0; 00469 } 00470 00471 /* 00472 * If the first timer has expired, stick an event on the queue. 00473 */ 00474 00475 if (blockTime.sec == 0 && blockTime.usec == 0 && 00476 !tsdPtr->timerPending) { 00477 tsdPtr->timerPending = 1; 00478 timerEvPtr = (Tcl_Event *) ckalloc(sizeof(Tcl_Event)); 00479 timerEvPtr->proc = TimerHandlerEventProc; 00480 Tcl_QueueEvent(timerEvPtr, TCL_QUEUE_TAIL); 00481 } 00482 } 00483 } 00484 00485 /* 00486 *---------------------------------------------------------------------- 00487 * 00488 * TimerHandlerEventProc -- 00489 * 00490 * This function is called by Tcl_ServiceEvent when a timer event reaches 00491 * the front of the event queue. This function handles the event by 00492 * invoking the callbacks for all timers that are ready. 00493 * 00494 * Results: 00495 * Returns 1 if the event was handled, meaning it should be removed from 00496 * the queue. Returns 0 if the event was not handled, meaning it should 00497 * stay on the queue. The only time the event isn't handled is if the 00498 * TCL_TIMER_EVENTS flag bit isn't set. 00499 * 00500 * Side effects: 00501 * Whatever the timer handler callback functions do. 00502 * 00503 *---------------------------------------------------------------------- 00504 */ 00505 00506 static int 00507 TimerHandlerEventProc( 00508 Tcl_Event *evPtr, /* Event to service. */ 00509 int flags) /* Flags that indicate what events to handle, 00510 * such as TCL_FILE_EVENTS. */ 00511 { 00512 TimerHandler *timerHandlerPtr, **nextPtrPtr; 00513 Tcl_Time time; 00514 int currentTimerId; 00515 ThreadSpecificData *tsdPtr = InitTimer(); 00516 00517 /* 00518 * Do nothing if timers aren't enabled. This leaves the event on the 00519 * queue, so we will get to it as soon as ServiceEvents() is called with 00520 * timers enabled. 00521 */ 00522 00523 if (!(flags & TCL_TIMER_EVENTS)) { 00524 return 0; 00525 } 00526 00527 /* 00528 * The code below is trickier than it may look, for the following reasons: 00529 * 00530 * 1. New handlers can get added to the list while the current one is 00531 * being processed. If new ones get added, we don't want to process 00532 * them during this pass through the list to avoid starving other event 00533 * sources. This is implemented using the token number in the handler: 00534 * new handlers will have a newer token than any of the ones currently 00535 * on the list. 00536 * 2. The handler can call Tcl_DoOneEvent, so we have to remove the 00537 * handler from the list before calling it. Otherwise an infinite loop 00538 * could result. 00539 * 3. Tcl_DeleteTimerHandler can be called to remove an element from the 00540 * list while a handler is executing, so the list could change 00541 * structure during the call. 00542 * 4. Because we only fetch the current time before entering the loop, the 00543 * only way a new timer will even be considered runnable is if its 00544 * expiration time is within the same millisecond as the current time. 00545 * This is fairly likely on Windows, since it has a course granularity 00546 * clock. Since timers are placed on the queue in time order with the 00547 * most recently created handler appearing after earlier ones with the 00548 * same expiration time, we don't have to worry about newer generation 00549 * timers appearing before later ones. 00550 */ 00551 00552 tsdPtr->timerPending = 0; 00553 currentTimerId = tsdPtr->lastTimerId; 00554 Tcl_GetTime(&time); 00555 while (1) { 00556 nextPtrPtr = &tsdPtr->firstTimerHandlerPtr; 00557 timerHandlerPtr = tsdPtr->firstTimerHandlerPtr; 00558 if (timerHandlerPtr == NULL) { 00559 break; 00560 } 00561 00562 if (TCL_TIME_BEFORE(time, timerHandlerPtr->time)) { 00563 break; 00564 } 00565 00566 /* 00567 * Bail out if the next timer is of a newer generation. 00568 */ 00569 00570 if ((currentTimerId - PTR2INT(timerHandlerPtr->token)) < 0) { 00571 break; 00572 } 00573 00574 /* 00575 * Remove the handler from the queue before invoking it, to avoid 00576 * potential reentrancy problems. 00577 */ 00578 00579 (*nextPtrPtr) = timerHandlerPtr->nextPtr; 00580 (*timerHandlerPtr->proc)(timerHandlerPtr->clientData); 00581 ckfree((char *) timerHandlerPtr); 00582 } 00583 TimerSetupProc(NULL, TCL_TIMER_EVENTS); 00584 return 1; 00585 } 00586 00587 /* 00588 *-------------------------------------------------------------- 00589 * 00590 * Tcl_DoWhenIdle -- 00591 * 00592 * Arrange for proc to be invoked the next time the system is idle (i.e., 00593 * just before the next time that Tcl_DoOneEvent would have to wait for 00594 * something to happen). 00595 * 00596 * Results: 00597 * None. 00598 * 00599 * Side effects: 00600 * Proc will eventually be called, with clientData as argument. See the 00601 * manual entry for details. 00602 * 00603 *-------------------------------------------------------------- 00604 */ 00605 00606 void 00607 Tcl_DoWhenIdle( 00608 Tcl_IdleProc *proc, /* Function to invoke. */ 00609 ClientData clientData) /* Arbitrary value to pass to proc. */ 00610 { 00611 register IdleHandler *idlePtr; 00612 Tcl_Time blockTime; 00613 ThreadSpecificData *tsdPtr = InitTimer(); 00614 00615 idlePtr = (IdleHandler *) ckalloc(sizeof(IdleHandler)); 00616 idlePtr->proc = proc; 00617 idlePtr->clientData = clientData; 00618 idlePtr->generation = tsdPtr->idleGeneration; 00619 idlePtr->nextPtr = NULL; 00620 if (tsdPtr->lastIdlePtr == NULL) { 00621 tsdPtr->idleList = idlePtr; 00622 } else { 00623 tsdPtr->lastIdlePtr->nextPtr = idlePtr; 00624 } 00625 tsdPtr->lastIdlePtr = idlePtr; 00626 00627 blockTime.sec = 0; 00628 blockTime.usec = 0; 00629 Tcl_SetMaxBlockTime(&blockTime); 00630 } 00631 00632 /* 00633 *---------------------------------------------------------------------- 00634 * 00635 * Tcl_CancelIdleCall -- 00636 * 00637 * If there are any when-idle calls requested to a given function with 00638 * given clientData, cancel all of them. 00639 * 00640 * Results: 00641 * None. 00642 * 00643 * Side effects: 00644 * If the proc/clientData combination were on the when-idle list, they 00645 * are removed so that they will never be called. 00646 * 00647 *---------------------------------------------------------------------- 00648 */ 00649 00650 void 00651 Tcl_CancelIdleCall( 00652 Tcl_IdleProc *proc, /* Function that was previously registered. */ 00653 ClientData clientData) /* Arbitrary value to pass to proc. */ 00654 { 00655 register IdleHandler *idlePtr, *prevPtr; 00656 IdleHandler *nextPtr; 00657 ThreadSpecificData *tsdPtr = InitTimer(); 00658 00659 for (prevPtr = NULL, idlePtr = tsdPtr->idleList; idlePtr != NULL; 00660 prevPtr = idlePtr, idlePtr = idlePtr->nextPtr) { 00661 while ((idlePtr->proc == proc) 00662 && (idlePtr->clientData == clientData)) { 00663 nextPtr = idlePtr->nextPtr; 00664 ckfree((char *) idlePtr); 00665 idlePtr = nextPtr; 00666 if (prevPtr == NULL) { 00667 tsdPtr->idleList = idlePtr; 00668 } else { 00669 prevPtr->nextPtr = idlePtr; 00670 } 00671 if (idlePtr == NULL) { 00672 tsdPtr->lastIdlePtr = prevPtr; 00673 return; 00674 } 00675 } 00676 } 00677 } 00678 00679 /* 00680 *---------------------------------------------------------------------- 00681 * 00682 * TclServiceIdle -- 00683 * 00684 * This function is invoked by the notifier when it becomes idle. It will 00685 * invoke all idle handlers that are present at the time the call is 00686 * invoked, but not those added during idle processing. 00687 * 00688 * Results: 00689 * The return value is 1 if TclServiceIdle found something to do, 00690 * otherwise return value is 0. 00691 * 00692 * Side effects: 00693 * Invokes all pending idle handlers. 00694 * 00695 *---------------------------------------------------------------------- 00696 */ 00697 00698 int 00699 TclServiceIdle(void) 00700 { 00701 IdleHandler *idlePtr; 00702 int oldGeneration; 00703 Tcl_Time blockTime; 00704 ThreadSpecificData *tsdPtr = InitTimer(); 00705 00706 if (tsdPtr->idleList == NULL) { 00707 return 0; 00708 } 00709 00710 oldGeneration = tsdPtr->idleGeneration; 00711 tsdPtr->idleGeneration++; 00712 00713 /* 00714 * The code below is trickier than it may look, for the following reasons: 00715 * 00716 * 1. New handlers can get added to the list while the current one is 00717 * being processed. If new ones get added, we don't want to process 00718 * them during this pass through the list (want to check for other work 00719 * to do first). This is implemented using the generation number in the 00720 * handler: new handlers will have a different generation than any of 00721 * the ones currently on the list. 00722 * 2. The handler can call Tcl_DoOneEvent, so we have to remove the 00723 * handler from the list before calling it. Otherwise an infinite loop 00724 * could result. 00725 * 3. Tcl_CancelIdleCall can be called to remove an element from the list 00726 * while a handler is executing, so the list could change structure 00727 * during the call. 00728 */ 00729 00730 for (idlePtr = tsdPtr->idleList; 00731 ((idlePtr != NULL) 00732 && ((oldGeneration - idlePtr->generation) >= 0)); 00733 idlePtr = tsdPtr->idleList) { 00734 tsdPtr->idleList = idlePtr->nextPtr; 00735 if (tsdPtr->idleList == NULL) { 00736 tsdPtr->lastIdlePtr = NULL; 00737 } 00738 (*idlePtr->proc)(idlePtr->clientData); 00739 ckfree((char *) idlePtr); 00740 } 00741 if (tsdPtr->idleList) { 00742 blockTime.sec = 0; 00743 blockTime.usec = 0; 00744 Tcl_SetMaxBlockTime(&blockTime); 00745 } 00746 return 1; 00747 } 00748 00749 /* 00750 *---------------------------------------------------------------------- 00751 * 00752 * Tcl_AfterObjCmd -- 00753 * 00754 * This function is invoked to process the "after" Tcl command. See the 00755 * user documentation for details on what it does. 00756 * 00757 * Results: 00758 * A standard Tcl result. 00759 * 00760 * Side effects: 00761 * See the user documentation. 00762 * 00763 *---------------------------------------------------------------------- 00764 */ 00765 00766 /* ARGSUSED */ 00767 int 00768 Tcl_AfterObjCmd( 00769 ClientData clientData, /* Unused */ 00770 Tcl_Interp *interp, /* Current interpreter. */ 00771 int objc, /* Number of arguments. */ 00772 Tcl_Obj *CONST objv[]) /* Argument objects. */ 00773 { 00774 Tcl_WideInt ms; /* Number of milliseconds to wait */ 00775 Tcl_Time wakeup; 00776 AfterInfo *afterPtr; 00777 AfterAssocData *assocPtr; 00778 int length; 00779 int index; 00780 char buf[16 + TCL_INTEGER_SPACE]; 00781 static CONST char *afterSubCmds[] = { 00782 "cancel", "idle", "info", NULL 00783 }; 00784 enum afterSubCmds {AFTER_CANCEL, AFTER_IDLE, AFTER_INFO}; 00785 ThreadSpecificData *tsdPtr = InitTimer(); 00786 00787 if (objc < 2) { 00788 Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?"); 00789 return TCL_ERROR; 00790 } 00791 00792 /* 00793 * Create the "after" information associated for this interpreter, if it 00794 * doesn't already exist. 00795 */ 00796 00797 assocPtr = Tcl_GetAssocData(interp, "tclAfter", NULL); 00798 if (assocPtr == NULL) { 00799 assocPtr = (AfterAssocData *) ckalloc(sizeof(AfterAssocData)); 00800 assocPtr->interp = interp; 00801 assocPtr->firstAfterPtr = NULL; 00802 Tcl_SetAssocData(interp, "tclAfter", AfterCleanupProc, 00803 (ClientData) assocPtr); 00804 } 00805 00806 /* 00807 * First lets see if the command was passed a number as the first argument. 00808 */ 00809 00810 if (objv[1]->typePtr == &tclIntType 00811 #ifndef NO_WIDE_TYPE 00812 || objv[1]->typePtr == &tclWideIntType 00813 #endif 00814 || objv[1]->typePtr == &tclBignumType 00815 || ( Tcl_GetIndexFromObj(NULL, objv[1], afterSubCmds, "", 0, 00816 &index) != TCL_OK )) { 00817 index = -1; 00818 if (Tcl_GetWideIntFromObj(NULL, objv[1], &ms) != TCL_OK) { 00819 Tcl_AppendResult(interp, "bad argument \"", 00820 Tcl_GetString(objv[1]), 00821 "\": must be cancel, idle, info, or an integer", 00822 NULL); 00823 return TCL_ERROR; 00824 } 00825 } 00826 00827 /* 00828 * At this point, either index = -1 and ms contains the number of ms 00829 * to wait, or else index is the index of a subcommand. 00830 */ 00831 00832 switch (index) { 00833 case -1: { 00834 if (ms < 0) { 00835 ms = 0; 00836 } 00837 if (objc == 2) { 00838 return AfterDelay(interp, ms); 00839 } 00840 afterPtr = (AfterInfo *) ckalloc((unsigned) (sizeof(AfterInfo))); 00841 afterPtr->assocPtr = assocPtr; 00842 if (objc == 3) { 00843 afterPtr->commandPtr = objv[2]; 00844 } else { 00845 afterPtr->commandPtr = Tcl_ConcatObj(objc-2, objv+2); 00846 } 00847 Tcl_IncrRefCount(afterPtr->commandPtr); 00848 00849 /* 00850 * The variable below is used to generate unique identifiers for after 00851 * commands. This id can wrap around, which can potentially cause 00852 * problems. However, there are not likely to be problems in practice, 00853 * because after commands can only be requested to about a month in 00854 * the future, and wrap-around is unlikely to occur in less than about 00855 * 1-10 years. Thus it's unlikely that any old ids will still be 00856 * around when wrap-around occurs. 00857 */ 00858 00859 afterPtr->id = tsdPtr->afterId; 00860 tsdPtr->afterId += 1; 00861 Tcl_GetTime(&wakeup); 00862 wakeup.sec += (long)(ms / 1000); 00863 wakeup.usec += ((long)(ms % 1000)) * 1000; 00864 if (wakeup.usec > 1000000) { 00865 wakeup.sec++; 00866 wakeup.usec -= 1000000; 00867 } 00868 afterPtr->token = TclCreateAbsoluteTimerHandler(&wakeup, AfterProc, 00869 (ClientData) afterPtr); 00870 afterPtr->nextPtr = assocPtr->firstAfterPtr; 00871 assocPtr->firstAfterPtr = afterPtr; 00872 Tcl_SetObjResult(interp, Tcl_ObjPrintf("after#%d", afterPtr->id)); 00873 return TCL_OK; 00874 } 00875 case AFTER_CANCEL: { 00876 Tcl_Obj *commandPtr; 00877 char *command, *tempCommand; 00878 int tempLength; 00879 00880 if (objc < 3) { 00881 Tcl_WrongNumArgs(interp, 2, objv, "id|command"); 00882 return TCL_ERROR; 00883 } 00884 if (objc == 3) { 00885 commandPtr = objv[2]; 00886 } else { 00887 commandPtr = Tcl_ConcatObj(objc-2, objv+2);; 00888 } 00889 command = Tcl_GetStringFromObj(commandPtr, &length); 00890 for (afterPtr = assocPtr->firstAfterPtr; afterPtr != NULL; 00891 afterPtr = afterPtr->nextPtr) { 00892 tempCommand = Tcl_GetStringFromObj(afterPtr->commandPtr, 00893 &tempLength); 00894 if ((length == tempLength) 00895 && (memcmp((void*) command, (void*) tempCommand, 00896 (unsigned) length) == 0)) { 00897 break; 00898 } 00899 } 00900 if (afterPtr == NULL) { 00901 afterPtr = GetAfterEvent(assocPtr, commandPtr); 00902 } 00903 if (objc != 3) { 00904 Tcl_DecrRefCount(commandPtr); 00905 } 00906 if (afterPtr != NULL) { 00907 if (afterPtr->token != NULL) { 00908 Tcl_DeleteTimerHandler(afterPtr->token); 00909 } else { 00910 Tcl_CancelIdleCall(AfterProc, (ClientData) afterPtr); 00911 } 00912 FreeAfterPtr(afterPtr); 00913 } 00914 break; 00915 } 00916 case AFTER_IDLE: 00917 if (objc < 3) { 00918 Tcl_WrongNumArgs(interp, 2, objv, "script script ..."); 00919 return TCL_ERROR; 00920 } 00921 afterPtr = (AfterInfo *) ckalloc((unsigned) (sizeof(AfterInfo))); 00922 afterPtr->assocPtr = assocPtr; 00923 if (objc == 3) { 00924 afterPtr->commandPtr = objv[2]; 00925 } else { 00926 afterPtr->commandPtr = Tcl_ConcatObj(objc-2, objv+2); 00927 } 00928 Tcl_IncrRefCount(afterPtr->commandPtr); 00929 afterPtr->id = tsdPtr->afterId; 00930 tsdPtr->afterId += 1; 00931 afterPtr->token = NULL; 00932 afterPtr->nextPtr = assocPtr->firstAfterPtr; 00933 assocPtr->firstAfterPtr = afterPtr; 00934 Tcl_DoWhenIdle(AfterProc, (ClientData) afterPtr); 00935 Tcl_SetObjResult(interp, Tcl_ObjPrintf("after#%d", afterPtr->id)); 00936 break; 00937 case AFTER_INFO: { 00938 Tcl_Obj *resultListPtr; 00939 00940 if (objc == 2) { 00941 for (afterPtr = assocPtr->firstAfterPtr; afterPtr != NULL; 00942 afterPtr = afterPtr->nextPtr) { 00943 if (assocPtr->interp == interp) { 00944 sprintf(buf, "after#%d", afterPtr->id); 00945 Tcl_AppendElement(interp, buf); 00946 } 00947 } 00948 return TCL_OK; 00949 } 00950 if (objc != 3) { 00951 Tcl_WrongNumArgs(interp, 2, objv, "?id?"); 00952 return TCL_ERROR; 00953 } 00954 afterPtr = GetAfterEvent(assocPtr, objv[2]); 00955 if (afterPtr == NULL) { 00956 Tcl_AppendResult(interp, "event \"", TclGetString(objv[2]), 00957 "\" doesn't exist", NULL); 00958 return TCL_ERROR; 00959 } 00960 resultListPtr = Tcl_NewObj(); 00961 Tcl_ListObjAppendElement(interp, resultListPtr, afterPtr->commandPtr); 00962 Tcl_ListObjAppendElement(interp, resultListPtr, Tcl_NewStringObj( 00963 (afterPtr->token == NULL) ? "idle" : "timer", -1)); 00964 Tcl_SetObjResult(interp, resultListPtr); 00965 break; 00966 } 00967 default: 00968 Tcl_Panic("Tcl_AfterObjCmd: bad subcommand index to afterSubCmds"); 00969 } 00970 return TCL_OK; 00971 } 00972 00973 /* 00974 *---------------------------------------------------------------------- 00975 * 00976 * AfterDelay -- 00977 * 00978 * Implements the blocking delay behaviour of [after $time]. Tricky 00979 * because it has to take into account any time limit that has been set. 00980 * 00981 * Results: 00982 * Standard Tcl result code (with error set if an error occurred due to a 00983 * time limit being exceeded). 00984 * 00985 * Side effects: 00986 * May adjust the time limit granularity marker. 00987 * 00988 *---------------------------------------------------------------------- 00989 */ 00990 00991 static int 00992 AfterDelay( 00993 Tcl_Interp *interp, 00994 Tcl_WideInt ms) 00995 { 00996 Interp *iPtr = (Interp *) interp; 00997 00998 Tcl_Time endTime, now; 00999 Tcl_WideInt diff; 01000 01001 Tcl_GetTime(&endTime); 01002 endTime.sec += (long)(ms/1000); 01003 endTime.usec += ((int)(ms%1000))*1000; 01004 if (endTime.usec >= 1000000) { 01005 endTime.sec++; 01006 endTime.usec -= 1000000; 01007 } 01008 01009 do { 01010 Tcl_GetTime(&now); 01011 if (iPtr->limit.timeEvent != NULL 01012 && TCL_TIME_BEFORE(iPtr->limit.time, now)) { 01013 iPtr->limit.granularityTicker = 0; 01014 if (Tcl_LimitCheck(interp) != TCL_OK) { 01015 return TCL_ERROR; 01016 } 01017 } 01018 if (iPtr->limit.timeEvent == NULL 01019 || TCL_TIME_BEFORE(endTime, iPtr->limit.time)) { 01020 diff = TCL_TIME_DIFF_MS(endTime, now); 01021 #ifndef TCL_WIDE_INT_IS_LONG 01022 if (diff > LONG_MAX) { 01023 diff = LONG_MAX; 01024 } 01025 #endif 01026 if (diff > 0) { 01027 Tcl_Sleep((long)diff); 01028 } 01029 } else { 01030 diff = TCL_TIME_DIFF_MS(iPtr->limit.time, now); 01031 #ifndef TCL_WIDE_INT_IS_LONG 01032 if (diff > LONG_MAX) { 01033 diff = LONG_MAX; 01034 } 01035 #endif 01036 if (diff > 0) { 01037 Tcl_Sleep((long)diff); 01038 } 01039 if (Tcl_LimitCheck(interp) != TCL_OK) { 01040 return TCL_ERROR; 01041 } 01042 } 01043 } while (TCL_TIME_BEFORE(now, endTime)); 01044 return TCL_OK; 01045 } 01046 01047 /* 01048 *---------------------------------------------------------------------- 01049 * 01050 * GetAfterEvent -- 01051 * 01052 * This function parses an "after" id such as "after#4" and returns a 01053 * pointer to the AfterInfo structure. 01054 * 01055 * Results: 01056 * The return value is either a pointer to an AfterInfo structure, if one 01057 * is found that corresponds to "cmdString" and is for interp, or NULL if 01058 * no corresponding after event can be found. 01059 * 01060 * Side effects: 01061 * None. 01062 * 01063 *---------------------------------------------------------------------- 01064 */ 01065 01066 static AfterInfo * 01067 GetAfterEvent( 01068 AfterAssocData *assocPtr, /* Points to "after"-related information for 01069 * this interpreter. */ 01070 Tcl_Obj *commandPtr) 01071 { 01072 char *cmdString; /* Textual identifier for after event, such as 01073 * "after#6". */ 01074 AfterInfo *afterPtr; 01075 int id; 01076 char *end; 01077 01078 cmdString = TclGetString(commandPtr); 01079 if (strncmp(cmdString, "after#", 6) != 0) { 01080 return NULL; 01081 } 01082 cmdString += 6; 01083 id = strtoul(cmdString, &end, 10); 01084 if ((end == cmdString) || (*end != 0)) { 01085 return NULL; 01086 } 01087 for (afterPtr = assocPtr->firstAfterPtr; afterPtr != NULL; 01088 afterPtr = afterPtr->nextPtr) { 01089 if (afterPtr->id == id) { 01090 return afterPtr; 01091 } 01092 } 01093 return NULL; 01094 } 01095 01096 /* 01097 *---------------------------------------------------------------------- 01098 * 01099 * AfterProc -- 01100 * 01101 * Timer callback to execute commands registered with the "after" 01102 * command. 01103 * 01104 * Results: 01105 * None. 01106 * 01107 * Side effects: 01108 * Executes whatever command was specified. If the command returns an 01109 * error, then the command "bgerror" is invoked to process the error; if 01110 * bgerror fails then information about the error is output on stderr. 01111 * 01112 *---------------------------------------------------------------------- 01113 */ 01114 01115 static void 01116 AfterProc( 01117 ClientData clientData) /* Describes command to execute. */ 01118 { 01119 AfterInfo *afterPtr = (AfterInfo *) clientData; 01120 AfterAssocData *assocPtr = afterPtr->assocPtr; 01121 AfterInfo *prevPtr; 01122 int result; 01123 Tcl_Interp *interp; 01124 01125 /* 01126 * First remove the callback from our list of callbacks; otherwise someone 01127 * could delete the callback while it's being executed, which could cause 01128 * a core dump. 01129 */ 01130 01131 if (assocPtr->firstAfterPtr == afterPtr) { 01132 assocPtr->firstAfterPtr = afterPtr->nextPtr; 01133 } else { 01134 for (prevPtr = assocPtr->firstAfterPtr; prevPtr->nextPtr != afterPtr; 01135 prevPtr = prevPtr->nextPtr) { 01136 /* Empty loop body. */ 01137 } 01138 prevPtr->nextPtr = afterPtr->nextPtr; 01139 } 01140 01141 /* 01142 * Execute the callback. 01143 */ 01144 01145 interp = assocPtr->interp; 01146 Tcl_Preserve((ClientData) interp); 01147 result = Tcl_EvalObjEx(interp, afterPtr->commandPtr, TCL_EVAL_GLOBAL); 01148 if (result != TCL_OK) { 01149 Tcl_AddErrorInfo(interp, "\n (\"after\" script)"); 01150 TclBackgroundException(interp, result); 01151 } 01152 Tcl_Release((ClientData) interp); 01153 01154 /* 01155 * Free the memory for the callback. 01156 */ 01157 01158 Tcl_DecrRefCount(afterPtr->commandPtr); 01159 ckfree((char *) afterPtr); 01160 } 01161 01162 /* 01163 *---------------------------------------------------------------------- 01164 * 01165 * FreeAfterPtr -- 01166 * 01167 * This function removes an "after" command from the list of those that 01168 * are pending and frees its resources. This function does *not* cancel 01169 * the timer handler; if that's needed, the caller must do it. 01170 * 01171 * Results: 01172 * None. 01173 * 01174 * Side effects: 01175 * The memory associated with afterPtr is released. 01176 * 01177 *---------------------------------------------------------------------- 01178 */ 01179 01180 static void 01181 FreeAfterPtr( 01182 AfterInfo *afterPtr) /* Command to be deleted. */ 01183 { 01184 AfterInfo *prevPtr; 01185 AfterAssocData *assocPtr = afterPtr->assocPtr; 01186 01187 if (assocPtr->firstAfterPtr == afterPtr) { 01188 assocPtr->firstAfterPtr = afterPtr->nextPtr; 01189 } else { 01190 for (prevPtr = assocPtr->firstAfterPtr; prevPtr->nextPtr != afterPtr; 01191 prevPtr = prevPtr->nextPtr) { 01192 /* Empty loop body. */ 01193 } 01194 prevPtr->nextPtr = afterPtr->nextPtr; 01195 } 01196 Tcl_DecrRefCount(afterPtr->commandPtr); 01197 ckfree((char *) afterPtr); 01198 } 01199 01200 /* 01201 *---------------------------------------------------------------------- 01202 * 01203 * AfterCleanupProc -- 01204 * 01205 * This function is invoked whenever an interpreter is deleted 01206 * to cleanup the AssocData for "tclAfter". 01207 * 01208 * Results: 01209 * None. 01210 * 01211 * Side effects: 01212 * After commands are removed. 01213 * 01214 *---------------------------------------------------------------------- 01215 */ 01216 01217 /* ARGSUSED */ 01218 static void 01219 AfterCleanupProc( 01220 ClientData clientData, /* Points to AfterAssocData for the 01221 * interpreter. */ 01222 Tcl_Interp *interp) /* Interpreter that is being deleted. */ 01223 { 01224 AfterAssocData *assocPtr = (AfterAssocData *) clientData; 01225 AfterInfo *afterPtr; 01226 01227 while (assocPtr->firstAfterPtr != NULL) { 01228 afterPtr = assocPtr->firstAfterPtr; 01229 assocPtr->firstAfterPtr = afterPtr->nextPtr; 01230 if (afterPtr->token != NULL) { 01231 Tcl_DeleteTimerHandler(afterPtr->token); 01232 } else { 01233 Tcl_CancelIdleCall(AfterProc, (ClientData) afterPtr); 01234 } 01235 Tcl_DecrRefCount(afterPtr->commandPtr); 01236 ckfree((char *) afterPtr); 01237 } 01238 ckfree((char *) assocPtr); 01239 } 01240 01241 /* 01242 * Local Variables: 01243 * mode: c 01244 * c-basic-offset: 4 01245 * fill-column: 78 01246 * End: 01247 */
Generated on Wed Mar 12 12:18:23 2008 by 1.5.1 |