tclTimer.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:32k
源码类别:

通讯编程

开发平台:

Visual C++

  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.  *
  9.  * See the file "license.terms" for information on usage and redistribution
  10.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  11.  *
  12.  * RCS: @(#) $Id: tclTimer.c,v 1.6.2.4 2005/11/09 21:46:20 kennykb Exp $
  13.  */
  14. #include "tclInt.h"
  15. #include "tclPort.h"
  16. /*
  17.  * For each timer callback that's pending there is one record of the following
  18.  * type.  The normal handlers (created by Tcl_CreateTimerHandler) are chained
  19.  * together in a list sorted by time (earliest event first).
  20.  */
  21. typedef struct TimerHandler {
  22.     Tcl_Time time; /* When timer is to fire. */
  23.     Tcl_TimerProc *proc; /* Procedure to call. */
  24.     ClientData clientData; /* Argument to pass to proc. */
  25.     Tcl_TimerToken token; /* Identifies handler so it can be
  26.  * deleted. */
  27.     struct TimerHandler *nextPtr; /* Next event in queue, or NULL for
  28.  * end of queue. */
  29. } TimerHandler;
  30. /*
  31.  * The data structure below is used by the "after" command to remember
  32.  * the command to be executed later.  All of the pending "after" commands
  33.  * for an interpreter are linked together in a list.
  34.  */
  35. typedef struct AfterInfo {
  36.     struct AfterAssocData *assocPtr;
  37. /* Pointer to the "tclAfter" assocData for
  38.  * the interp in which command will be
  39.  * executed. */
  40.     Tcl_Obj *commandPtr; /* Command to execute. */
  41.     int id; /* Integer identifier for command;  used to
  42.  * cancel it. */
  43.     Tcl_TimerToken token; /* Used to cancel the "after" command.  NULL
  44.  * means that the command is run as an
  45.  * idle handler rather than as a timer
  46.  * handler.  NULL means this is an "after
  47.  * idle" handler rather than a
  48.                                  * timer handler. */
  49.     struct AfterInfo *nextPtr; /* Next in list of all "after" commands for
  50.  * this interpreter. */
  51. } AfterInfo;
  52. /*
  53.  * One of the following structures is associated with each interpreter
  54.  * for which an "after" command has ever been invoked.  A pointer to
  55.  * this structure is stored in the AssocData for the "tclAfter" key.
  56.  */
  57. typedef struct AfterAssocData {
  58.     Tcl_Interp *interp; /* The interpreter for which this data is
  59.  * registered. */
  60.     AfterInfo *firstAfterPtr; /* First in list of all "after" commands
  61.  * still pending for this interpreter, or
  62.  * NULL if none. */
  63. } AfterAssocData;
  64. /*
  65.  * There is one of the following structures for each of the
  66.  * handlers declared in a call to Tcl_DoWhenIdle.  All of the
  67.  * currently-active handlers are linked together into a list.
  68.  */
  69. typedef struct IdleHandler {
  70.     Tcl_IdleProc (*proc); /* Procedure to call. */
  71.     ClientData clientData; /* Value to pass to proc. */
  72.     int generation; /* Used to distinguish older handlers from
  73.  * recently-created ones. */
  74.     struct IdleHandler *nextPtr;/* Next in list of active handlers. */
  75. } IdleHandler;
  76. /*
  77.  * The timer and idle queues are per-thread because they are associated
  78.  * with the notifier, which is also per-thread.
  79.  *
  80.  * All static variables used in this file are collected into a single
  81.  * instance of the following structure.  For multi-threaded implementations,
  82.  * there is one instance of this structure for each thread.
  83.  *
  84.  * Notice that different structures with the same name appear in other
  85.  * files.  The structure defined below is used in this file only.
  86.  */
  87. typedef struct ThreadSpecificData {
  88.     TimerHandler *firstTimerHandlerPtr; /* First event in queue. */
  89.     int lastTimerId; /* Timer identifier of most recently
  90.  * created timer. */
  91.     int timerPending; /* 1 if a timer event is in the queue. */
  92.     IdleHandler *idleList; /* First in list of all idle handlers. */
  93.     IdleHandler *lastIdlePtr; /* Last in list (or NULL for empty list). */
  94.     int idleGeneration; /* Used to fill in the "generation" fields
  95.  * of IdleHandler structures.  Increments
  96.  * each time Tcl_DoOneEvent starts calling
  97.  * idle handlers, so that all old handlers
  98.  * can be called without calling any of the
  99.  * new ones created by old ones. */
  100.     int afterId; /* For unique identifiers of after events. */
  101. } ThreadSpecificData;
  102. static Tcl_ThreadDataKey dataKey;
  103. /*
  104.  * Prototypes for procedures referenced only in this file:
  105.  */
  106. static void AfterCleanupProc _ANSI_ARGS_((ClientData clientData,
  107.     Tcl_Interp *interp));
  108. static void AfterProc _ANSI_ARGS_((ClientData clientData));
  109. static void FreeAfterPtr _ANSI_ARGS_((AfterInfo *afterPtr));
  110. static AfterInfo * GetAfterEvent _ANSI_ARGS_((AfterAssocData *assocPtr,
  111.     Tcl_Obj *commandPtr));
  112. static ThreadSpecificData *InitTimer _ANSI_ARGS_((void));
  113. static void TimerExitProc _ANSI_ARGS_((ClientData clientData));
  114. static int TimerHandlerEventProc _ANSI_ARGS_((Tcl_Event *evPtr,
  115.     int flags));
  116. static void TimerCheckProc _ANSI_ARGS_((ClientData clientData,
  117.     int flags));
  118. static void TimerSetupProc _ANSI_ARGS_((ClientData clientData,
  119.     int flags));
  120. /*
  121.  *----------------------------------------------------------------------
  122.  *
  123.  * InitTimer --
  124.  *
  125.  * This function initializes the timer module.
  126.  *
  127.  * Results:
  128.  * A pointer to the thread specific data.
  129.  *
  130.  * Side effects:
  131.  * Registers the idle and timer event sources.
  132.  *
  133.  *----------------------------------------------------------------------
  134.  */
  135. static ThreadSpecificData *
  136. InitTimer()
  137. {
  138.     ThreadSpecificData *tsdPtr = 
  139. (ThreadSpecificData *) TclThreadDataKeyGet(&dataKey);
  140.     if (tsdPtr == NULL) {
  141. tsdPtr = TCL_TSD_INIT(&dataKey);
  142. Tcl_CreateEventSource(TimerSetupProc, TimerCheckProc, NULL);
  143. Tcl_CreateThreadExitHandler(TimerExitProc, NULL);
  144.     }
  145.     return tsdPtr;
  146. }
  147. /*
  148.  *----------------------------------------------------------------------
  149.  *
  150.  * TimerExitProc --
  151.  *
  152.  * This function is call at exit or unload time to remove the
  153.  * timer and idle event sources.
  154.  *
  155.  * Results:
  156.  * None.
  157.  *
  158.  * Side effects:
  159.  * Removes the timer and idle event sources and remaining events.
  160.  *
  161.  *----------------------------------------------------------------------
  162.  */
  163. static void
  164. TimerExitProc(clientData)
  165.     ClientData clientData; /* Not used. */
  166. {
  167.     ThreadSpecificData *tsdPtr =
  168. (ThreadSpecificData *) TclThreadDataKeyGet(&dataKey);
  169.     Tcl_DeleteEventSource(TimerSetupProc, TimerCheckProc, NULL);
  170.     if (tsdPtr != NULL) {
  171. register TimerHandler *timerHandlerPtr;
  172. timerHandlerPtr = tsdPtr->firstTimerHandlerPtr;
  173. while (timerHandlerPtr != NULL) {
  174.     tsdPtr->firstTimerHandlerPtr = timerHandlerPtr->nextPtr;
  175.     ckfree((char *) timerHandlerPtr);
  176.     timerHandlerPtr = tsdPtr->firstTimerHandlerPtr;
  177. }
  178.     }
  179. }
  180. /*
  181.  *--------------------------------------------------------------
  182.  *
  183.  * Tcl_CreateTimerHandler --
  184.  *
  185.  * Arrange for a given procedure to be invoked at a particular
  186.  * time in the future.
  187.  *
  188.  * Results:
  189.  * The return value is a token for the timer event, which
  190.  * may be used to delete the event before it fires.
  191.  *
  192.  * Side effects:
  193.  * When milliseconds have elapsed, proc will be invoked
  194.  * exactly once.
  195.  *
  196.  *--------------------------------------------------------------
  197.  */
  198. Tcl_TimerToken
  199. Tcl_CreateTimerHandler(milliseconds, proc, clientData)
  200.     int milliseconds; /* How many milliseconds to wait
  201.  * before invoking proc. */
  202.     Tcl_TimerProc *proc; /* Procedure to invoke. */
  203.     ClientData clientData; /* Arbitrary data to pass to proc. */
  204. {
  205.     register TimerHandler *timerHandlerPtr, *tPtr2, *prevPtr;
  206.     Tcl_Time time;
  207.     ThreadSpecificData *tsdPtr;
  208.     tsdPtr = InitTimer();
  209.     timerHandlerPtr = (TimerHandler *) ckalloc(sizeof(TimerHandler));
  210.     /*
  211.      * Compute when the event should fire.
  212.      */
  213.     Tcl_GetTime(&time);
  214.     timerHandlerPtr->time.sec = time.sec + milliseconds/1000;
  215.     timerHandlerPtr->time.usec = time.usec + (milliseconds%1000)*1000;
  216.     if (timerHandlerPtr->time.usec >= 1000000) {
  217. timerHandlerPtr->time.usec -= 1000000;
  218. timerHandlerPtr->time.sec += 1;
  219.     }
  220.     /*
  221.      * Fill in other fields for the event.
  222.      */
  223.     timerHandlerPtr->proc = proc;
  224.     timerHandlerPtr->clientData = clientData;
  225.     tsdPtr->lastTimerId++;
  226.     timerHandlerPtr->token = (Tcl_TimerToken) tsdPtr->lastTimerId;
  227.     /*
  228.      * Add the event to the queue in the correct position
  229.      * (ordered by event firing time).
  230.      */
  231.     for (tPtr2 = tsdPtr->firstTimerHandlerPtr, prevPtr = NULL; tPtr2 != NULL;
  232.     prevPtr = tPtr2, tPtr2 = tPtr2->nextPtr) {
  233. if ((tPtr2->time.sec > timerHandlerPtr->time.sec)
  234. || ((tPtr2->time.sec == timerHandlerPtr->time.sec)
  235. && (tPtr2->time.usec > timerHandlerPtr->time.usec))) {
  236.     break;
  237. }
  238.     }
  239.     timerHandlerPtr->nextPtr = tPtr2;
  240.     if (prevPtr == NULL) {
  241. tsdPtr->firstTimerHandlerPtr = timerHandlerPtr;
  242.     } else {
  243. prevPtr->nextPtr = timerHandlerPtr;
  244.     }
  245.     TimerSetupProc(NULL, TCL_ALL_EVENTS);
  246.     return timerHandlerPtr->token;
  247. }
  248. /*
  249.  *--------------------------------------------------------------
  250.  *
  251.  * Tcl_DeleteTimerHandler --
  252.  *
  253.  * Delete a previously-registered timer handler.
  254.  *
  255.  * Results:
  256.  * None.
  257.  *
  258.  * Side effects:
  259.  * Destroy the timer callback identified by TimerToken,
  260.  * so that its associated procedure will not be called.
  261.  * If the callback has already fired, or if the given
  262.  * token doesn't exist, then nothing happens.
  263.  *
  264.  *--------------------------------------------------------------
  265.  */
  266. void
  267. Tcl_DeleteTimerHandler(token)
  268.     Tcl_TimerToken token; /* Result previously returned by
  269.  * Tcl_DeleteTimerHandler. */
  270. {
  271.     register TimerHandler *timerHandlerPtr, *prevPtr;
  272.     ThreadSpecificData *tsdPtr = InitTimer();
  273.     if (token == NULL) {
  274. return;
  275.     }
  276.     for (timerHandlerPtr = tsdPtr->firstTimerHandlerPtr, prevPtr = NULL;
  277.     timerHandlerPtr != NULL; prevPtr = timerHandlerPtr,
  278.     timerHandlerPtr = timerHandlerPtr->nextPtr) {
  279. if (timerHandlerPtr->token != token) {
  280.     continue;
  281. }
  282. if (prevPtr == NULL) {
  283.     tsdPtr->firstTimerHandlerPtr = timerHandlerPtr->nextPtr;
  284. } else {
  285.     prevPtr->nextPtr = timerHandlerPtr->nextPtr;
  286. }
  287. ckfree((char *) timerHandlerPtr);
  288. return;
  289.     }
  290. }
  291. /*
  292.  *----------------------------------------------------------------------
  293.  *
  294.  * TimerSetupProc --
  295.  *
  296.  * This function is called by Tcl_DoOneEvent to setup the timer
  297.  * event source for before blocking.  This routine checks both the
  298.  * idle and after timer lists.
  299.  *
  300.  * Results:
  301.  * None.
  302.  *
  303.  * Side effects:
  304.  * May update the maximum notifier block time.
  305.  *
  306.  *----------------------------------------------------------------------
  307.  */
  308. static void
  309. TimerSetupProc(data, flags)
  310.     ClientData data; /* Not used. */
  311.     int flags; /* Event flags as passed to Tcl_DoOneEvent. */
  312. {
  313.     Tcl_Time blockTime;
  314.     ThreadSpecificData *tsdPtr = InitTimer();
  315.     if (((flags & TCL_IDLE_EVENTS) && tsdPtr->idleList)
  316.     || ((flags & TCL_TIMER_EVENTS) && tsdPtr->timerPending)) {
  317. /*
  318.  * There is an idle handler or a pending timer event, so just poll.
  319.  */
  320. blockTime.sec = 0;
  321. blockTime.usec = 0;
  322.     } else if ((flags & TCL_TIMER_EVENTS) && tsdPtr->firstTimerHandlerPtr) {
  323. /*
  324.  * Compute the timeout for the next timer on the list.
  325.  */
  326. Tcl_GetTime(&blockTime);
  327. blockTime.sec = tsdPtr->firstTimerHandlerPtr->time.sec - blockTime.sec;
  328. blockTime.usec = tsdPtr->firstTimerHandlerPtr->time.usec -
  329. blockTime.usec;
  330. if (blockTime.usec < 0) {
  331.     blockTime.sec -= 1;
  332.     blockTime.usec += 1000000;
  333. }
  334. if (blockTime.sec < 0) {
  335.     blockTime.sec = 0;
  336.     blockTime.usec = 0;
  337. }
  338.     } else {
  339. return;
  340.     }
  341.     Tcl_SetMaxBlockTime(&blockTime);
  342. }
  343. /*
  344.  *----------------------------------------------------------------------
  345.  *
  346.  * TimerCheckProc --
  347.  *
  348.  * This function is called by Tcl_DoOneEvent to check the timer
  349.  * event source for events.  This routine checks both the
  350.  * idle and after timer lists.
  351.  *
  352.  * Results:
  353.  * None.
  354.  *
  355.  * Side effects:
  356.  * May queue an event and update the maximum notifier block time.
  357.  *
  358.  *----------------------------------------------------------------------
  359.  */
  360. static void
  361. TimerCheckProc(data, flags)
  362.     ClientData data; /* Not used. */
  363.     int flags; /* Event flags as passed to Tcl_DoOneEvent. */
  364. {
  365.     Tcl_Event *timerEvPtr;
  366.     Tcl_Time blockTime;
  367.     ThreadSpecificData *tsdPtr = InitTimer();
  368.     if ((flags & TCL_TIMER_EVENTS) && tsdPtr->firstTimerHandlerPtr) {
  369. /*
  370.  * Compute the timeout for the next timer on the list.
  371.  */
  372. Tcl_GetTime(&blockTime);
  373. blockTime.sec = tsdPtr->firstTimerHandlerPtr->time.sec - blockTime.sec;
  374. blockTime.usec = tsdPtr->firstTimerHandlerPtr->time.usec -
  375. blockTime.usec;
  376. if (blockTime.usec < 0) {
  377.     blockTime.sec -= 1;
  378.     blockTime.usec += 1000000;
  379. }
  380. if (blockTime.sec < 0) {
  381.     blockTime.sec = 0;
  382.     blockTime.usec = 0;
  383. }
  384. /*
  385.  * If the first timer has expired, stick an event on the queue.
  386.  */
  387. if (blockTime.sec == 0 && blockTime.usec == 0 &&
  388. !tsdPtr->timerPending) {
  389.     tsdPtr->timerPending = 1;
  390.     timerEvPtr = (Tcl_Event *) ckalloc(sizeof(Tcl_Event));
  391.     timerEvPtr->proc = TimerHandlerEventProc;
  392.     Tcl_QueueEvent(timerEvPtr, TCL_QUEUE_TAIL);
  393. }
  394.     }
  395. }
  396. /*
  397.  *----------------------------------------------------------------------
  398.  *
  399.  * TimerHandlerEventProc --
  400.  *
  401.  * This procedure is called by Tcl_ServiceEvent when a timer event
  402.  * reaches the front of the event queue.  This procedure handles
  403.  * the event by invoking the callbacks for all timers that are
  404.  * ready.
  405.  *
  406.  * Results:
  407.  * Returns 1 if the event was handled, meaning it should be removed
  408.  * from the queue.  Returns 0 if the event was not handled, meaning
  409.  * it should stay on the queue.  The only time the event isn't
  410.  * handled is if the TCL_TIMER_EVENTS flag bit isn't set.
  411.  *
  412.  * Side effects:
  413.  * Whatever the timer handler callback procedures do.
  414.  *
  415.  *----------------------------------------------------------------------
  416.  */
  417. static int
  418. TimerHandlerEventProc(evPtr, flags)
  419.     Tcl_Event *evPtr; /* Event to service. */
  420.     int flags; /* Flags that indicate what events to
  421.  * handle, such as TCL_FILE_EVENTS. */
  422. {
  423.     TimerHandler *timerHandlerPtr, **nextPtrPtr;
  424.     Tcl_Time time;
  425.     int currentTimerId;
  426.     ThreadSpecificData *tsdPtr = InitTimer();
  427.     /*
  428.      * Do nothing if timers aren't enabled.  This leaves the event on the
  429.      * queue, so we will get to it as soon as ServiceEvents() is called
  430.      * with timers enabled.
  431.      */
  432.     if (!(flags & TCL_TIMER_EVENTS)) {
  433. return 0;
  434.     }
  435.     /*
  436.      * The code below is trickier than it may look, for the following
  437.      * reasons:
  438.      *
  439.      * 1. New handlers can get added to the list while the current
  440.      *    one is being processed.  If new ones get added, we don't
  441.      *    want to process them during this pass through the list to avoid
  442.      *   starving other event sources.  This is implemented using the
  443.      *   token number in the handler:  new handlers will have a
  444.      *    newer token than any of the ones currently on the list.
  445.      * 2. The handler can call Tcl_DoOneEvent, so we have to remove
  446.      *    the handler from the list before calling it. Otherwise an
  447.      *    infinite loop could result.
  448.      * 3. Tcl_DeleteTimerHandler can be called to remove an element from
  449.      *    the list while a handler is executing, so the list could
  450.      *    change structure during the call.
  451.      * 4. Because we only fetch the current time before entering the loop,
  452.      *    the only way a new timer will even be considered runnable is if
  453.      *   its expiration time is within the same millisecond as the
  454.      *   current time.  This is fairly likely on Windows, since it has
  455.      *   a course granularity clock.  Since timers are placed
  456.      *   on the queue in time order with the most recently created
  457.      *    handler appearing after earlier ones with the same expiration
  458.      *   time, we don't have to worry about newer generation timers
  459.      *   appearing before later ones.
  460.      */
  461.     tsdPtr->timerPending = 0;
  462.     currentTimerId = tsdPtr->lastTimerId;
  463.     Tcl_GetTime(&time);
  464.     while (1) {
  465. nextPtrPtr = &tsdPtr->firstTimerHandlerPtr;
  466. timerHandlerPtr = tsdPtr->firstTimerHandlerPtr;
  467. if (timerHandlerPtr == NULL) {
  468.     break;
  469. }
  470.     
  471. if ((timerHandlerPtr->time.sec > time.sec)
  472. || ((timerHandlerPtr->time.sec == time.sec)
  473. && (timerHandlerPtr->time.usec > time.usec))) {
  474.     break;
  475. }
  476. /*
  477.  * Bail out if the next timer is of a newer generation.
  478.  */
  479. if ((currentTimerId - (int)timerHandlerPtr->token) < 0) {
  480.     break;
  481. }
  482. /*
  483.  * Remove the handler from the queue before invoking it,
  484.  * to avoid potential reentrancy problems.
  485.  */
  486. (*nextPtrPtr) = timerHandlerPtr->nextPtr;
  487. (*timerHandlerPtr->proc)(timerHandlerPtr->clientData);
  488. ckfree((char *) timerHandlerPtr);
  489.     }
  490.     TimerSetupProc(NULL, TCL_TIMER_EVENTS);
  491.     return 1;
  492. }
  493. /*
  494.  *--------------------------------------------------------------
  495.  *
  496.  * Tcl_DoWhenIdle --
  497.  *
  498.  * Arrange for proc to be invoked the next time the system is
  499.  * idle (i.e., just before the next time that Tcl_DoOneEvent
  500.  * would have to wait for something to happen).
  501.  *
  502.  * Results:
  503.  * None.
  504.  *
  505.  * Side effects:
  506.  * Proc will eventually be called, with clientData as argument.
  507.  * See the manual entry for details.
  508.  *
  509.  *--------------------------------------------------------------
  510.  */
  511. void
  512. Tcl_DoWhenIdle(proc, clientData)
  513.     Tcl_IdleProc *proc; /* Procedure to invoke. */
  514.     ClientData clientData; /* Arbitrary value to pass to proc. */
  515. {
  516.     register IdleHandler *idlePtr;
  517.     Tcl_Time blockTime;
  518.     ThreadSpecificData *tsdPtr = InitTimer();
  519.     idlePtr = (IdleHandler *) ckalloc(sizeof(IdleHandler));
  520.     idlePtr->proc = proc;
  521.     idlePtr->clientData = clientData;
  522.     idlePtr->generation = tsdPtr->idleGeneration;
  523.     idlePtr->nextPtr = NULL;
  524.     if (tsdPtr->lastIdlePtr == NULL) {
  525. tsdPtr->idleList = idlePtr;
  526.     } else {
  527. tsdPtr->lastIdlePtr->nextPtr = idlePtr;
  528.     }
  529.     tsdPtr->lastIdlePtr = idlePtr;
  530.     blockTime.sec = 0;
  531.     blockTime.usec = 0;
  532.     Tcl_SetMaxBlockTime(&blockTime);
  533. }
  534. /*
  535.  *----------------------------------------------------------------------
  536.  *
  537.  * Tcl_CancelIdleCall --
  538.  *
  539.  * If there are any when-idle calls requested to a given procedure
  540.  * with given clientData, cancel all of them.
  541.  *
  542.  * Results:
  543.  * None.
  544.  *
  545.  * Side effects:
  546.  * If the proc/clientData combination were on the when-idle list,
  547.  * they are removed so that they will never be called.
  548.  *
  549.  *----------------------------------------------------------------------
  550.  */
  551. void
  552. Tcl_CancelIdleCall(proc, clientData)
  553.     Tcl_IdleProc *proc; /* Procedure that was previously registered. */
  554.     ClientData clientData; /* Arbitrary value to pass to proc. */
  555. {
  556.     register IdleHandler *idlePtr, *prevPtr;
  557.     IdleHandler *nextPtr;
  558.     ThreadSpecificData *tsdPtr = InitTimer();
  559.     for (prevPtr = NULL, idlePtr = tsdPtr->idleList; idlePtr != NULL;
  560.     prevPtr = idlePtr, idlePtr = idlePtr->nextPtr) {
  561. while ((idlePtr->proc == proc)
  562. && (idlePtr->clientData == clientData)) {
  563.     nextPtr = idlePtr->nextPtr;
  564.     ckfree((char *) idlePtr);
  565.     idlePtr = nextPtr;
  566.     if (prevPtr == NULL) {
  567. tsdPtr->idleList = idlePtr;
  568.     } else {
  569. prevPtr->nextPtr = idlePtr;
  570.     }
  571.     if (idlePtr == NULL) {
  572. tsdPtr->lastIdlePtr = prevPtr;
  573. return;
  574.     }
  575. }
  576.     }
  577. }
  578. /*
  579.  *----------------------------------------------------------------------
  580.  *
  581.  * TclServiceIdle --
  582.  *
  583.  * This procedure is invoked by the notifier when it becomes
  584.  * idle.  It will invoke all idle handlers that are present at
  585.  * the time the call is invoked, but not those added during idle
  586.  * processing.
  587.  *
  588.  * Results:
  589.  * The return value is 1 if TclServiceIdle found something to
  590.  * do, otherwise return value is 0.
  591.  *
  592.  * Side effects:
  593.  * Invokes all pending idle handlers.
  594.  *
  595.  *----------------------------------------------------------------------
  596.  */
  597. int
  598. TclServiceIdle()
  599. {
  600.     IdleHandler *idlePtr;
  601.     int oldGeneration;
  602.     Tcl_Time blockTime;
  603.     ThreadSpecificData *tsdPtr = InitTimer();
  604.     if (tsdPtr->idleList == NULL) {
  605. return 0;
  606.     }
  607.     oldGeneration = tsdPtr->idleGeneration;
  608.     tsdPtr->idleGeneration++;
  609.     /*
  610.      * The code below is trickier than it may look, for the following
  611.      * reasons:
  612.      *
  613.      * 1. New handlers can get added to the list while the current
  614.      *    one is being processed.  If new ones get added, we don't
  615.      *    want to process them during this pass through the list (want
  616.      *    to check for other work to do first).  This is implemented
  617.      *    using the generation number in the handler:  new handlers
  618.      *    will have a different generation than any of the ones currently
  619.      *    on the list.
  620.      * 2. The handler can call Tcl_DoOneEvent, so we have to remove
  621.      *    the handler from the list before calling it. Otherwise an
  622.      *    infinite loop could result.
  623.      * 3. Tcl_CancelIdleCall can be called to remove an element from
  624.      *    the list while a handler is executing, so the list could
  625.      *    change structure during the call.
  626.      */
  627.     for (idlePtr = tsdPtr->idleList;
  628.     ((idlePtr != NULL)
  629.     && ((oldGeneration - idlePtr->generation) >= 0));
  630.     idlePtr = tsdPtr->idleList) {
  631. tsdPtr->idleList = idlePtr->nextPtr;
  632. if (tsdPtr->idleList == NULL) {
  633.     tsdPtr->lastIdlePtr = NULL;
  634. }
  635. (*idlePtr->proc)(idlePtr->clientData);
  636. ckfree((char *) idlePtr);
  637.     }
  638.     if (tsdPtr->idleList) {
  639. blockTime.sec = 0;
  640. blockTime.usec = 0;
  641. Tcl_SetMaxBlockTime(&blockTime);
  642.     }
  643.     return 1;
  644. }
  645. /*
  646.  *----------------------------------------------------------------------
  647.  *
  648.  * Tcl_AfterObjCmd --
  649.  *
  650.  * This procedure is invoked to process the "after" Tcl command.
  651.  * See the user documentation for details on what it does.
  652.  *
  653.  * Results:
  654.  * A standard Tcl result.
  655.  *
  656.  * Side effects:
  657.  * See the user documentation.
  658.  *
  659.  *----------------------------------------------------------------------
  660.  */
  661. /* ARGSUSED */
  662. int
  663. Tcl_AfterObjCmd(clientData, interp, objc, objv)
  664.     ClientData clientData; /* Unused */
  665.     Tcl_Interp *interp; /* Current interpreter. */
  666.     int objc; /* Number of arguments. */
  667.     Tcl_Obj *CONST objv[]; /* Argument objects. */
  668. {
  669.     int ms;
  670.     AfterInfo *afterPtr;
  671.     AfterAssocData *assocPtr;
  672.     int length;
  673.     char *argString;
  674.     int index;
  675.     char buf[16 + TCL_INTEGER_SPACE];
  676.     static CONST char *afterSubCmds[] = {
  677. "cancel", "idle", "info", (char *) NULL
  678.     };
  679.     enum afterSubCmds {AFTER_CANCEL, AFTER_IDLE, AFTER_INFO};
  680.     ThreadSpecificData *tsdPtr = InitTimer();
  681.     if (objc < 2) {
  682. Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?");
  683. return TCL_ERROR;
  684.     }
  685.     /*
  686.      * Create the "after" information associated for this interpreter,
  687.      * if it doesn't already exist.  
  688.      */
  689.     assocPtr = Tcl_GetAssocData( interp, "tclAfter", NULL );
  690.     if (assocPtr == NULL) {
  691. assocPtr = (AfterAssocData *) ckalloc(sizeof(AfterAssocData));
  692. assocPtr->interp = interp;
  693. assocPtr->firstAfterPtr = NULL;
  694. Tcl_SetAssocData(interp, "tclAfter", AfterCleanupProc,
  695. (ClientData) assocPtr);
  696.     }
  697.     /*
  698.      * First lets see if the command was passed a number as the first argument.
  699.      */
  700.     if (objv[1]->typePtr == &tclIntType) {
  701. ms = (int) objv[1]->internalRep.longValue;
  702. goto processInteger;
  703.     }
  704.     argString = Tcl_GetStringFromObj(objv[1], &length);
  705.     if (argString[0] == '+' || argString[0] == '-'
  706. || isdigit(UCHAR(argString[0]))) { /* INTL: digit */
  707. if (Tcl_GetIntFromObj(interp, objv[1], &ms) != TCL_OK) {
  708.     return TCL_ERROR;
  709. }
  710. processInteger:
  711. if (ms < 0) {
  712.     ms = 0;
  713. }
  714. if (objc == 2) {
  715.     Tcl_Sleep(ms);
  716.     return TCL_OK;
  717. }
  718. afterPtr = (AfterInfo *) ckalloc((unsigned) (sizeof(AfterInfo)));
  719. afterPtr->assocPtr = assocPtr;
  720. if (objc == 3) {
  721.     afterPtr->commandPtr = objv[2];
  722. } else {
  723.       afterPtr->commandPtr = Tcl_ConcatObj(objc-2, objv+2);
  724. }
  725. Tcl_IncrRefCount(afterPtr->commandPtr);
  726. /*
  727.  * The variable below is used to generate unique identifiers for
  728.  * after commands.  This id can wrap around, which can potentially
  729.  * cause problems.  However, there are not likely to be problems
  730.  * in practice, because after commands can only be requested to
  731.  * about a month in the future, and wrap-around is unlikely to
  732.  * occur in less than about 1-10 years.  Thus it's unlikely that
  733.  * any old ids will still be around when wrap-around occurs.
  734.  */
  735. afterPtr->id = tsdPtr->afterId;
  736. tsdPtr->afterId += 1;
  737. afterPtr->token = Tcl_CreateTimerHandler(ms, AfterProc,
  738. (ClientData) afterPtr);
  739. afterPtr->nextPtr = assocPtr->firstAfterPtr;
  740. assocPtr->firstAfterPtr = afterPtr;
  741. sprintf(buf, "after#%d", afterPtr->id);
  742. Tcl_AppendResult(interp, buf, (char *) NULL);
  743. return TCL_OK;
  744.     }
  745.     /*
  746.      * If it's not a number it must be a subcommand.
  747.      */
  748.     if (Tcl_GetIndexFromObj(NULL, objv[1], afterSubCmds, "argument",
  749.             0, &index) != TCL_OK) {
  750. Tcl_AppendResult(interp, "bad argument "", argString,
  751. "": must be cancel, idle, info, or a number",
  752. (char *) NULL);
  753. return TCL_ERROR;
  754.     }
  755.     switch ((enum afterSubCmds) index) {
  756.         case AFTER_CANCEL: {
  757.     Tcl_Obj *commandPtr;
  758.     char *command, *tempCommand;
  759.     int tempLength;
  760.     if (objc < 3) {
  761. Tcl_WrongNumArgs(interp, 2, objv, "id|command");
  762. return TCL_ERROR;
  763.     }
  764.     if (objc == 3) {
  765. commandPtr = objv[2];
  766.     } else {
  767. commandPtr = Tcl_ConcatObj(objc-2, objv+2);;
  768.     }
  769.     command = Tcl_GetStringFromObj(commandPtr, &length);
  770.     for (afterPtr = assocPtr->firstAfterPtr;  afterPtr != NULL;
  771.     afterPtr = afterPtr->nextPtr) {
  772. tempCommand = Tcl_GetStringFromObj(afterPtr->commandPtr,
  773. &tempLength);
  774. if ((length == tempLength)
  775.         && (memcmp((void*) command, (void*) tempCommand,
  776.         (unsigned) length) == 0)) {
  777.     break;
  778. }
  779.     }
  780.     if (afterPtr == NULL) {
  781. afterPtr = GetAfterEvent(assocPtr, commandPtr);
  782.     }
  783.     if (objc != 3) {
  784. Tcl_DecrRefCount(commandPtr);
  785.     }
  786.     if (afterPtr != NULL) {
  787. if (afterPtr->token != NULL) {
  788.     Tcl_DeleteTimerHandler(afterPtr->token);
  789. } else {
  790.     Tcl_CancelIdleCall(AfterProc, (ClientData) afterPtr);
  791. }
  792. FreeAfterPtr(afterPtr);
  793.     }
  794.     break;
  795. }
  796. case AFTER_IDLE:
  797.     if (objc < 3) {
  798. Tcl_WrongNumArgs(interp, 2, objv, "script script ...");
  799. return TCL_ERROR;
  800.     }
  801.     afterPtr = (AfterInfo *) ckalloc((unsigned) (sizeof(AfterInfo)));
  802.     afterPtr->assocPtr = assocPtr;
  803.     if (objc == 3) {
  804.   afterPtr->commandPtr = objv[2];
  805.     } else {
  806. afterPtr->commandPtr = Tcl_ConcatObj(objc-2, objv+2);
  807.     }
  808.     Tcl_IncrRefCount(afterPtr->commandPtr);
  809.     afterPtr->id = tsdPtr->afterId;
  810.     tsdPtr->afterId += 1;
  811.     afterPtr->token = NULL;
  812.     afterPtr->nextPtr = assocPtr->firstAfterPtr;
  813.     assocPtr->firstAfterPtr = afterPtr;
  814.     Tcl_DoWhenIdle(AfterProc, (ClientData) afterPtr);
  815.     sprintf(buf, "after#%d", afterPtr->id);
  816.     Tcl_AppendResult(interp, buf, (char *) NULL);
  817.     break;
  818. case AFTER_INFO: {
  819.     Tcl_Obj *resultListPtr;
  820.     if (objc == 2) {
  821. for (afterPtr = assocPtr->firstAfterPtr; afterPtr != NULL;
  822.      afterPtr = afterPtr->nextPtr) {
  823.     if (assocPtr->interp == interp) {
  824. sprintf(buf, "after#%d", afterPtr->id);
  825. Tcl_AppendElement(interp, buf);
  826.     }
  827. }
  828. return TCL_OK;
  829.     }
  830.     if (objc != 3) {
  831. Tcl_WrongNumArgs(interp, 2, objv, "?id?");
  832. return TCL_ERROR;
  833.     }
  834.     afterPtr = GetAfterEvent(assocPtr, objv[2]);
  835.     if (afterPtr == NULL) {
  836. Tcl_AppendResult(interp, "event "", Tcl_GetString(objv[2]),
  837. "" doesn't exist", (char *) NULL);
  838. return TCL_ERROR;
  839.     }
  840.     resultListPtr = Tcl_GetObjResult(interp);
  841.       Tcl_ListObjAppendElement(interp, resultListPtr, afterPtr->commandPtr);
  842.       Tcl_ListObjAppendElement(interp, resultListPtr, Tcl_NewStringObj(
  843.   (afterPtr->token == NULL) ? "idle" : "timer", -1));
  844.     Tcl_SetObjResult(interp, resultListPtr);
  845.     break;
  846. }
  847. default: {
  848.     panic("Tcl_AfterObjCmd: bad subcommand index to afterSubCmds");
  849. }
  850.     }
  851.     return TCL_OK;
  852. }
  853. /*
  854.  *----------------------------------------------------------------------
  855.  *
  856.  * GetAfterEvent --
  857.  *
  858.  * This procedure parses an "after" id such as "after#4" and
  859.  * returns a pointer to the AfterInfo structure.
  860.  *
  861.  * Results:
  862.  * The return value is either a pointer to an AfterInfo structure,
  863.  * if one is found that corresponds to "cmdString" and is for interp,
  864.  * or NULL if no corresponding after event can be found.
  865.  *
  866.  * Side effects:
  867.  * None.
  868.  *
  869.  *----------------------------------------------------------------------
  870.  */
  871. static AfterInfo *
  872. GetAfterEvent(assocPtr, commandPtr)
  873.     AfterAssocData *assocPtr; /* Points to "after"-related information for
  874.  * this interpreter. */
  875.     Tcl_Obj *commandPtr;
  876. {
  877.     char *cmdString; /* Textual identifier for after event, such
  878.  * as "after#6". */
  879.     AfterInfo *afterPtr;
  880.     int id;
  881.     char *end;
  882.     cmdString = Tcl_GetString(commandPtr);
  883.     if (strncmp(cmdString, "after#", 6) != 0) {
  884. return NULL;
  885.     }
  886.     cmdString += 6;
  887.     id = strtoul(cmdString, &end, 10);
  888.     if ((end == cmdString) || (*end != 0)) {
  889. return NULL;
  890.     }
  891.     for (afterPtr = assocPtr->firstAfterPtr; afterPtr != NULL;
  892.     afterPtr = afterPtr->nextPtr) {
  893. if (afterPtr->id == id) {
  894.     return afterPtr;
  895. }
  896.     }
  897.     return NULL;
  898. }
  899. /*
  900.  *----------------------------------------------------------------------
  901.  *
  902.  * AfterProc --
  903.  *
  904.  * Timer callback to execute commands registered with the
  905.  * "after" command.
  906.  *
  907.  * Results:
  908.  * None.
  909.  *
  910.  * Side effects:
  911.  * Executes whatever command was specified.  If the command
  912.  * returns an error, then the command "bgerror" is invoked
  913.  * to process the error;  if bgerror fails then information
  914.  * about the error is output on stderr.
  915.  *
  916.  *----------------------------------------------------------------------
  917.  */
  918. static void
  919. AfterProc(clientData)
  920.     ClientData clientData; /* Describes command to execute. */
  921. {
  922.     AfterInfo *afterPtr = (AfterInfo *) clientData;
  923.     AfterAssocData *assocPtr = afterPtr->assocPtr;
  924.     AfterInfo *prevPtr;
  925.     int result;
  926.     Tcl_Interp *interp;
  927.     char *script;
  928.     int numBytes;
  929.     /*
  930.      * First remove the callback from our list of callbacks;  otherwise
  931.      * someone could delete the callback while it's being executed, which
  932.      * could cause a core dump.
  933.      */
  934.     if (assocPtr->firstAfterPtr == afterPtr) {
  935. assocPtr->firstAfterPtr = afterPtr->nextPtr;
  936.     } else {
  937. for (prevPtr = assocPtr->firstAfterPtr; prevPtr->nextPtr != afterPtr;
  938. prevPtr = prevPtr->nextPtr) {
  939.     /* Empty loop body. */
  940. }
  941. prevPtr->nextPtr = afterPtr->nextPtr;
  942.     }
  943.     /*
  944.      * Execute the callback.
  945.      */
  946.     interp = assocPtr->interp;
  947.     Tcl_Preserve((ClientData) interp);
  948.     script = Tcl_GetStringFromObj(afterPtr->commandPtr, &numBytes);
  949.     result = Tcl_EvalEx(interp, script, numBytes, TCL_EVAL_GLOBAL);
  950.     if (result != TCL_OK) {
  951. Tcl_AddErrorInfo(interp, "n    ("after" script)");
  952. Tcl_BackgroundError(interp);
  953.     }
  954.     Tcl_Release((ClientData) interp);
  955.     
  956.     /*
  957.      * Free the memory for the callback.
  958.      */
  959.     Tcl_DecrRefCount(afterPtr->commandPtr);
  960.     ckfree((char *) afterPtr);
  961. }
  962. /*
  963.  *----------------------------------------------------------------------
  964.  *
  965.  * FreeAfterPtr --
  966.  *
  967.  * This procedure removes an "after" command from the list of
  968.  * those that are pending and frees its resources.  This procedure
  969.  * does *not* cancel the timer handler;  if that's needed, the
  970.  * caller must do it.
  971.  *
  972.  * Results:
  973.  * None.
  974.  *
  975.  * Side effects:
  976.  * The memory associated with afterPtr is released.
  977.  *
  978.  *----------------------------------------------------------------------
  979.  */
  980. static void
  981. FreeAfterPtr(afterPtr)
  982.     AfterInfo *afterPtr; /* Command to be deleted. */
  983. {
  984.     AfterInfo *prevPtr;
  985.     AfterAssocData *assocPtr = afterPtr->assocPtr;
  986.     if (assocPtr->firstAfterPtr == afterPtr) {
  987. assocPtr->firstAfterPtr = afterPtr->nextPtr;
  988.     } else {
  989. for (prevPtr = assocPtr->firstAfterPtr; prevPtr->nextPtr != afterPtr;
  990. prevPtr = prevPtr->nextPtr) {
  991.     /* Empty loop body. */
  992. }
  993. prevPtr->nextPtr = afterPtr->nextPtr;
  994.     }
  995.     Tcl_DecrRefCount(afterPtr->commandPtr);
  996.     ckfree((char *) afterPtr);
  997. }
  998. /*
  999.  *----------------------------------------------------------------------
  1000.  *
  1001.  * AfterCleanupProc --
  1002.  *
  1003.  * This procedure is invoked whenever an interpreter is deleted
  1004.  * to cleanup the AssocData for "tclAfter".
  1005.  *
  1006.  * Results:
  1007.  * None.
  1008.  *
  1009.  * Side effects:
  1010.  * After commands are removed.
  1011.  *
  1012.  *----------------------------------------------------------------------
  1013.  */
  1014. /* ARGSUSED */
  1015. static void
  1016. AfterCleanupProc(clientData, interp)
  1017.     ClientData clientData; /* Points to AfterAssocData for the
  1018.  * interpreter. */
  1019.     Tcl_Interp *interp; /* Interpreter that is being deleted. */
  1020. {
  1021.     AfterAssocData *assocPtr = (AfterAssocData *) clientData;
  1022.     AfterInfo *afterPtr;
  1023.     while (assocPtr->firstAfterPtr != NULL) {
  1024. afterPtr = assocPtr->firstAfterPtr;
  1025. assocPtr->firstAfterPtr = afterPtr->nextPtr;
  1026. if (afterPtr->token != NULL) {
  1027.     Tcl_DeleteTimerHandler(afterPtr->token);
  1028. } else {
  1029.     Tcl_CancelIdleCall(AfterProc, (ClientData) afterPtr);
  1030. }
  1031. Tcl_DecrRefCount(afterPtr->commandPtr);
  1032. ckfree((char *) afterPtr);
  1033.     }
  1034.     ckfree((char *) assocPtr);
  1035. }