os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/generic/tclTimer.c
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
     1 /* 
     2  * tclTimer.c --
     3  *
     4  *	This file provides timer event management facilities for Tcl,
     5  *	including the "after" command.
     6  *
     7  * Copyright (c) 1997 by Sun Microsystems, Inc.
     8  * Portions Copyright (c) 2007-2008 Nokia Corporation and/or its subsidiaries. All rights reserved.  
     9  *
    10  * See the file "license.terms" for information on usage and redistribution
    11  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
    12  *
    13  * RCS: @(#) $Id: tclTimer.c,v 1.6.2.4 2005/11/09 21:46:20 kennykb Exp $
    14  */
    15 
    16 #include "tclInt.h"
    17 #include "tclPort.h"
    18 #if defined(__SYMBIAN32__) && defined(__WINSCW__)
    19 #include "tclSymbianGlobals.h"
    20 #define dataKey getdataKey(7)
    21 #endif 
    22 
    23 /*
    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).
    27  */
    28 
    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
    34 					 * deleted. */
    35     struct TimerHandler *nextPtr;	/* Next event in queue, or NULL for
    36 					 * end of queue. */
    37 } TimerHandler;
    38 
    39 /*
    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.
    43  */
    44 
    45 typedef struct AfterInfo {
    46     struct AfterAssocData *assocPtr;
    47 				/* Pointer to the "tclAfter" assocData for
    48 				 * the interp in which command will be
    49 				 * executed. */
    50     Tcl_Obj *commandPtr;	/* Command to execute. */
    51     int id;			/* Integer identifier for command;  used to
    52 				 * cancel it. */
    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
    58                                  * timer handler. */
    59     struct AfterInfo *nextPtr;	/* Next in list of all "after" commands for
    60 				 * this interpreter. */
    61 } AfterInfo;
    62 
    63 /*
    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.
    67  */
    68 
    69 typedef struct AfterAssocData {
    70     Tcl_Interp *interp;		/* The interpreter for which this data is
    71 				 * registered. */
    72     AfterInfo *firstAfterPtr;	/* First in list of all "after" commands
    73 				 * still pending for this interpreter, or
    74 				 * NULL if none. */
    75 } AfterAssocData;
    76 
    77 /*
    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.
    81  */
    82 
    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. */
    89 } IdleHandler;
    90 
    91 /*
    92  * The timer and idle queues are per-thread because they are associated
    93  * with the notifier, which is also per-thread.
    94  *
    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.
    98  *
    99  * Notice that different structures with the same name appear in other
   100  * files.  The structure defined below is used in this file only.
   101  */
   102 
   103 typedef struct ThreadSpecificData {
   104     TimerHandler *firstTimerHandlerPtr;	/* First event in queue. */
   105     int lastTimerId;		/* Timer identifier of most recently
   106 				 * created timer. */
   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;
   118 
   119 #if !defined(__SYMBIAN32__) || !defined(__WINSCW__)
   120 static Tcl_ThreadDataKey dataKey;
   121 #endif
   122 
   123 /*
   124  * Prototypes for procedures referenced only in this file:
   125  */
   126 
   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,
   136 			    int flags));
   137 static void		TimerCheckProc _ANSI_ARGS_((ClientData clientData,
   138 			    int flags));
   139 static void		TimerSetupProc _ANSI_ARGS_((ClientData clientData,
   140 			    int flags));
   141 
   142 /*
   143  *----------------------------------------------------------------------
   144  *
   145  * InitTimer --
   146  *
   147  *	This function initializes the timer module.
   148  *
   149  * Results:
   150  *	A pointer to the thread specific data.
   151  *
   152  * Side effects:
   153  *	Registers the idle and timer event sources.
   154  *
   155  *----------------------------------------------------------------------
   156  */
   157 
   158 static ThreadSpecificData *
   159 InitTimer()
   160 {
   161     ThreadSpecificData *tsdPtr = 
   162 	(ThreadSpecificData *) TclThreadDataKeyGet(&dataKey);
   163 
   164     if (tsdPtr == NULL) {
   165 	tsdPtr = TCL_TSD_INIT(&dataKey);
   166 	Tcl_CreateEventSource(TimerSetupProc, TimerCheckProc, NULL);
   167 	Tcl_CreateThreadExitHandler(TimerExitProc, NULL);
   168     }
   169     return tsdPtr;
   170 }
   171 
   172 /*
   173  *----------------------------------------------------------------------
   174  *
   175  * TimerExitProc --
   176  *
   177  *	This function is call at exit or unload time to remove the
   178  *	timer and idle event sources.
   179  *
   180  * Results:
   181  *	None.
   182  *
   183  * Side effects:
   184  *	Removes the timer and idle event sources and remaining events.
   185  *
   186  *----------------------------------------------------------------------
   187  */
   188 
   189 static void
   190 TimerExitProc(clientData)
   191     ClientData clientData;	/* Not used. */
   192 {
   193     ThreadSpecificData *tsdPtr =
   194 	(ThreadSpecificData *) TclThreadDataKeyGet(&dataKey);
   195 
   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;
   204 	}
   205     }
   206 }
   207 
   208 /*
   209  *--------------------------------------------------------------
   210  *
   211  * Tcl_CreateTimerHandler --
   212  *
   213  *	Arrange for a given procedure to be invoked at a particular
   214  *	time in the future.
   215  *
   216  * Results:
   217  *	The return value is a token for the timer event, which
   218  *	may be used to delete the event before it fires.
   219  *
   220  * Side effects:
   221  *	When milliseconds have elapsed, proc will be invoked
   222  *	exactly once.
   223  *
   224  *--------------------------------------------------------------
   225  */
   226 
   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. */
   233 {
   234     register TimerHandler *timerHandlerPtr, *tPtr2, *prevPtr;
   235     Tcl_Time time;
   236     ThreadSpecificData *tsdPtr;
   237 
   238     tsdPtr = InitTimer();
   239 
   240     timerHandlerPtr = (TimerHandler *) ckalloc(sizeof(TimerHandler));
   241 
   242     /*
   243      * Compute when the event should fire.
   244      */
   245 
   246     Tcl_GetTime(&time);
   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;
   252     }
   253 
   254     /*
   255      * Fill in other fields for the event.
   256      */
   257 
   258     timerHandlerPtr->proc = proc;
   259     timerHandlerPtr->clientData = clientData;
   260     tsdPtr->lastTimerId++;
   261     timerHandlerPtr->token = (Tcl_TimerToken) tsdPtr->lastTimerId;
   262 
   263     /*
   264      * Add the event to the queue in the correct position
   265      * (ordered by event firing time).
   266      */
   267 
   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))) {
   273 	    break;
   274 	}
   275     }
   276     timerHandlerPtr->nextPtr = tPtr2;
   277     if (prevPtr == NULL) {
   278 	tsdPtr->firstTimerHandlerPtr = timerHandlerPtr;
   279     } else {
   280 	prevPtr->nextPtr = timerHandlerPtr;
   281     }
   282 
   283     TimerSetupProc(NULL, TCL_ALL_EVENTS);
   284 
   285     return timerHandlerPtr->token;
   286 }
   287 
   288 /*
   289  *--------------------------------------------------------------
   290  *
   291  * Tcl_DeleteTimerHandler --
   292  *
   293  *	Delete a previously-registered timer handler.
   294  *
   295  * Results:
   296  *	None.
   297  *
   298  * Side effects:
   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.
   303  *
   304  *--------------------------------------------------------------
   305  */
   306 
   307 EXPORT_C void
   308 Tcl_DeleteTimerHandler(token)
   309     Tcl_TimerToken token;	/* Result previously returned by
   310 				 * Tcl_DeleteTimerHandler. */
   311 {
   312     register TimerHandler *timerHandlerPtr, *prevPtr;
   313     ThreadSpecificData *tsdPtr = InitTimer();
   314 
   315     if (token == NULL) {
   316 	return;
   317     }
   318 
   319     for (timerHandlerPtr = tsdPtr->firstTimerHandlerPtr, prevPtr = NULL;
   320 	    timerHandlerPtr != NULL; prevPtr = timerHandlerPtr,
   321 	    timerHandlerPtr = timerHandlerPtr->nextPtr) {
   322 	if (timerHandlerPtr->token != token) {
   323 	    continue;
   324 	}
   325 	if (prevPtr == NULL) {
   326 	    tsdPtr->firstTimerHandlerPtr = timerHandlerPtr->nextPtr;
   327 	} else {
   328 	    prevPtr->nextPtr = timerHandlerPtr->nextPtr;
   329 	}
   330 	ckfree((char *) timerHandlerPtr);
   331 	return;
   332     }
   333 }
   334 
   335 /*
   336  *----------------------------------------------------------------------
   337  *
   338  * TimerSetupProc --
   339  *
   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.
   343  *
   344  * Results:
   345  *	None.
   346  *
   347  * Side effects:
   348  *	May update the maximum notifier block time.
   349  *
   350  *----------------------------------------------------------------------
   351  */
   352 
   353 static void
   354 TimerSetupProc(data, flags)
   355     ClientData data;		/* Not used. */
   356     int flags;			/* Event flags as passed to Tcl_DoOneEvent. */
   357 {
   358     Tcl_Time blockTime;
   359     ThreadSpecificData *tsdPtr = InitTimer();
   360 
   361     if (((flags & TCL_IDLE_EVENTS) && tsdPtr->idleList)
   362 	    || ((flags & TCL_TIMER_EVENTS) && tsdPtr->timerPending)) {
   363 	/*
   364 	 * There is an idle handler or a pending timer event, so just poll.
   365 	 */
   366 
   367 	blockTime.sec = 0;
   368 	blockTime.usec = 0;
   369 
   370     } else if ((flags & TCL_TIMER_EVENTS) && tsdPtr->firstTimerHandlerPtr) {
   371 	/*
   372 	 * Compute the timeout for the next timer on the list.
   373 	 */
   374 
   375 	Tcl_GetTime(&blockTime);
   376 	blockTime.sec = tsdPtr->firstTimerHandlerPtr->time.sec - blockTime.sec;
   377 	blockTime.usec = tsdPtr->firstTimerHandlerPtr->time.usec -
   378 		blockTime.usec;
   379 	if (blockTime.usec < 0) {
   380 	    blockTime.sec -= 1;
   381 	    blockTime.usec += 1000000;
   382 	}
   383 	if (blockTime.sec < 0) {
   384 	    blockTime.sec = 0;
   385 	    blockTime.usec = 0;
   386 	}
   387     } else {
   388 	return;
   389     }
   390 	
   391     Tcl_SetMaxBlockTime(&blockTime);
   392 }
   393 
   394 /*
   395  *----------------------------------------------------------------------
   396  *
   397  * TimerCheckProc --
   398  *
   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.
   402  *
   403  * Results:
   404  *	None.
   405  *
   406  * Side effects:
   407  *	May queue an event and update the maximum notifier block time.
   408  *
   409  *----------------------------------------------------------------------
   410  */
   411 
   412 static void
   413 TimerCheckProc(data, flags)
   414     ClientData data;		/* Not used. */
   415     int flags;			/* Event flags as passed to Tcl_DoOneEvent. */
   416 {
   417     Tcl_Event *timerEvPtr;
   418     Tcl_Time blockTime;
   419     ThreadSpecificData *tsdPtr = InitTimer();
   420 
   421     if ((flags & TCL_TIMER_EVENTS) && tsdPtr->firstTimerHandlerPtr) {
   422 	/*
   423 	 * Compute the timeout for the next timer on the list.
   424 	 */
   425 
   426 	Tcl_GetTime(&blockTime);
   427 	blockTime.sec = tsdPtr->firstTimerHandlerPtr->time.sec - blockTime.sec;
   428 	blockTime.usec = tsdPtr->firstTimerHandlerPtr->time.usec -
   429 		blockTime.usec;
   430 	if (blockTime.usec < 0) {
   431 	    blockTime.sec -= 1;
   432 	    blockTime.usec += 1000000;
   433 	}
   434 	if (blockTime.sec < 0) {
   435 	    blockTime.sec = 0;
   436 	    blockTime.usec = 0;
   437 	}
   438 
   439 	/*
   440 	 * If the first timer has expired, stick an event on the queue.
   441 	 */
   442 
   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);
   449 	}
   450     }
   451 }
   452 
   453 /*
   454  *----------------------------------------------------------------------
   455  *
   456  * TimerHandlerEventProc --
   457  *
   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
   461  *	ready.
   462  *
   463  * Results:
   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.
   468  *
   469  * Side effects:
   470  *	Whatever the timer handler callback procedures do.
   471  *
   472  *----------------------------------------------------------------------
   473  */
   474 
   475 static int
   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. */
   480 {
   481     TimerHandler *timerHandlerPtr, **nextPtrPtr;
   482     Tcl_Time time;
   483     int currentTimerId;
   484     ThreadSpecificData *tsdPtr = InitTimer();
   485 
   486     /*
   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.
   490      */
   491 
   492     if (!(flags & TCL_TIMER_EVENTS)) {
   493 	return 0;
   494     }
   495 
   496     /*
   497      * The code below is trickier than it may look, for the following
   498      * reasons:
   499      *
   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.
   521      */
   522 
   523     tsdPtr->timerPending = 0;
   524     currentTimerId = tsdPtr->lastTimerId;
   525     Tcl_GetTime(&time);
   526     while (1) {
   527 	nextPtrPtr = &tsdPtr->firstTimerHandlerPtr;
   528 	timerHandlerPtr = tsdPtr->firstTimerHandlerPtr;
   529 	if (timerHandlerPtr == NULL) {
   530 	    break;
   531 	}
   532 	    
   533 	if ((timerHandlerPtr->time.sec > time.sec)
   534 		|| ((timerHandlerPtr->time.sec == time.sec)
   535 			&& (timerHandlerPtr->time.usec > time.usec))) {
   536 	    break;
   537 	}
   538 
   539 	/*
   540 	 * Bail out if the next timer is of a newer generation.
   541 	 */
   542 
   543 	if ((currentTimerId - (int)timerHandlerPtr->token) < 0) {
   544 	    break;
   545 	}
   546 
   547 	/*
   548 	 * Remove the handler from the queue before invoking it,
   549 	 * to avoid potential reentrancy problems.
   550 	 */
   551 
   552 	(*nextPtrPtr) = timerHandlerPtr->nextPtr;
   553 	(*timerHandlerPtr->proc)(timerHandlerPtr->clientData);
   554 	ckfree((char *) timerHandlerPtr);
   555     }
   556     TimerSetupProc(NULL, TCL_TIMER_EVENTS);
   557     return 1;
   558 }
   559 
   560 /*
   561  *--------------------------------------------------------------
   562  *
   563  * Tcl_DoWhenIdle --
   564  *
   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).
   568  *
   569  * Results:
   570  *	None.
   571  *
   572  * Side effects:
   573  *	Proc will eventually be called, with clientData as argument.
   574  *	See the manual entry for details.
   575  *
   576  *--------------------------------------------------------------
   577  */
   578 
   579 EXPORT_C void
   580 Tcl_DoWhenIdle(proc, clientData)
   581     Tcl_IdleProc *proc;		/* Procedure to invoke. */
   582     ClientData clientData;	/* Arbitrary value to pass to proc. */
   583 {
   584     register IdleHandler *idlePtr;
   585     Tcl_Time blockTime;
   586     ThreadSpecificData *tsdPtr = InitTimer();
   587 
   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;
   595     } else {
   596 	tsdPtr->lastIdlePtr->nextPtr = idlePtr;
   597     }
   598     tsdPtr->lastIdlePtr = idlePtr;
   599 
   600     blockTime.sec = 0;
   601     blockTime.usec = 0;
   602     Tcl_SetMaxBlockTime(&blockTime);
   603 }
   604 
   605 /*
   606  *----------------------------------------------------------------------
   607  *
   608  * Tcl_CancelIdleCall --
   609  *
   610  *	If there are any when-idle calls requested to a given procedure
   611  *	with given clientData, cancel all of them.
   612  *
   613  * Results:
   614  *	None.
   615  *
   616  * Side effects:
   617  *	If the proc/clientData combination were on the when-idle list,
   618  *	they are removed so that they will never be called.
   619  *
   620  *----------------------------------------------------------------------
   621  */
   622 
   623 EXPORT_C void
   624 Tcl_CancelIdleCall(proc, clientData)
   625     Tcl_IdleProc *proc;		/* Procedure that was previously registered. */
   626     ClientData clientData;	/* Arbitrary value to pass to proc. */
   627 {
   628     register IdleHandler *idlePtr, *prevPtr;
   629     IdleHandler *nextPtr;
   630     ThreadSpecificData *tsdPtr = InitTimer();
   631 
   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);
   638 	    idlePtr = nextPtr;
   639 	    if (prevPtr == NULL) {
   640 		tsdPtr->idleList = idlePtr;
   641 	    } else {
   642 		prevPtr->nextPtr = idlePtr;
   643 	    }
   644 	    if (idlePtr == NULL) {
   645 		tsdPtr->lastIdlePtr = prevPtr;
   646 		return;
   647 	    }
   648 	}
   649     }
   650 }
   651 
   652 /*
   653  *----------------------------------------------------------------------
   654  *
   655  * TclServiceIdle --
   656  *
   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
   660  *	processing.
   661  *
   662  * Results:
   663  *	The return value is 1 if TclServiceIdle found something to
   664  *	do, otherwise return value is 0.
   665  *
   666  * Side effects:
   667  *	Invokes all pending idle handlers.
   668  *
   669  *----------------------------------------------------------------------
   670  */
   671 
   672 int
   673 TclServiceIdle()
   674 {
   675     IdleHandler *idlePtr;
   676     int oldGeneration;
   677     Tcl_Time blockTime;
   678     ThreadSpecificData *tsdPtr = InitTimer();
   679 
   680     if (tsdPtr->idleList == NULL) {
   681 	return 0;
   682     }
   683 
   684     oldGeneration = tsdPtr->idleGeneration;
   685     tsdPtr->idleGeneration++;
   686 
   687     /*
   688      * The code below is trickier than it may look, for the following
   689      * reasons:
   690      *
   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
   697      *    on the list.
   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.
   704      */
   705 
   706     for (idlePtr = tsdPtr->idleList;
   707 	    ((idlePtr != NULL)
   708 		    && ((oldGeneration - idlePtr->generation) >= 0));
   709 	    idlePtr = tsdPtr->idleList) {
   710 	tsdPtr->idleList = idlePtr->nextPtr;
   711 	if (tsdPtr->idleList == NULL) {
   712 	    tsdPtr->lastIdlePtr = NULL;
   713 	}
   714 	(*idlePtr->proc)(idlePtr->clientData);
   715 	ckfree((char *) idlePtr);
   716     }
   717     if (tsdPtr->idleList) {
   718 	blockTime.sec = 0;
   719 	blockTime.usec = 0;
   720 	Tcl_SetMaxBlockTime(&blockTime);
   721     }
   722     return 1;
   723 }
   724 
   725 /*
   726  *----------------------------------------------------------------------
   727  *
   728  * Tcl_AfterObjCmd --
   729  *
   730  *	This procedure is invoked to process the "after" Tcl command.
   731  *	See the user documentation for details on what it does.
   732  *
   733  * Results:
   734  *	A standard Tcl result.
   735  *
   736  * Side effects:
   737  *	See the user documentation.
   738  *
   739  *----------------------------------------------------------------------
   740  */
   741 
   742 	/* ARGSUSED */
   743 int
   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. */
   749 {
   750     int ms;
   751     AfterInfo *afterPtr;
   752     AfterAssocData *assocPtr;
   753     int length;
   754     char *argString;
   755     int index;
   756     char buf[16 + TCL_INTEGER_SPACE];
   757     static CONST char *afterSubCmds[] = {
   758 	"cancel", "idle", "info", (char *) NULL
   759     };
   760     enum afterSubCmds {AFTER_CANCEL, AFTER_IDLE, AFTER_INFO};
   761     ThreadSpecificData *tsdPtr = InitTimer();
   762 
   763     if (objc < 2) {
   764 	Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?");
   765 	return TCL_ERROR;
   766     }
   767 
   768     /*
   769      * Create the "after" information associated for this interpreter,
   770      * if it doesn't already exist.  
   771      */
   772 
   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);
   780     }
   781 
   782     /*
   783      * First lets see if the command was passed a number as the first argument.
   784      */
   785 
   786     if (objv[1]->typePtr == &tclIntType) {
   787 	ms = (int) objv[1]->internalRep.longValue;
   788 	goto processInteger;
   789     }
   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) {
   794 	    return TCL_ERROR;
   795 	}
   796 processInteger:
   797 	if (ms < 0) {
   798 	    ms = 0;
   799 	}
   800 	if (objc == 2) {
   801 	    Tcl_Sleep(ms);
   802 	    return TCL_OK;
   803 	}
   804 	afterPtr = (AfterInfo *) ckalloc((unsigned) (sizeof(AfterInfo)));
   805 	afterPtr->assocPtr = assocPtr;
   806 	if (objc == 3) {
   807 	    afterPtr->commandPtr = objv[2];
   808 	} else {
   809  	    afterPtr->commandPtr = Tcl_ConcatObj(objc-2, objv+2);
   810 	}
   811 	Tcl_IncrRefCount(afterPtr->commandPtr);
   812 	/*
   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.
   820 	 */
   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);
   829 	return TCL_OK;
   830     }
   831 
   832     /*
   833      * If it's not a number it must be a subcommand.
   834      */
   835 
   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",
   840 		(char *) NULL);
   841 	return TCL_ERROR;
   842     }
   843     switch ((enum afterSubCmds) index) {
   844         case AFTER_CANCEL: {
   845 	    Tcl_Obj *commandPtr;
   846 	    char *command, *tempCommand;
   847 	    int tempLength;
   848 
   849 	    if (objc < 3) {
   850 		Tcl_WrongNumArgs(interp, 2, objv, "id|command");
   851 		return TCL_ERROR;
   852 	    }
   853 	    if (objc == 3) {
   854 		commandPtr = objv[2];
   855 	    } else {
   856 		commandPtr = Tcl_ConcatObj(objc-2, objv+2);;
   857 	    }
   858 	    command = Tcl_GetStringFromObj(commandPtr, &length);
   859 	    for (afterPtr = assocPtr->firstAfterPtr;  afterPtr != NULL;
   860 		    afterPtr = afterPtr->nextPtr) {
   861 		tempCommand = Tcl_GetStringFromObj(afterPtr->commandPtr,
   862 			&tempLength);
   863 		if ((length == tempLength)
   864 		        && (memcmp((void*) command, (void*) tempCommand,
   865 			        (unsigned) length) == 0)) {
   866 		    break;
   867 		}
   868 	    }
   869 	    if (afterPtr == NULL) {
   870 		afterPtr = GetAfterEvent(assocPtr, commandPtr);
   871 	    }
   872 	    if (objc != 3) {
   873 		Tcl_DecrRefCount(commandPtr);
   874 	    }
   875 	    if (afterPtr != NULL) {
   876 		if (afterPtr->token != NULL) {
   877 		    Tcl_DeleteTimerHandler(afterPtr->token);
   878 		} else {
   879 		    Tcl_CancelIdleCall(AfterProc, (ClientData) afterPtr);
   880 		}
   881 		FreeAfterPtr(afterPtr);
   882 	    }
   883 	    break;
   884 	}
   885 	case AFTER_IDLE:
   886 	    if (objc < 3) {
   887 		Tcl_WrongNumArgs(interp, 2, objv, "script script ...");
   888 		return TCL_ERROR;
   889 	    }
   890 	    afterPtr = (AfterInfo *) ckalloc((unsigned) (sizeof(AfterInfo)));
   891 	    afterPtr->assocPtr = assocPtr;
   892 	    if (objc == 3) {
   893  		afterPtr->commandPtr = objv[2];
   894 	    } else {
   895 		afterPtr->commandPtr = Tcl_ConcatObj(objc-2, objv+2);
   896 	    }
   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);
   906 	    break;
   907 	case AFTER_INFO: {
   908 	    Tcl_Obj *resultListPtr;
   909 
   910 	    if (objc == 2) {
   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);
   916 		    }
   917 		}
   918 		return TCL_OK;
   919 	    }
   920 	    if (objc != 3) {
   921 		Tcl_WrongNumArgs(interp, 2, objv, "?id?");
   922 		return TCL_ERROR;
   923 	    }
   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);
   928 		return TCL_ERROR;
   929 	    }
   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);
   935 	    break;
   936 	}
   937 	default: {
   938 	    panic("Tcl_AfterObjCmd: bad subcommand index to afterSubCmds");
   939 	}
   940     }
   941     return TCL_OK;
   942 }
   943 
   944 /*
   945  *----------------------------------------------------------------------
   946  *
   947  * GetAfterEvent --
   948  *
   949  *	This procedure parses an "after" id such as "after#4" and
   950  *	returns a pointer to the AfterInfo structure.
   951  *
   952  * Results:
   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.
   956  *
   957  * Side effects:
   958  *	None.
   959  *
   960  *----------------------------------------------------------------------
   961  */
   962 
   963 static AfterInfo *
   964 GetAfterEvent(assocPtr, commandPtr)
   965     AfterAssocData *assocPtr;	/* Points to "after"-related information for
   966 				 * this interpreter. */
   967     Tcl_Obj *commandPtr;
   968 {
   969     char *cmdString;		/* Textual identifier for after event, such
   970 				 * as "after#6". */
   971     AfterInfo *afterPtr;
   972     int id;
   973     char *end;
   974 
   975     cmdString = Tcl_GetString(commandPtr);
   976     if (strncmp(cmdString, "after#", 6) != 0) {
   977 	return NULL;
   978     }
   979     cmdString += 6;
   980     id = strtoul(cmdString, &end, 10);
   981     if ((end == cmdString) || (*end != 0)) {
   982 	return NULL;
   983     }
   984     for (afterPtr = assocPtr->firstAfterPtr; afterPtr != NULL;
   985 	    afterPtr = afterPtr->nextPtr) {
   986 	if (afterPtr->id == id) {
   987 	    return afterPtr;
   988 	}
   989     }
   990     return NULL;
   991 }
   992 
   993 /*
   994  *----------------------------------------------------------------------
   995  *
   996  * AfterProc --
   997  *
   998  *	Timer callback to execute commands registered with the
   999  *	"after" command.
  1000  *
  1001  * Results:
  1002  *	None.
  1003  *
  1004  * Side effects:
  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.
  1009  *
  1010  *----------------------------------------------------------------------
  1011  */
  1012 
  1013 static void
  1014 AfterProc(clientData)
  1015     ClientData clientData;	/* Describes command to execute. */
  1016 {
  1017     AfterInfo *afterPtr = (AfterInfo *) clientData;
  1018     AfterAssocData *assocPtr = afterPtr->assocPtr;
  1019     AfterInfo *prevPtr;
  1020     int result;
  1021     Tcl_Interp *interp;
  1022     char *script;
  1023     int numBytes;
  1024 
  1025     /*
  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.
  1029      */
  1030 
  1031     if (assocPtr->firstAfterPtr == afterPtr) {
  1032 	assocPtr->firstAfterPtr = afterPtr->nextPtr;
  1033     } else {
  1034 	for (prevPtr = assocPtr->firstAfterPtr; prevPtr->nextPtr != afterPtr;
  1035 		prevPtr = prevPtr->nextPtr) {
  1036 	    /* Empty loop body. */
  1037 	}
  1038 	prevPtr->nextPtr = afterPtr->nextPtr;
  1039     }
  1040 
  1041     /*
  1042      * Execute the callback.
  1043      */
  1044 
  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);
  1052     }
  1053     Tcl_Release((ClientData) interp);
  1054     
  1055     /*
  1056      * Free the memory for the callback.
  1057      */
  1058 
  1059     Tcl_DecrRefCount(afterPtr->commandPtr);
  1060     ckfree((char *) afterPtr);
  1061 }
  1062 
  1063 /*
  1064  *----------------------------------------------------------------------
  1065  *
  1066  * FreeAfterPtr --
  1067  *
  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.
  1072  *
  1073  * Results:
  1074  *	None.
  1075  *
  1076  * Side effects:
  1077  *	The memory associated with afterPtr is released.
  1078  *
  1079  *----------------------------------------------------------------------
  1080  */
  1081 
  1082 static void
  1083 FreeAfterPtr(afterPtr)
  1084     AfterInfo *afterPtr;		/* Command to be deleted. */
  1085 {
  1086     AfterInfo *prevPtr;
  1087     AfterAssocData *assocPtr = afterPtr->assocPtr;
  1088 
  1089     if (assocPtr->firstAfterPtr == afterPtr) {
  1090 	assocPtr->firstAfterPtr = afterPtr->nextPtr;
  1091     } else {
  1092 	for (prevPtr = assocPtr->firstAfterPtr; prevPtr->nextPtr != afterPtr;
  1093 		prevPtr = prevPtr->nextPtr) {
  1094 	    /* Empty loop body. */
  1095 	}
  1096 	prevPtr->nextPtr = afterPtr->nextPtr;
  1097     }
  1098     Tcl_DecrRefCount(afterPtr->commandPtr);
  1099     ckfree((char *) afterPtr);
  1100 }
  1101 
  1102 /*
  1103  *----------------------------------------------------------------------
  1104  *
  1105  * AfterCleanupProc --
  1106  *
  1107  *	This procedure is invoked whenever an interpreter is deleted
  1108  *	to cleanup the AssocData for "tclAfter".
  1109  *
  1110  * Results:
  1111  *	None.
  1112  *
  1113  * Side effects:
  1114  *	After commands are removed.
  1115  *
  1116  *----------------------------------------------------------------------
  1117  */
  1118 
  1119 	/* ARGSUSED */
  1120 static void
  1121 AfterCleanupProc(clientData, interp)
  1122     ClientData clientData;	/* Points to AfterAssocData for the
  1123 				 * interpreter. */
  1124     Tcl_Interp *interp;		/* Interpreter that is being deleted. */
  1125 {
  1126     AfterAssocData *assocPtr = (AfterAssocData *) clientData;
  1127     AfterInfo *afterPtr;
  1128 
  1129     while (assocPtr->firstAfterPtr != NULL) {
  1130 	afterPtr = assocPtr->firstAfterPtr;
  1131 	assocPtr->firstAfterPtr = afterPtr->nextPtr;
  1132 	if (afterPtr->token != NULL) {
  1133 	    Tcl_DeleteTimerHandler(afterPtr->token);
  1134 	} else {
  1135 	    Tcl_CancelIdleCall(AfterProc, (ClientData) afterPtr);
  1136 	}
  1137 	Tcl_DecrRefCount(afterPtr->commandPtr);
  1138 	ckfree((char *) afterPtr);
  1139     }
  1140     ckfree((char *) assocPtr);
  1141 }