os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/generic/tclTimer.c
Update contrib.
4 * This file provides timer event management facilities for Tcl,
5 * including the "after" command.
7 * Copyright (c) 1997 by Sun Microsystems, Inc.
8 * Portions Copyright (c) 2007-2008 Nokia Corporation and/or its subsidiaries. All rights reserved.
10 * See the file "license.terms" for information on usage and redistribution
11 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13 * RCS: @(#) $Id: tclTimer.c,v 1.6.2.4 2005/11/09 21:46:20 kennykb Exp $
18 #if defined(__SYMBIAN32__) && defined(__WINSCW__)
19 #include "tclSymbianGlobals.h"
20 #define dataKey getdataKey(7)
24 * For each timer callback that's pending there is one record of the following
25 * type. The normal handlers (created by Tcl_CreateTimerHandler) are chained
26 * together in a list sorted by time (earliest event first).
29 typedef struct TimerHandler {
30 Tcl_Time time; /* When timer is to fire. */
31 Tcl_TimerProc *proc; /* Procedure to call. */
32 ClientData clientData; /* Argument to pass to proc. */
33 Tcl_TimerToken token; /* Identifies handler so it can be
35 struct TimerHandler *nextPtr; /* Next event in queue, or NULL for
40 * The data structure below is used by the "after" command to remember
41 * the command to be executed later. All of the pending "after" commands
42 * for an interpreter are linked together in a list.
45 typedef struct AfterInfo {
46 struct AfterAssocData *assocPtr;
47 /* Pointer to the "tclAfter" assocData for
48 * the interp in which command will be
50 Tcl_Obj *commandPtr; /* Command to execute. */
51 int id; /* Integer identifier for command; used to
53 Tcl_TimerToken token; /* Used to cancel the "after" command. NULL
54 * means that the command is run as an
55 * idle handler rather than as a timer
56 * handler. NULL means this is an "after
57 * idle" handler rather than a
59 struct AfterInfo *nextPtr; /* Next in list of all "after" commands for
60 * this interpreter. */
64 * One of the following structures is associated with each interpreter
65 * for which an "after" command has ever been invoked. A pointer to
66 * this structure is stored in the AssocData for the "tclAfter" key.
69 typedef struct AfterAssocData {
70 Tcl_Interp *interp; /* The interpreter for which this data is
72 AfterInfo *firstAfterPtr; /* First in list of all "after" commands
73 * still pending for this interpreter, or
78 * There is one of the following structures for each of the
79 * handlers declared in a call to Tcl_DoWhenIdle. All of the
80 * currently-active handlers are linked together into a list.
83 typedef struct IdleHandler {
84 Tcl_IdleProc (*proc); /* Procedure to call. */
85 ClientData clientData; /* Value to pass to proc. */
86 int generation; /* Used to distinguish older handlers from
87 * recently-created ones. */
88 struct IdleHandler *nextPtr;/* Next in list of active handlers. */
92 * The timer and idle queues are per-thread because they are associated
93 * with the notifier, which is also per-thread.
95 * All static variables used in this file are collected into a single
96 * instance of the following structure. For multi-threaded implementations,
97 * there is one instance of this structure for each thread.
99 * Notice that different structures with the same name appear in other
100 * files. The structure defined below is used in this file only.
103 typedef struct ThreadSpecificData {
104 TimerHandler *firstTimerHandlerPtr; /* First event in queue. */
105 int lastTimerId; /* Timer identifier of most recently
107 int timerPending; /* 1 if a timer event is in the queue. */
108 IdleHandler *idleList; /* First in list of all idle handlers. */
109 IdleHandler *lastIdlePtr; /* Last in list (or NULL for empty list). */
110 int idleGeneration; /* Used to fill in the "generation" fields
111 * of IdleHandler structures. Increments
112 * each time Tcl_DoOneEvent starts calling
113 * idle handlers, so that all old handlers
114 * can be called without calling any of the
115 * new ones created by old ones. */
116 int afterId; /* For unique identifiers of after events. */
117 } ThreadSpecificData;
119 #if !defined(__SYMBIAN32__) || !defined(__WINSCW__)
120 static Tcl_ThreadDataKey dataKey;
124 * Prototypes for procedures referenced only in this file:
127 static void AfterCleanupProc _ANSI_ARGS_((ClientData clientData,
128 Tcl_Interp *interp));
129 static void AfterProc _ANSI_ARGS_((ClientData clientData));
130 static void FreeAfterPtr _ANSI_ARGS_((AfterInfo *afterPtr));
131 static AfterInfo * GetAfterEvent _ANSI_ARGS_((AfterAssocData *assocPtr,
132 Tcl_Obj *commandPtr));
133 static ThreadSpecificData *InitTimer _ANSI_ARGS_((void));
134 static void TimerExitProc _ANSI_ARGS_((ClientData clientData));
135 static int TimerHandlerEventProc _ANSI_ARGS_((Tcl_Event *evPtr,
137 static void TimerCheckProc _ANSI_ARGS_((ClientData clientData,
139 static void TimerSetupProc _ANSI_ARGS_((ClientData clientData,
143 *----------------------------------------------------------------------
147 * This function initializes the timer module.
150 * A pointer to the thread specific data.
153 * Registers the idle and timer event sources.
155 *----------------------------------------------------------------------
158 static ThreadSpecificData *
161 ThreadSpecificData *tsdPtr =
162 (ThreadSpecificData *) TclThreadDataKeyGet(&dataKey);
164 if (tsdPtr == NULL) {
165 tsdPtr = TCL_TSD_INIT(&dataKey);
166 Tcl_CreateEventSource(TimerSetupProc, TimerCheckProc, NULL);
167 Tcl_CreateThreadExitHandler(TimerExitProc, NULL);
173 *----------------------------------------------------------------------
177 * This function is call at exit or unload time to remove the
178 * timer and idle event sources.
184 * Removes the timer and idle event sources and remaining events.
186 *----------------------------------------------------------------------
190 TimerExitProc(clientData)
191 ClientData clientData; /* Not used. */
193 ThreadSpecificData *tsdPtr =
194 (ThreadSpecificData *) TclThreadDataKeyGet(&dataKey);
196 Tcl_DeleteEventSource(TimerSetupProc, TimerCheckProc, NULL);
197 if (tsdPtr != NULL) {
198 register TimerHandler *timerHandlerPtr;
199 timerHandlerPtr = tsdPtr->firstTimerHandlerPtr;
200 while (timerHandlerPtr != NULL) {
201 tsdPtr->firstTimerHandlerPtr = timerHandlerPtr->nextPtr;
202 ckfree((char *) timerHandlerPtr);
203 timerHandlerPtr = tsdPtr->firstTimerHandlerPtr;
209 *--------------------------------------------------------------
211 * Tcl_CreateTimerHandler --
213 * Arrange for a given procedure to be invoked at a particular
214 * time in the future.
217 * The return value is a token for the timer event, which
218 * may be used to delete the event before it fires.
221 * When milliseconds have elapsed, proc will be invoked
224 *--------------------------------------------------------------
227 EXPORT_C Tcl_TimerToken
228 Tcl_CreateTimerHandler(milliseconds, proc, clientData)
229 int milliseconds; /* How many milliseconds to wait
230 * before invoking proc. */
231 Tcl_TimerProc *proc; /* Procedure to invoke. */
232 ClientData clientData; /* Arbitrary data to pass to proc. */
234 register TimerHandler *timerHandlerPtr, *tPtr2, *prevPtr;
236 ThreadSpecificData *tsdPtr;
238 tsdPtr = InitTimer();
240 timerHandlerPtr = (TimerHandler *) ckalloc(sizeof(TimerHandler));
243 * Compute when the event should fire.
247 timerHandlerPtr->time.sec = time.sec + milliseconds/1000;
248 timerHandlerPtr->time.usec = time.usec + (milliseconds%1000)*1000;
249 if (timerHandlerPtr->time.usec >= 1000000) {
250 timerHandlerPtr->time.usec -= 1000000;
251 timerHandlerPtr->time.sec += 1;
255 * Fill in other fields for the event.
258 timerHandlerPtr->proc = proc;
259 timerHandlerPtr->clientData = clientData;
260 tsdPtr->lastTimerId++;
261 timerHandlerPtr->token = (Tcl_TimerToken) tsdPtr->lastTimerId;
264 * Add the event to the queue in the correct position
265 * (ordered by event firing time).
268 for (tPtr2 = tsdPtr->firstTimerHandlerPtr, prevPtr = NULL; tPtr2 != NULL;
269 prevPtr = tPtr2, tPtr2 = tPtr2->nextPtr) {
270 if ((tPtr2->time.sec > timerHandlerPtr->time.sec)
271 || ((tPtr2->time.sec == timerHandlerPtr->time.sec)
272 && (tPtr2->time.usec > timerHandlerPtr->time.usec))) {
276 timerHandlerPtr->nextPtr = tPtr2;
277 if (prevPtr == NULL) {
278 tsdPtr->firstTimerHandlerPtr = timerHandlerPtr;
280 prevPtr->nextPtr = timerHandlerPtr;
283 TimerSetupProc(NULL, TCL_ALL_EVENTS);
285 return timerHandlerPtr->token;
289 *--------------------------------------------------------------
291 * Tcl_DeleteTimerHandler --
293 * Delete a previously-registered timer handler.
299 * Destroy the timer callback identified by TimerToken,
300 * so that its associated procedure will not be called.
301 * If the callback has already fired, or if the given
302 * token doesn't exist, then nothing happens.
304 *--------------------------------------------------------------
308 Tcl_DeleteTimerHandler(token)
309 Tcl_TimerToken token; /* Result previously returned by
310 * Tcl_DeleteTimerHandler. */
312 register TimerHandler *timerHandlerPtr, *prevPtr;
313 ThreadSpecificData *tsdPtr = InitTimer();
319 for (timerHandlerPtr = tsdPtr->firstTimerHandlerPtr, prevPtr = NULL;
320 timerHandlerPtr != NULL; prevPtr = timerHandlerPtr,
321 timerHandlerPtr = timerHandlerPtr->nextPtr) {
322 if (timerHandlerPtr->token != token) {
325 if (prevPtr == NULL) {
326 tsdPtr->firstTimerHandlerPtr = timerHandlerPtr->nextPtr;
328 prevPtr->nextPtr = timerHandlerPtr->nextPtr;
330 ckfree((char *) timerHandlerPtr);
336 *----------------------------------------------------------------------
340 * This function is called by Tcl_DoOneEvent to setup the timer
341 * event source for before blocking. This routine checks both the
342 * idle and after timer lists.
348 * May update the maximum notifier block time.
350 *----------------------------------------------------------------------
354 TimerSetupProc(data, flags)
355 ClientData data; /* Not used. */
356 int flags; /* Event flags as passed to Tcl_DoOneEvent. */
359 ThreadSpecificData *tsdPtr = InitTimer();
361 if (((flags & TCL_IDLE_EVENTS) && tsdPtr->idleList)
362 || ((flags & TCL_TIMER_EVENTS) && tsdPtr->timerPending)) {
364 * There is an idle handler or a pending timer event, so just poll.
370 } else if ((flags & TCL_TIMER_EVENTS) && tsdPtr->firstTimerHandlerPtr) {
372 * Compute the timeout for the next timer on the list.
375 Tcl_GetTime(&blockTime);
376 blockTime.sec = tsdPtr->firstTimerHandlerPtr->time.sec - blockTime.sec;
377 blockTime.usec = tsdPtr->firstTimerHandlerPtr->time.usec -
379 if (blockTime.usec < 0) {
381 blockTime.usec += 1000000;
383 if (blockTime.sec < 0) {
391 Tcl_SetMaxBlockTime(&blockTime);
395 *----------------------------------------------------------------------
399 * This function is called by Tcl_DoOneEvent to check the timer
400 * event source for events. This routine checks both the
401 * idle and after timer lists.
407 * May queue an event and update the maximum notifier block time.
409 *----------------------------------------------------------------------
413 TimerCheckProc(data, flags)
414 ClientData data; /* Not used. */
415 int flags; /* Event flags as passed to Tcl_DoOneEvent. */
417 Tcl_Event *timerEvPtr;
419 ThreadSpecificData *tsdPtr = InitTimer();
421 if ((flags & TCL_TIMER_EVENTS) && tsdPtr->firstTimerHandlerPtr) {
423 * Compute the timeout for the next timer on the list.
426 Tcl_GetTime(&blockTime);
427 blockTime.sec = tsdPtr->firstTimerHandlerPtr->time.sec - blockTime.sec;
428 blockTime.usec = tsdPtr->firstTimerHandlerPtr->time.usec -
430 if (blockTime.usec < 0) {
432 blockTime.usec += 1000000;
434 if (blockTime.sec < 0) {
440 * If the first timer has expired, stick an event on the queue.
443 if (blockTime.sec == 0 && blockTime.usec == 0 &&
444 !tsdPtr->timerPending) {
445 tsdPtr->timerPending = 1;
446 timerEvPtr = (Tcl_Event *) ckalloc(sizeof(Tcl_Event));
447 timerEvPtr->proc = TimerHandlerEventProc;
448 Tcl_QueueEvent(timerEvPtr, TCL_QUEUE_TAIL);
454 *----------------------------------------------------------------------
456 * TimerHandlerEventProc --
458 * This procedure is called by Tcl_ServiceEvent when a timer event
459 * reaches the front of the event queue. This procedure handles
460 * the event by invoking the callbacks for all timers that are
464 * Returns 1 if the event was handled, meaning it should be removed
465 * from the queue. Returns 0 if the event was not handled, meaning
466 * it should stay on the queue. The only time the event isn't
467 * handled is if the TCL_TIMER_EVENTS flag bit isn't set.
470 * Whatever the timer handler callback procedures do.
472 *----------------------------------------------------------------------
476 TimerHandlerEventProc(evPtr, flags)
477 Tcl_Event *evPtr; /* Event to service. */
478 int flags; /* Flags that indicate what events to
479 * handle, such as TCL_FILE_EVENTS. */
481 TimerHandler *timerHandlerPtr, **nextPtrPtr;
484 ThreadSpecificData *tsdPtr = InitTimer();
487 * Do nothing if timers aren't enabled. This leaves the event on the
488 * queue, so we will get to it as soon as ServiceEvents() is called
489 * with timers enabled.
492 if (!(flags & TCL_TIMER_EVENTS)) {
497 * The code below is trickier than it may look, for the following
500 * 1. New handlers can get added to the list while the current
501 * one is being processed. If new ones get added, we don't
502 * want to process them during this pass through the list to avoid
503 * starving other event sources. This is implemented using the
504 * token number in the handler: new handlers will have a
505 * newer token than any of the ones currently on the list.
506 * 2. The handler can call Tcl_DoOneEvent, so we have to remove
507 * the handler from the list before calling it. Otherwise an
508 * infinite loop could result.
509 * 3. Tcl_DeleteTimerHandler can be called to remove an element from
510 * the list while a handler is executing, so the list could
511 * change structure during the call.
512 * 4. Because we only fetch the current time before entering the loop,
513 * the only way a new timer will even be considered runnable is if
514 * its expiration time is within the same millisecond as the
515 * current time. This is fairly likely on Windows, since it has
516 * a course granularity clock. Since timers are placed
517 * on the queue in time order with the most recently created
518 * handler appearing after earlier ones with the same expiration
519 * time, we don't have to worry about newer generation timers
520 * appearing before later ones.
523 tsdPtr->timerPending = 0;
524 currentTimerId = tsdPtr->lastTimerId;
527 nextPtrPtr = &tsdPtr->firstTimerHandlerPtr;
528 timerHandlerPtr = tsdPtr->firstTimerHandlerPtr;
529 if (timerHandlerPtr == NULL) {
533 if ((timerHandlerPtr->time.sec > time.sec)
534 || ((timerHandlerPtr->time.sec == time.sec)
535 && (timerHandlerPtr->time.usec > time.usec))) {
540 * Bail out if the next timer is of a newer generation.
543 if ((currentTimerId - (int)timerHandlerPtr->token) < 0) {
548 * Remove the handler from the queue before invoking it,
549 * to avoid potential reentrancy problems.
552 (*nextPtrPtr) = timerHandlerPtr->nextPtr;
553 (*timerHandlerPtr->proc)(timerHandlerPtr->clientData);
554 ckfree((char *) timerHandlerPtr);
556 TimerSetupProc(NULL, TCL_TIMER_EVENTS);
561 *--------------------------------------------------------------
565 * Arrange for proc to be invoked the next time the system is
566 * idle (i.e., just before the next time that Tcl_DoOneEvent
567 * would have to wait for something to happen).
573 * Proc will eventually be called, with clientData as argument.
574 * See the manual entry for details.
576 *--------------------------------------------------------------
580 Tcl_DoWhenIdle(proc, clientData)
581 Tcl_IdleProc *proc; /* Procedure to invoke. */
582 ClientData clientData; /* Arbitrary value to pass to proc. */
584 register IdleHandler *idlePtr;
586 ThreadSpecificData *tsdPtr = InitTimer();
588 idlePtr = (IdleHandler *) ckalloc(sizeof(IdleHandler));
589 idlePtr->proc = proc;
590 idlePtr->clientData = clientData;
591 idlePtr->generation = tsdPtr->idleGeneration;
592 idlePtr->nextPtr = NULL;
593 if (tsdPtr->lastIdlePtr == NULL) {
594 tsdPtr->idleList = idlePtr;
596 tsdPtr->lastIdlePtr->nextPtr = idlePtr;
598 tsdPtr->lastIdlePtr = idlePtr;
602 Tcl_SetMaxBlockTime(&blockTime);
606 *----------------------------------------------------------------------
608 * Tcl_CancelIdleCall --
610 * If there are any when-idle calls requested to a given procedure
611 * with given clientData, cancel all of them.
617 * If the proc/clientData combination were on the when-idle list,
618 * they are removed so that they will never be called.
620 *----------------------------------------------------------------------
624 Tcl_CancelIdleCall(proc, clientData)
625 Tcl_IdleProc *proc; /* Procedure that was previously registered. */
626 ClientData clientData; /* Arbitrary value to pass to proc. */
628 register IdleHandler *idlePtr, *prevPtr;
629 IdleHandler *nextPtr;
630 ThreadSpecificData *tsdPtr = InitTimer();
632 for (prevPtr = NULL, idlePtr = tsdPtr->idleList; idlePtr != NULL;
633 prevPtr = idlePtr, idlePtr = idlePtr->nextPtr) {
634 while ((idlePtr->proc == proc)
635 && (idlePtr->clientData == clientData)) {
636 nextPtr = idlePtr->nextPtr;
637 ckfree((char *) idlePtr);
639 if (prevPtr == NULL) {
640 tsdPtr->idleList = idlePtr;
642 prevPtr->nextPtr = idlePtr;
644 if (idlePtr == NULL) {
645 tsdPtr->lastIdlePtr = prevPtr;
653 *----------------------------------------------------------------------
657 * This procedure is invoked by the notifier when it becomes
658 * idle. It will invoke all idle handlers that are present at
659 * the time the call is invoked, but not those added during idle
663 * The return value is 1 if TclServiceIdle found something to
664 * do, otherwise return value is 0.
667 * Invokes all pending idle handlers.
669 *----------------------------------------------------------------------
675 IdleHandler *idlePtr;
678 ThreadSpecificData *tsdPtr = InitTimer();
680 if (tsdPtr->idleList == NULL) {
684 oldGeneration = tsdPtr->idleGeneration;
685 tsdPtr->idleGeneration++;
688 * The code below is trickier than it may look, for the following
691 * 1. New handlers can get added to the list while the current
692 * one is being processed. If new ones get added, we don't
693 * want to process them during this pass through the list (want
694 * to check for other work to do first). This is implemented
695 * using the generation number in the handler: new handlers
696 * will have a different generation than any of the ones currently
698 * 2. The handler can call Tcl_DoOneEvent, so we have to remove
699 * the handler from the list before calling it. Otherwise an
700 * infinite loop could result.
701 * 3. Tcl_CancelIdleCall can be called to remove an element from
702 * the list while a handler is executing, so the list could
703 * change structure during the call.
706 for (idlePtr = tsdPtr->idleList;
708 && ((oldGeneration - idlePtr->generation) >= 0));
709 idlePtr = tsdPtr->idleList) {
710 tsdPtr->idleList = idlePtr->nextPtr;
711 if (tsdPtr->idleList == NULL) {
712 tsdPtr->lastIdlePtr = NULL;
714 (*idlePtr->proc)(idlePtr->clientData);
715 ckfree((char *) idlePtr);
717 if (tsdPtr->idleList) {
720 Tcl_SetMaxBlockTime(&blockTime);
726 *----------------------------------------------------------------------
730 * This procedure is invoked to process the "after" Tcl command.
731 * See the user documentation for details on what it does.
734 * A standard Tcl result.
737 * See the user documentation.
739 *----------------------------------------------------------------------
744 Tcl_AfterObjCmd(clientData, interp, objc, objv)
745 ClientData clientData; /* Unused */
746 Tcl_Interp *interp; /* Current interpreter. */
747 int objc; /* Number of arguments. */
748 Tcl_Obj *CONST objv[]; /* Argument objects. */
752 AfterAssocData *assocPtr;
756 char buf[16 + TCL_INTEGER_SPACE];
757 static CONST char *afterSubCmds[] = {
758 "cancel", "idle", "info", (char *) NULL
760 enum afterSubCmds {AFTER_CANCEL, AFTER_IDLE, AFTER_INFO};
761 ThreadSpecificData *tsdPtr = InitTimer();
764 Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?");
769 * Create the "after" information associated for this interpreter,
770 * if it doesn't already exist.
773 assocPtr = Tcl_GetAssocData( interp, "tclAfter", NULL );
774 if (assocPtr == NULL) {
775 assocPtr = (AfterAssocData *) ckalloc(sizeof(AfterAssocData));
776 assocPtr->interp = interp;
777 assocPtr->firstAfterPtr = NULL;
778 Tcl_SetAssocData(interp, "tclAfter", AfterCleanupProc,
779 (ClientData) assocPtr);
783 * First lets see if the command was passed a number as the first argument.
786 if (objv[1]->typePtr == &tclIntType) {
787 ms = (int) objv[1]->internalRep.longValue;
790 argString = Tcl_GetStringFromObj(objv[1], &length);
791 if (argString[0] == '+' || argString[0] == '-'
792 || isdigit(UCHAR(argString[0]))) { /* INTL: digit */
793 if (Tcl_GetIntFromObj(interp, objv[1], &ms) != TCL_OK) {
804 afterPtr = (AfterInfo *) ckalloc((unsigned) (sizeof(AfterInfo)));
805 afterPtr->assocPtr = assocPtr;
807 afterPtr->commandPtr = objv[2];
809 afterPtr->commandPtr = Tcl_ConcatObj(objc-2, objv+2);
811 Tcl_IncrRefCount(afterPtr->commandPtr);
813 * The variable below is used to generate unique identifiers for
814 * after commands. This id can wrap around, which can potentially
815 * cause problems. However, there are not likely to be problems
816 * in practice, because after commands can only be requested to
817 * about a month in the future, and wrap-around is unlikely to
818 * occur in less than about 1-10 years. Thus it's unlikely that
819 * any old ids will still be around when wrap-around occurs.
821 afterPtr->id = tsdPtr->afterId;
822 tsdPtr->afterId += 1;
823 afterPtr->token = Tcl_CreateTimerHandler(ms, AfterProc,
824 (ClientData) afterPtr);
825 afterPtr->nextPtr = assocPtr->firstAfterPtr;
826 assocPtr->firstAfterPtr = afterPtr;
827 sprintf(buf, "after#%d", afterPtr->id);
828 Tcl_AppendResult(interp, buf, (char *) NULL);
833 * If it's not a number it must be a subcommand.
836 if (Tcl_GetIndexFromObj(NULL, objv[1], afterSubCmds, "argument",
837 0, &index) != TCL_OK) {
838 Tcl_AppendResult(interp, "bad argument \"", argString,
839 "\": must be cancel, idle, info, or a number",
843 switch ((enum afterSubCmds) index) {
846 char *command, *tempCommand;
850 Tcl_WrongNumArgs(interp, 2, objv, "id|command");
854 commandPtr = objv[2];
856 commandPtr = Tcl_ConcatObj(objc-2, objv+2);;
858 command = Tcl_GetStringFromObj(commandPtr, &length);
859 for (afterPtr = assocPtr->firstAfterPtr; afterPtr != NULL;
860 afterPtr = afterPtr->nextPtr) {
861 tempCommand = Tcl_GetStringFromObj(afterPtr->commandPtr,
863 if ((length == tempLength)
864 && (memcmp((void*) command, (void*) tempCommand,
865 (unsigned) length) == 0)) {
869 if (afterPtr == NULL) {
870 afterPtr = GetAfterEvent(assocPtr, commandPtr);
873 Tcl_DecrRefCount(commandPtr);
875 if (afterPtr != NULL) {
876 if (afterPtr->token != NULL) {
877 Tcl_DeleteTimerHandler(afterPtr->token);
879 Tcl_CancelIdleCall(AfterProc, (ClientData) afterPtr);
881 FreeAfterPtr(afterPtr);
887 Tcl_WrongNumArgs(interp, 2, objv, "script script ...");
890 afterPtr = (AfterInfo *) ckalloc((unsigned) (sizeof(AfterInfo)));
891 afterPtr->assocPtr = assocPtr;
893 afterPtr->commandPtr = objv[2];
895 afterPtr->commandPtr = Tcl_ConcatObj(objc-2, objv+2);
897 Tcl_IncrRefCount(afterPtr->commandPtr);
898 afterPtr->id = tsdPtr->afterId;
899 tsdPtr->afterId += 1;
900 afterPtr->token = NULL;
901 afterPtr->nextPtr = assocPtr->firstAfterPtr;
902 assocPtr->firstAfterPtr = afterPtr;
903 Tcl_DoWhenIdle(AfterProc, (ClientData) afterPtr);
904 sprintf(buf, "after#%d", afterPtr->id);
905 Tcl_AppendResult(interp, buf, (char *) NULL);
908 Tcl_Obj *resultListPtr;
911 for (afterPtr = assocPtr->firstAfterPtr; afterPtr != NULL;
912 afterPtr = afterPtr->nextPtr) {
913 if (assocPtr->interp == interp) {
914 sprintf(buf, "after#%d", afterPtr->id);
915 Tcl_AppendElement(interp, buf);
921 Tcl_WrongNumArgs(interp, 2, objv, "?id?");
924 afterPtr = GetAfterEvent(assocPtr, objv[2]);
925 if (afterPtr == NULL) {
926 Tcl_AppendResult(interp, "event \"", Tcl_GetString(objv[2]),
927 "\" doesn't exist", (char *) NULL);
930 resultListPtr = Tcl_GetObjResult(interp);
931 Tcl_ListObjAppendElement(interp, resultListPtr, afterPtr->commandPtr);
932 Tcl_ListObjAppendElement(interp, resultListPtr, Tcl_NewStringObj(
933 (afterPtr->token == NULL) ? "idle" : "timer", -1));
934 Tcl_SetObjResult(interp, resultListPtr);
938 panic("Tcl_AfterObjCmd: bad subcommand index to afterSubCmds");
945 *----------------------------------------------------------------------
949 * This procedure parses an "after" id such as "after#4" and
950 * returns a pointer to the AfterInfo structure.
953 * The return value is either a pointer to an AfterInfo structure,
954 * if one is found that corresponds to "cmdString" and is for interp,
955 * or NULL if no corresponding after event can be found.
960 *----------------------------------------------------------------------
964 GetAfterEvent(assocPtr, commandPtr)
965 AfterAssocData *assocPtr; /* Points to "after"-related information for
966 * this interpreter. */
969 char *cmdString; /* Textual identifier for after event, such
975 cmdString = Tcl_GetString(commandPtr);
976 if (strncmp(cmdString, "after#", 6) != 0) {
980 id = strtoul(cmdString, &end, 10);
981 if ((end == cmdString) || (*end != 0)) {
984 for (afterPtr = assocPtr->firstAfterPtr; afterPtr != NULL;
985 afterPtr = afterPtr->nextPtr) {
986 if (afterPtr->id == id) {
994 *----------------------------------------------------------------------
998 * Timer callback to execute commands registered with the
1005 * Executes whatever command was specified. If the command
1006 * returns an error, then the command "bgerror" is invoked
1007 * to process the error; if bgerror fails then information
1008 * about the error is output on stderr.
1010 *----------------------------------------------------------------------
1014 AfterProc(clientData)
1015 ClientData clientData; /* Describes command to execute. */
1017 AfterInfo *afterPtr = (AfterInfo *) clientData;
1018 AfterAssocData *assocPtr = afterPtr->assocPtr;
1026 * First remove the callback from our list of callbacks; otherwise
1027 * someone could delete the callback while it's being executed, which
1028 * could cause a core dump.
1031 if (assocPtr->firstAfterPtr == afterPtr) {
1032 assocPtr->firstAfterPtr = afterPtr->nextPtr;
1034 for (prevPtr = assocPtr->firstAfterPtr; prevPtr->nextPtr != afterPtr;
1035 prevPtr = prevPtr->nextPtr) {
1036 /* Empty loop body. */
1038 prevPtr->nextPtr = afterPtr->nextPtr;
1042 * Execute the callback.
1045 interp = assocPtr->interp;
1046 Tcl_Preserve((ClientData) interp);
1047 script = Tcl_GetStringFromObj(afterPtr->commandPtr, &numBytes);
1048 result = Tcl_EvalEx(interp, script, numBytes, TCL_EVAL_GLOBAL);
1049 if (result != TCL_OK) {
1050 Tcl_AddErrorInfo(interp, "\n (\"after\" script)");
1051 Tcl_BackgroundError(interp);
1053 Tcl_Release((ClientData) interp);
1056 * Free the memory for the callback.
1059 Tcl_DecrRefCount(afterPtr->commandPtr);
1060 ckfree((char *) afterPtr);
1064 *----------------------------------------------------------------------
1068 * This procedure removes an "after" command from the list of
1069 * those that are pending and frees its resources. This procedure
1070 * does *not* cancel the timer handler; if that's needed, the
1071 * caller must do it.
1077 * The memory associated with afterPtr is released.
1079 *----------------------------------------------------------------------
1083 FreeAfterPtr(afterPtr)
1084 AfterInfo *afterPtr; /* Command to be deleted. */
1087 AfterAssocData *assocPtr = afterPtr->assocPtr;
1089 if (assocPtr->firstAfterPtr == afterPtr) {
1090 assocPtr->firstAfterPtr = afterPtr->nextPtr;
1092 for (prevPtr = assocPtr->firstAfterPtr; prevPtr->nextPtr != afterPtr;
1093 prevPtr = prevPtr->nextPtr) {
1094 /* Empty loop body. */
1096 prevPtr->nextPtr = afterPtr->nextPtr;
1098 Tcl_DecrRefCount(afterPtr->commandPtr);
1099 ckfree((char *) afterPtr);
1103 *----------------------------------------------------------------------
1105 * AfterCleanupProc --
1107 * This procedure is invoked whenever an interpreter is deleted
1108 * to cleanup the AssocData for "tclAfter".
1114 * After commands are removed.
1116 *----------------------------------------------------------------------
1121 AfterCleanupProc(clientData, interp)
1122 ClientData clientData; /* Points to AfterAssocData for the
1124 Tcl_Interp *interp; /* Interpreter that is being deleted. */
1126 AfterAssocData *assocPtr = (AfterAssocData *) clientData;
1127 AfterInfo *afterPtr;
1129 while (assocPtr->firstAfterPtr != NULL) {
1130 afterPtr = assocPtr->firstAfterPtr;
1131 assocPtr->firstAfterPtr = afterPtr->nextPtr;
1132 if (afterPtr->token != NULL) {
1133 Tcl_DeleteTimerHandler(afterPtr->token);
1135 Tcl_CancelIdleCall(AfterProc, (ClientData) afterPtr);
1137 Tcl_DecrRefCount(afterPtr->commandPtr);
1138 ckfree((char *) afterPtr);
1140 ckfree((char *) assocPtr);