/[dtapublic]/projs/trunk/shared_source/c_tcl_base_7_5_w_mods/tclnotify.c
ViewVC logotype

Diff of /projs/trunk/shared_source/c_tcl_base_7_5_w_mods/tclnotify.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

projs/trunk/shared_source/tcl_base/tclnotify.c revision 42 by dashley, Fri Oct 14 01:50:00 2016 UTC projs/trunk/shared_source/c_tcl_base_7_5_w_mods/tclnotify.c revision 71 by dashley, Sat Nov 5 11:07:06 2016 UTC
# Line 1  Line 1 
 /* $Header: /cvsroot/esrg/sfesrg/esrgpcpj/shared/tcl_base/tclnotify.c,v 1.1.1.1 2001/06/13 04:43:48 dtashley Exp $ */  
   
 /*  
  * tclNotify.c --  
  *  
  *      This file implements the generic portion of the Tcl notifier.  
  *      The notifier is lowest-level part of the event system.  It  
  *      manages an event queue that holds Tcl_Event structures.  The  
  *      platform specific portion of the notifier is defined in the  
  *      tcl*Notify.c files in each platform directory.  
  *  
  * Copyright (c) 1995-1997 Sun Microsystems, Inc.  
  * Copyright (c) 1998 by Scriptics Corporation.  
  *  
  * See the file "license.terms" for information on usage and redistribution  
  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.  
  *  
  * RCS: @(#) $Id: tclnotify.c,v 1.1.1.1 2001/06/13 04:43:48 dtashley Exp $  
  */  
   
 #include "tclInt.h"  
 #include "tclPort.h"  
   
 extern TclStubs tclStubs;  
   
 /*  
  * For each event source (created with Tcl_CreateEventSource) there  
  * is a structure of the following type:  
  */  
   
 typedef struct EventSource {  
     Tcl_EventSetupProc *setupProc;  
     Tcl_EventCheckProc *checkProc;  
     ClientData clientData;  
     struct EventSource *nextPtr;  
 } EventSource;  
   
 /*  
  * The following structure keeps track of the state of the notifier on a  
  * per-thread basis. The first three elements keep track of the event queue.  
  * In addition to the first (next to be serviced) and last events in the queue,  
  * we keep track of a "marker" event.  This provides a simple priority  
  * mechanism whereby events can be inserted at the front of the queue but  
  * behind all other high-priority events already in the queue (this is used for  
  * things like a sequence of Enter and Leave events generated during a grab in  
  * Tk).  These elements are protected by the queueMutex so that any thread  
  * can queue an event on any notifier.  Note that all of the values in this  
  * structure will be initialized to 0.  
  */  
   
 typedef struct ThreadSpecificData {  
     Tcl_Event *firstEventPtr;   /* First pending event, or NULL if none. */  
     Tcl_Event *lastEventPtr;    /* Last pending event, or NULL if none. */  
     Tcl_Event *markerEventPtr;  /* Last high-priority event in queue, or  
                                  * NULL if none. */  
     Tcl_Mutex queueMutex;       /* Mutex to protect access to the previous  
                                  * three fields. */  
     int serviceMode;            /* One of TCL_SERVICE_NONE or  
                                  * TCL_SERVICE_ALL. */  
     int blockTimeSet;           /* 0 means there is no maximum block  
                                  * time:  block forever. */  
     Tcl_Time blockTime;         /* If blockTimeSet is 1, gives the  
                                  * maximum elapsed time for the next block. */  
     int inTraversal;            /* 1 if Tcl_SetMaxBlockTime is being  
                                  * called during an event source traversal. */  
     EventSource *firstEventSourcePtr;  
                                 /* Pointer to first event source in  
                                  * list of event sources for this thread. */  
     Tcl_ThreadId threadId;      /* Thread that owns this notifier instance. */  
     ClientData clientData;      /* Opaque handle for platform specific  
                                  * notifier. */  
     struct ThreadSpecificData *nextPtr;  
                                 /* Next notifier in global list of notifiers.  
                                  * Access is controlled by the listLock global  
                                  * mutex. */  
 } ThreadSpecificData;  
   
 static Tcl_ThreadDataKey dataKey;  
   
 /*  
  * Global list of notifiers.  Access to this list is controlled by the  
  * listLock mutex.  If this becomes a performance bottleneck, this could  
  * be replaced with a hashtable.  
  */  
   
 static ThreadSpecificData *firstNotifierPtr;  
 TCL_DECLARE_MUTEX(listLock)  
   
 /*  
  * Declarations for routines used only in this file.  
  */  
   
 static void             QueueEvent _ANSI_ARGS_((ThreadSpecificData *tsdPtr,  
                             Tcl_Event* evPtr, Tcl_QueuePosition position));  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * TclInitNotifier --  
  *  
  *      Initialize the thread local data structures for the notifier  
  *      subsystem.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      Adds the current thread to the global list of notifiers.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 void  
 TclInitNotifier()  
 {  
     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);  
   
     Tcl_MutexLock(&listLock);  
   
     tsdPtr->threadId = Tcl_GetCurrentThread();  
     tsdPtr->clientData = Tcl_InitNotifier();  
     tsdPtr->nextPtr = firstNotifierPtr;  
     firstNotifierPtr = tsdPtr;  
   
     Tcl_MutexUnlock(&listLock);  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * TclFinalizeNotifier --  
  *  
  *      Finalize the thread local data structures for the notifier  
  *      subsystem.  
  *  
  * Results:  
  *      None.    
  *  
  * Side effects:  
  *      Removes the notifier associated with the current thread from  
  *      the global notifier list.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 void  
 TclFinalizeNotifier()  
 {  
     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);  
     ThreadSpecificData **prevPtrPtr;  
   
     Tcl_MutexLock(&listLock);  
   
     Tcl_FinalizeNotifier(tsdPtr->clientData);  
     Tcl_MutexFinalize(&(tsdPtr->queueMutex));  
     for (prevPtrPtr = &firstNotifierPtr; *prevPtrPtr != NULL;  
          prevPtrPtr = &((*prevPtrPtr)->nextPtr)) {  
         if (*prevPtrPtr == tsdPtr) {  
             *prevPtrPtr = tsdPtr->nextPtr;  
             break;  
         }  
     }  
   
     Tcl_MutexUnlock(&listLock);  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * Tcl_SetNotifier --  
  *  
  *      Install a set of alternate functions for use with the notifier.  
  #      In particular, this can be used to install the Xt-based  
  *      notifier for use with the Browser plugin.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      Overstomps part of the stub vector.  This relies on hooks  
  *      added to the default procedures in case those are called  
  *      directly (i.e., not through the stub table.)  
  *  
  *----------------------------------------------------------------------  
  */  
   
 void  
 Tcl_SetNotifier(notifierProcPtr)  
     Tcl_NotifierProcs *notifierProcPtr;  
 {  
 #if !defined(__WIN32__) && !defined(MAC_TCL) /* UNIX */  
     tclStubs.tcl_CreateFileHandler = notifierProcPtr->createFileHandlerProc;  
     tclStubs.tcl_DeleteFileHandler = notifierProcPtr->deleteFileHandlerProc;  
 #endif  
     tclStubs.tcl_SetTimer = notifierProcPtr->setTimerProc;  
     tclStubs.tcl_WaitForEvent = notifierProcPtr->waitForEventProc;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * Tcl_CreateEventSource --  
  *  
  *      This procedure is invoked to create a new source of events.  
  *      The source is identified by a procedure that gets invoked  
  *      during Tcl_DoOneEvent to check for events on that source  
  *      and queue them.  
  *  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      SetupProc and checkProc will be invoked each time that Tcl_DoOneEvent  
  *      runs out of things to do.  SetupProc will be invoked before  
  *      Tcl_DoOneEvent calls select or whatever else it uses to wait  
  *      for events.  SetupProc typically calls functions like  
  *      Tcl_SetMaxBlockTime to indicate what to wait for.  
  *  
  *      CheckProc is called after select or whatever operation was actually  
  *      used to wait.  It figures out whether anything interesting actually  
  *      happened (e.g. by calling Tcl_AsyncReady), and then calls  
  *      Tcl_QueueEvent to queue any events that are ready.  
  *  
  *      Each of these procedures is passed two arguments, e.g.  
  *              (*checkProc)(ClientData clientData, int flags));  
  *      ClientData is the same as the clientData argument here, and flags  
  *      is a combination of things like TCL_FILE_EVENTS that indicates  
  *      what events are of interest:  setupProc and checkProc use flags  
  *      to figure out whether their events are relevant or not.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 void  
 Tcl_CreateEventSource(setupProc, checkProc, clientData)  
     Tcl_EventSetupProc *setupProc;      /* Procedure to invoke to figure out  
                                          * what to wait for. */  
     Tcl_EventCheckProc *checkProc;      /* Procedure to call after waiting  
                                          * to see what happened. */  
     ClientData clientData;              /* One-word argument to pass to  
                                          * setupProc and checkProc. */  
 {  
     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);  
     EventSource *sourcePtr = (EventSource *) ckalloc(sizeof(EventSource));  
   
     sourcePtr->setupProc = setupProc;  
     sourcePtr->checkProc = checkProc;  
     sourcePtr->clientData = clientData;  
     sourcePtr->nextPtr = tsdPtr->firstEventSourcePtr;  
     tsdPtr->firstEventSourcePtr = sourcePtr;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * Tcl_DeleteEventSource --  
  *  
  *      This procedure is invoked to delete the source of events  
  *      given by proc and clientData.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      The given event source is cancelled, so its procedure will  
  *      never again be called.  If no such source exists, nothing  
  *      happens.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 void  
 Tcl_DeleteEventSource(setupProc, checkProc, clientData)  
     Tcl_EventSetupProc *setupProc;      /* Procedure to invoke to figure out  
                                          * what to wait for. */  
     Tcl_EventCheckProc *checkProc;      /* Procedure to call after waiting  
                                          * to see what happened. */  
     ClientData clientData;              /* One-word argument to pass to  
                                          * setupProc and checkProc. */  
 {  
     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);  
     EventSource *sourcePtr, *prevPtr;  
   
     for (sourcePtr = tsdPtr->firstEventSourcePtr, prevPtr = NULL;  
             sourcePtr != NULL;  
             prevPtr = sourcePtr, sourcePtr = sourcePtr->nextPtr) {  
         if ((sourcePtr->setupProc != setupProc)  
                 || (sourcePtr->checkProc != checkProc)  
                 || (sourcePtr->clientData != clientData)) {  
             continue;  
         }  
         if (prevPtr == NULL) {  
             tsdPtr->firstEventSourcePtr = sourcePtr->nextPtr;  
         } else {  
             prevPtr->nextPtr = sourcePtr->nextPtr;  
         }  
         ckfree((char *) sourcePtr);  
         return;  
     }  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * Tcl_QueueEvent --  
  *  
  *      Queue an event on the event queue associated with the  
  *      current thread.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      None.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 void  
 Tcl_QueueEvent(evPtr, position)  
     Tcl_Event* evPtr;           /* Event to add to queue.  The storage  
                                  * space must have been allocated the caller  
                                  * with malloc (ckalloc), and it becomes  
                                  * the property of the event queue.  It  
                                  * will be freed after the event has been  
                                  * handled. */  
     Tcl_QueuePosition position; /* One of TCL_QUEUE_TAIL, TCL_QUEUE_HEAD,  
                                  * TCL_QUEUE_MARK. */  
 {  
     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);  
     QueueEvent(tsdPtr, evPtr, position);  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * Tcl_ThreadQueueEvent --  
  *  
  *      Queue an event on the specified thread's event queue.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      None.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 void  
 Tcl_ThreadQueueEvent(threadId, evPtr, position)  
     Tcl_ThreadId threadId;      /* Identifier for thread to use. */  
     Tcl_Event* evPtr;           /* Event to add to queue.  The storage  
                                  * space must have been allocated the caller  
                                  * with malloc (ckalloc), and it becomes  
                                  * the property of the event queue.  It  
                                  * will be freed after the event has been  
                                  * handled. */  
     Tcl_QueuePosition position; /* One of TCL_QUEUE_TAIL, TCL_QUEUE_HEAD,  
                                  * TCL_QUEUE_MARK. */  
 {  
     ThreadSpecificData *tsdPtr;  
   
     /*  
      * Find the notifier associated with the specified thread.  
      */  
   
     Tcl_MutexLock(&listLock);  
     for (tsdPtr = firstNotifierPtr; tsdPtr && tsdPtr->threadId != threadId;  
              tsdPtr = tsdPtr->nextPtr) {  
         /* Empty loop body. */  
     }  
   
     /*  
      * Queue the event if there was a notifier associated with the thread.  
      */  
   
     if (tsdPtr) {  
         QueueEvent(tsdPtr, evPtr, position);  
     }  
     Tcl_MutexUnlock(&listLock);  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * QueueEvent --  
  *  
  *      Insert an event into the specified thread's event queue at one  
  *      of three positions: the head, the tail, or before a floating  
  *      marker. Events inserted before the marker will be processed in  
  *      first-in-first-out order, but before any events inserted at  
  *      the tail of the queue.  Events inserted at the head of the  
  *      queue will be processed in last-in-first-out order.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      None.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 static void  
 QueueEvent(tsdPtr, evPtr, position)  
     ThreadSpecificData *tsdPtr; /* Handle to thread local data that indicates  
                                  * which event queue to use. */  
     Tcl_Event* evPtr;           /* Event to add to queue.  The storage  
                                  * space must have been allocated the caller  
                                  * with malloc (ckalloc), and it becomes  
                                  * the property of the event queue.  It  
                                  * will be freed after the event has been  
                                  * handled. */  
     Tcl_QueuePosition position; /* One of TCL_QUEUE_TAIL, TCL_QUEUE_HEAD,  
                                  * TCL_QUEUE_MARK. */  
 {  
     Tcl_MutexLock(&(tsdPtr->queueMutex));  
     if (position == TCL_QUEUE_TAIL) {  
         /*  
          * Append the event on the end of the queue.  
          */  
   
         evPtr->nextPtr = NULL;  
         if (tsdPtr->firstEventPtr == NULL) {  
             tsdPtr->firstEventPtr = evPtr;  
         } else {  
             tsdPtr->lastEventPtr->nextPtr = evPtr;  
         }  
         tsdPtr->lastEventPtr = evPtr;  
     } else if (position == TCL_QUEUE_HEAD) {  
         /*  
          * Push the event on the head of the queue.  
          */  
   
         evPtr->nextPtr = tsdPtr->firstEventPtr;  
         if (tsdPtr->firstEventPtr == NULL) {  
             tsdPtr->lastEventPtr = evPtr;  
         }            
         tsdPtr->firstEventPtr = evPtr;  
     } else if (position == TCL_QUEUE_MARK) {  
         /*  
          * Insert the event after the current marker event and advance  
          * the marker to the new event.  
          */  
   
         if (tsdPtr->markerEventPtr == NULL) {  
             evPtr->nextPtr = tsdPtr->firstEventPtr;  
             tsdPtr->firstEventPtr = evPtr;  
         } else {  
             evPtr->nextPtr = tsdPtr->markerEventPtr->nextPtr;  
             tsdPtr->markerEventPtr->nextPtr = evPtr;  
         }  
         tsdPtr->markerEventPtr = evPtr;  
         if (evPtr->nextPtr == NULL) {  
             tsdPtr->lastEventPtr = evPtr;  
         }  
     }  
     Tcl_MutexUnlock(&(tsdPtr->queueMutex));  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * Tcl_DeleteEvents --  
  *  
  *      Calls a procedure for each event in the queue and deletes those  
  *      for which the procedure returns 1. Events for which the  
  *      procedure returns 0 are left in the queue.  Operates on the  
  *      queue associated with the current thread.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      Potentially removes one or more events from the event queue.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 void  
 Tcl_DeleteEvents(proc, clientData)  
     Tcl_EventDeleteProc *proc;          /* The procedure to call. */  
     ClientData clientData;              /* type-specific data. */  
 {  
     Tcl_Event *evPtr, *prevPtr, *hold;  
     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);  
   
     Tcl_MutexLock(&(tsdPtr->queueMutex));  
     for (prevPtr = (Tcl_Event *) NULL, evPtr = tsdPtr->firstEventPtr;  
              evPtr != (Tcl_Event *) NULL;  
              ) {  
         if ((*proc) (evPtr, clientData) == 1) {  
             if (tsdPtr->firstEventPtr == evPtr) {  
                 tsdPtr->firstEventPtr = evPtr->nextPtr;  
                 if (evPtr->nextPtr == (Tcl_Event *) NULL) {  
                     tsdPtr->lastEventPtr = prevPtr;  
                 }  
                 if (tsdPtr->markerEventPtr == evPtr) {  
                     tsdPtr->markerEventPtr = prevPtr;  
                 }  
             } else {  
                 prevPtr->nextPtr = evPtr->nextPtr;  
             }  
             hold = evPtr;  
             evPtr = evPtr->nextPtr;  
             ckfree((char *) hold);  
         } else {  
             prevPtr = evPtr;  
             evPtr = evPtr->nextPtr;  
         }  
     }  
     Tcl_MutexUnlock(&(tsdPtr->queueMutex));  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * Tcl_ServiceEvent --  
  *  
  *      Process one event from the event queue, or invoke an  
  *      asynchronous event handler.  Operates on event queue for  
  *      current thread.  
  *  
  * Results:  
  *      The return value is 1 if the procedure actually found an event  
  *      to process.  If no processing occurred, then 0 is returned.  
  *  
  * Side effects:  
  *      Invokes all of the event handlers for the highest priority  
  *      event in the event queue.  May collapse some events into a  
  *      single event or discard stale events.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 int  
 Tcl_ServiceEvent(flags)  
     int flags;                  /* Indicates what events should be processed.  
                                  * May be any combination of TCL_WINDOW_EVENTS  
                                  * TCL_FILE_EVENTS, TCL_TIMER_EVENTS, or other  
                                  * flags defined elsewhere.  Events not  
                                  * matching this will be skipped for processing  
                                  * later. */  
 {  
     Tcl_Event *evPtr, *prevPtr;  
     Tcl_EventProc *proc;  
     int result;  
     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);  
   
     /*  
      * Asynchronous event handlers are considered to be the highest  
      * priority events, and so must be invoked before we process events  
      * on the event queue.  
      */  
       
     if (Tcl_AsyncReady()) {  
         (void) Tcl_AsyncInvoke((Tcl_Interp *) NULL, 0);  
         return 1;  
     }  
   
     /*  
      * No event flags is equivalent to TCL_ALL_EVENTS.  
      */  
       
     if ((flags & TCL_ALL_EVENTS) == 0) {  
         flags |= TCL_ALL_EVENTS;  
     }  
   
     /*  
      * Loop through all the events in the queue until we find one  
      * that can actually be handled.  
      */  
   
     Tcl_MutexLock(&(tsdPtr->queueMutex));  
     for (evPtr = tsdPtr->firstEventPtr; evPtr != NULL;  
          evPtr = evPtr->nextPtr) {  
         /*  
          * Call the handler for the event.  If it actually handles the  
          * event then free the storage for the event.  There are two  
          * tricky things here, both stemming from the fact that the event  
          * code may be re-entered while servicing the event:  
          *  
          * 1. Set the "proc" field to NULL.  This is a signal to ourselves  
          *    that we shouldn't reexecute the handler if the event loop  
          *    is re-entered.  
          * 2. When freeing the event, must search the queue again from the  
          *    front to find it.  This is because the event queue could  
          *    change almost arbitrarily while handling the event, so we  
          *    can't depend on pointers found now still being valid when  
          *    the handler returns.  
          */  
   
         proc = evPtr->proc;  
         if (proc == NULL) {  
             continue;  
         }  
         evPtr->proc = NULL;  
   
         /*  
          * Release the lock before calling the event procedure.  This  
          * allows other threads to post events if we enter a recursive  
          * event loop in this thread.  Note that we are making the assumption  
          * that if the proc returns 0, the event is still in the list.  
          */  
   
         Tcl_MutexUnlock(&(tsdPtr->queueMutex));  
         result = (*proc)(evPtr, flags);  
         Tcl_MutexLock(&(tsdPtr->queueMutex));  
   
         if (result) {  
             /*  
              * The event was processed, so remove it from the queue.  
              */  
   
             if (tsdPtr->firstEventPtr == evPtr) {  
                 tsdPtr->firstEventPtr = evPtr->nextPtr;  
                 if (evPtr->nextPtr == NULL) {  
                     tsdPtr->lastEventPtr = NULL;  
                 }  
                 if (tsdPtr->markerEventPtr == evPtr) {  
                     tsdPtr->markerEventPtr = NULL;  
                 }  
             } else {  
                 for (prevPtr = tsdPtr->firstEventPtr;  
                      prevPtr && prevPtr->nextPtr != evPtr;  
                      prevPtr = prevPtr->nextPtr) {  
                     /* Empty loop body. */  
                 }  
                 if (prevPtr) {  
                     prevPtr->nextPtr = evPtr->nextPtr;  
                     if (evPtr->nextPtr == NULL) {  
                         tsdPtr->lastEventPtr = prevPtr;  
                     }  
                     if (tsdPtr->markerEventPtr == evPtr) {  
                         tsdPtr->markerEventPtr = prevPtr;  
                     }  
                 } else {  
                     evPtr = NULL;  
                 }  
             }  
             if (evPtr) {  
                 ckfree((char *) evPtr);  
             }  
             Tcl_MutexUnlock(&(tsdPtr->queueMutex));  
             return 1;  
         } else {  
             /*  
              * The event wasn't actually handled, so we have to restore  
              * the proc field to allow the event to be attempted again.  
              */  
   
             evPtr->proc = proc;  
         }  
     }  
     Tcl_MutexUnlock(&(tsdPtr->queueMutex));  
     return 0;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * Tcl_GetServiceMode --  
  *  
  *      This routine returns the current service mode of the notifier.  
  *  
  * Results:  
  *      Returns either TCL_SERVICE_ALL or TCL_SERVICE_NONE.  
  *  
  * Side effects:  
  *      None.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 int  
 Tcl_GetServiceMode()  
 {  
     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);  
   
     return tsdPtr->serviceMode;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * Tcl_SetServiceMode --  
  *  
  *      This routine sets the current service mode of the tsdPtr->  
  *  
  * Results:  
  *      Returns the previous service mode.  
  *  
  * Side effects:  
  *      Invokes the notifier service mode hook procedure.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 int  
 Tcl_SetServiceMode(mode)  
     int mode;                   /* New service mode: TCL_SERVICE_ALL or  
                                  * TCL_SERVICE_NONE */  
 {  
     int oldMode;  
     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);  
   
     oldMode = tsdPtr->serviceMode;  
     tsdPtr->serviceMode = mode;  
     Tcl_ServiceModeHook(mode);  
     return oldMode;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * Tcl_SetMaxBlockTime --  
  *  
  *      This procedure is invoked by event sources to tell the notifier  
  *      how long it may block the next time it blocks.  The timePtr  
  *      argument gives a maximum time;  the actual time may be less if  
  *      some other event source requested a smaller time.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      May reduce the length of the next sleep in the tsdPtr->  
  *  
  *----------------------------------------------------------------------  
  */  
   
 void  
 Tcl_SetMaxBlockTime(timePtr)  
     Tcl_Time *timePtr;          /* Specifies a maximum elapsed time for  
                                  * the next blocking operation in the  
                                  * event tsdPtr-> */  
 {  
     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);  
   
     if (!tsdPtr->blockTimeSet || (timePtr->sec < tsdPtr->blockTime.sec)  
             || ((timePtr->sec == tsdPtr->blockTime.sec)  
             && (timePtr->usec < tsdPtr->blockTime.usec))) {  
         tsdPtr->blockTime = *timePtr;  
         tsdPtr->blockTimeSet = 1;  
     }  
   
     /*  
      * If we are called outside an event source traversal, set the  
      * timeout immediately.  
      */  
   
     if (!tsdPtr->inTraversal) {  
         if (tsdPtr->blockTimeSet) {  
             Tcl_SetTimer(&tsdPtr->blockTime);  
         } else {  
             Tcl_SetTimer(NULL);  
         }  
     }  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * Tcl_DoOneEvent --  
  *  
  *      Process a single event of some sort.  If there's no work to  
  *      do, wait for an event to occur, then process it.  
  *  
  * Results:  
  *      The return value is 1 if the procedure actually found an event  
  *      to process.  If no processing occurred, then 0 is returned (this  
  *      can happen if the TCL_DONT_WAIT flag is set or if there are no  
  *      event handlers to wait for in the set specified by flags).  
  *  
  * Side effects:  
  *      May delay execution of process while waiting for an event,  
  *      unless TCL_DONT_WAIT is set in the flags argument.  Event  
  *      sources are invoked to check for and queue events.  Event  
  *      handlers may produce arbitrary side effects.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 int  
 Tcl_DoOneEvent(flags)  
     int flags;                  /* Miscellaneous flag values:  may be any  
                                  * combination of TCL_DONT_WAIT,  
                                  * TCL_WINDOW_EVENTS, TCL_FILE_EVENTS,  
                                  * TCL_TIMER_EVENTS, TCL_IDLE_EVENTS, or  
                                  * others defined by event sources. */  
 {  
     int result = 0, oldMode;  
     EventSource *sourcePtr;  
     Tcl_Time *timePtr;  
     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);  
   
     /*  
      * The first thing we do is to service any asynchronous event  
      * handlers.  
      */  
       
     if (Tcl_AsyncReady()) {  
         (void) Tcl_AsyncInvoke((Tcl_Interp *) NULL, 0);  
         return 1;  
     }  
   
     /*  
      * No event flags is equivalent to TCL_ALL_EVENTS.  
      */  
       
     if ((flags & TCL_ALL_EVENTS) == 0) {  
         flags |= TCL_ALL_EVENTS;  
     }  
   
     /*  
      * Set the service mode to none so notifier event routines won't  
      * try to service events recursively.  
      */  
   
     oldMode = tsdPtr->serviceMode;  
     tsdPtr->serviceMode = TCL_SERVICE_NONE;  
   
     /*  
      * The core of this procedure is an infinite loop, even though  
      * we only service one event.  The reason for this is that we  
      * may be processing events that don't do anything inside of Tcl.  
      */  
   
     while (1) {  
   
         /*  
          * If idle events are the only things to service, skip the  
          * main part of the loop and go directly to handle idle  
          * events (i.e. don't wait even if TCL_DONT_WAIT isn't set).  
          */  
   
         if ((flags & TCL_ALL_EVENTS) == TCL_IDLE_EVENTS) {  
             flags = TCL_IDLE_EVENTS|TCL_DONT_WAIT;  
             goto idleEvents;  
         }  
   
         /*  
          * Ask Tcl to service a queued event, if there are any.  
          */  
   
         if (Tcl_ServiceEvent(flags)) {  
             result = 1;      
             break;  
         }  
   
         /*  
          * If TCL_DONT_WAIT is set, be sure to poll rather than  
          * blocking, otherwise reset the block time to infinity.  
          */  
   
         if (flags & TCL_DONT_WAIT) {  
             tsdPtr->blockTime.sec = 0;  
             tsdPtr->blockTime.usec = 0;  
             tsdPtr->blockTimeSet = 1;  
         } else {  
             tsdPtr->blockTimeSet = 0;  
         }  
   
         /*  
          * Set up all the event sources for new events.  This will  
          * cause the block time to be updated if necessary.  
          */  
   
         tsdPtr->inTraversal = 1;  
         for (sourcePtr = tsdPtr->firstEventSourcePtr; sourcePtr != NULL;  
              sourcePtr = sourcePtr->nextPtr) {  
             if (sourcePtr->setupProc) {  
                 (sourcePtr->setupProc)(sourcePtr->clientData, flags);  
             }  
         }  
         tsdPtr->inTraversal = 0;  
   
         if ((flags & TCL_DONT_WAIT) || tsdPtr->blockTimeSet) {  
             timePtr = &tsdPtr->blockTime;  
         } else {  
             timePtr = NULL;  
         }  
   
         /*  
          * Wait for a new event or a timeout.  If Tcl_WaitForEvent  
          * returns -1, we should abort Tcl_DoOneEvent.  
          */  
   
         result = Tcl_WaitForEvent(timePtr);  
         if (result < 0) {  
             result = 0;  
             break;  
         }  
   
         /*  
          * Check all the event sources for new events.  
          */  
   
         for (sourcePtr = tsdPtr->firstEventSourcePtr; sourcePtr != NULL;  
              sourcePtr = sourcePtr->nextPtr) {  
             if (sourcePtr->checkProc) {  
                 (sourcePtr->checkProc)(sourcePtr->clientData, flags);  
             }  
         }  
   
         /*  
          * Check for events queued by the notifier or event sources.  
          */  
   
         if (Tcl_ServiceEvent(flags)) {  
             result = 1;  
             break;  
         }  
   
         /*  
          * We've tried everything at this point, but nobody we know  
          * about had anything to do.  Check for idle events.  If none,  
          * either quit or go back to the top and try again.  
          */  
   
         idleEvents:  
         if (flags & TCL_IDLE_EVENTS) {  
             if (TclServiceIdle()) {  
                 result = 1;  
                 break;  
             }  
         }  
         if (flags & TCL_DONT_WAIT) {  
             break;  
         }  
   
         /*  
          * If Tcl_WaitForEvent has returned 1,  
          * indicating that one system event has been dispatched  
          * (and thus that some Tcl code might have been indirectly executed),  
          * we break out of the loop.  
          * We do this to give VwaitCmd for instance a chance to check  
          * if that system event had the side effect of changing the  
          * variable (so the vwait can return and unwind properly).  
          *  
          * NB: We will process idle events if any first, because  
          *     otherwise we might never do the idle events if the notifier  
          *     always gets system events.  
          */  
   
         if (result) {  
             break;  
         }  
   
     }  
   
     tsdPtr->serviceMode = oldMode;  
     return result;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * Tcl_ServiceAll --  
  *  
  *      This routine checks all of the event sources, processes  
  *      events that are on the Tcl event queue, and then calls the  
  *      any idle handlers.  Platform specific notifier callbacks that  
  *      generate events should call this routine before returning to  
  *      the system in order to ensure that Tcl gets a chance to  
  *      process the new events.  
  *  
  * Results:  
  *      Returns 1 if an event or idle handler was invoked, else 0.  
  *  
  * Side effects:  
  *      Anything that an event or idle handler may do.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 int  
 Tcl_ServiceAll()  
 {  
     int result = 0;  
     EventSource *sourcePtr;  
     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);  
   
     if (tsdPtr->serviceMode == TCL_SERVICE_NONE) {  
         return result;  
     }  
   
     /*  
      * We need to turn off event servicing like we to in Tcl_DoOneEvent,  
      * to avoid recursive calls.  
      */  
       
     tsdPtr->serviceMode = TCL_SERVICE_NONE;  
   
     /*  
      * Check async handlers first.  
      */  
   
     if (Tcl_AsyncReady()) {  
         (void) Tcl_AsyncInvoke((Tcl_Interp *) NULL, 0);  
     }  
   
     /*  
      * Make a single pass through all event sources, queued events,  
      * and idle handlers.  Note that we wait to update the notifier  
      * timer until the end so we can avoid multiple changes.  
      */  
   
     tsdPtr->inTraversal = 1;  
     tsdPtr->blockTimeSet = 0;  
   
     for (sourcePtr = tsdPtr->firstEventSourcePtr; sourcePtr != NULL;  
          sourcePtr = sourcePtr->nextPtr) {  
         if (sourcePtr->setupProc) {  
             (sourcePtr->setupProc)(sourcePtr->clientData, TCL_ALL_EVENTS);  
         }  
     }  
     for (sourcePtr = tsdPtr->firstEventSourcePtr; sourcePtr != NULL;  
          sourcePtr = sourcePtr->nextPtr) {  
         if (sourcePtr->checkProc) {  
             (sourcePtr->checkProc)(sourcePtr->clientData, TCL_ALL_EVENTS);  
         }  
     }  
   
     while (Tcl_ServiceEvent(0)) {  
         result = 1;  
     }  
     if (TclServiceIdle()) {  
         result = 1;  
     }  
   
     if (!tsdPtr->blockTimeSet) {  
         Tcl_SetTimer(NULL);  
     } else {  
         Tcl_SetTimer(&tsdPtr->blockTime);  
     }  
     tsdPtr->inTraversal = 0;  
     tsdPtr->serviceMode = TCL_SERVICE_ALL;  
     return result;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * Tcl_ThreadAlert --  
  *  
  *      This function wakes up the notifier associated with the  
  *      specified thread (if there is one).    
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      None.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 void  
 Tcl_ThreadAlert(threadId)  
     Tcl_ThreadId threadId;      /* Identifier for thread to use. */  
 {  
     ThreadSpecificData *tsdPtr;  
   
     /*  
      * Find the notifier associated with the specified thread.  
      * Note that we need to hold the listLock while calling  
      * Tcl_AlertNotifier to avoid a race condition where  
      * the specified thread might destroy its notifier.  
      */  
   
     Tcl_MutexLock(&listLock);  
     for (tsdPtr = firstNotifierPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) {  
         if (tsdPtr->threadId == threadId) {  
             Tcl_AlertNotifier(tsdPtr->clientData);  
             break;  
         }  
     }  
     Tcl_MutexUnlock(&listLock);  
 }  
   
   
 /* $History: tclnotify.c $  
  *  
  * *****************  Version 1  *****************  
  * User: Dtashley     Date: 1/02/01    Time: 1:35a  
  * Created in $/IjuScripter, IjuConsole/Source/Tcl Base  
  * Initial check-in.  
  */  
   
 /* End of TCLNOTIFY.C */  
1    /* $Header$ */
2    /*
3     * tclNotify.c --
4     *
5     *      This file implements the generic portion of the Tcl notifier.
6     *      The notifier is lowest-level part of the event system.  It
7     *      manages an event queue that holds Tcl_Event structures.  The
8     *      platform specific portion of the notifier is defined in the
9     *      tcl*Notify.c files in each platform directory.
10     *
11     * Copyright (c) 1995-1997 Sun Microsystems, Inc.
12     * Copyright (c) 1998 by Scriptics Corporation.
13     *
14     * See the file "license.terms" for information on usage and redistribution
15     * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
16     *
17     * RCS: @(#) $Id: tclnotify.c,v 1.1.1.1 2001/06/13 04:43:48 dtashley Exp $
18     */
19    
20    #include "tclInt.h"
21    #include "tclPort.h"
22    
23    extern TclStubs tclStubs;
24    
25    /*
26     * For each event source (created with Tcl_CreateEventSource) there
27     * is a structure of the following type:
28     */
29    
30    typedef struct EventSource {
31        Tcl_EventSetupProc *setupProc;
32        Tcl_EventCheckProc *checkProc;
33        ClientData clientData;
34        struct EventSource *nextPtr;
35    } EventSource;
36    
37    /*
38     * The following structure keeps track of the state of the notifier on a
39     * per-thread basis. The first three elements keep track of the event queue.
40     * In addition to the first (next to be serviced) and last events in the queue,
41     * we keep track of a "marker" event.  This provides a simple priority
42     * mechanism whereby events can be inserted at the front of the queue but
43     * behind all other high-priority events already in the queue (this is used for
44     * things like a sequence of Enter and Leave events generated during a grab in
45     * Tk).  These elements are protected by the queueMutex so that any thread
46     * can queue an event on any notifier.  Note that all of the values in this
47     * structure will be initialized to 0.
48     */
49    
50    typedef struct ThreadSpecificData {
51        Tcl_Event *firstEventPtr;   /* First pending event, or NULL if none. */
52        Tcl_Event *lastEventPtr;    /* Last pending event, or NULL if none. */
53        Tcl_Event *markerEventPtr;  /* Last high-priority event in queue, or
54                                     * NULL if none. */
55        Tcl_Mutex queueMutex;       /* Mutex to protect access to the previous
56                                     * three fields. */
57        int serviceMode;            /* One of TCL_SERVICE_NONE or
58                                     * TCL_SERVICE_ALL. */
59        int blockTimeSet;           /* 0 means there is no maximum block
60                                     * time:  block forever. */
61        Tcl_Time blockTime;         /* If blockTimeSet is 1, gives the
62                                     * maximum elapsed time for the next block. */
63        int inTraversal;            /* 1 if Tcl_SetMaxBlockTime is being
64                                     * called during an event source traversal. */
65        EventSource *firstEventSourcePtr;
66                                    /* Pointer to first event source in
67                                     * list of event sources for this thread. */
68        Tcl_ThreadId threadId;      /* Thread that owns this notifier instance. */
69        ClientData clientData;      /* Opaque handle for platform specific
70                                     * notifier. */
71        struct ThreadSpecificData *nextPtr;
72                                    /* Next notifier in global list of notifiers.
73                                     * Access is controlled by the listLock global
74                                     * mutex. */
75    } ThreadSpecificData;
76    
77    static Tcl_ThreadDataKey dataKey;
78    
79    /*
80     * Global list of notifiers.  Access to this list is controlled by the
81     * listLock mutex.  If this becomes a performance bottleneck, this could
82     * be replaced with a hashtable.
83     */
84    
85    static ThreadSpecificData *firstNotifierPtr;
86    TCL_DECLARE_MUTEX(listLock)
87    
88    /*
89     * Declarations for routines used only in this file.
90     */
91    
92    static void             QueueEvent _ANSI_ARGS_((ThreadSpecificData *tsdPtr,
93                                Tcl_Event* evPtr, Tcl_QueuePosition position));
94    
95    /*
96     *----------------------------------------------------------------------
97     *
98     * TclInitNotifier --
99     *
100     *      Initialize the thread local data structures for the notifier
101     *      subsystem.
102     *
103     * Results:
104     *      None.
105     *
106     * Side effects:
107     *      Adds the current thread to the global list of notifiers.
108     *
109     *----------------------------------------------------------------------
110     */
111    
112    void
113    TclInitNotifier()
114    {
115        ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
116    
117        Tcl_MutexLock(&listLock);
118    
119        tsdPtr->threadId = Tcl_GetCurrentThread();
120        tsdPtr->clientData = Tcl_InitNotifier();
121        tsdPtr->nextPtr = firstNotifierPtr;
122        firstNotifierPtr = tsdPtr;
123    
124        Tcl_MutexUnlock(&listLock);
125    }
126    
127    /*
128     *----------------------------------------------------------------------
129     *
130     * TclFinalizeNotifier --
131     *
132     *      Finalize the thread local data structures for the notifier
133     *      subsystem.
134     *
135     * Results:
136     *      None.  
137     *
138     * Side effects:
139     *      Removes the notifier associated with the current thread from
140     *      the global notifier list.
141     *
142     *----------------------------------------------------------------------
143     */
144    
145    void
146    TclFinalizeNotifier()
147    {
148        ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
149        ThreadSpecificData **prevPtrPtr;
150    
151        Tcl_MutexLock(&listLock);
152    
153        Tcl_FinalizeNotifier(tsdPtr->clientData);
154        Tcl_MutexFinalize(&(tsdPtr->queueMutex));
155        for (prevPtrPtr = &firstNotifierPtr; *prevPtrPtr != NULL;
156             prevPtrPtr = &((*prevPtrPtr)->nextPtr)) {
157            if (*prevPtrPtr == tsdPtr) {
158                *prevPtrPtr = tsdPtr->nextPtr;
159                break;
160            }
161        }
162    
163        Tcl_MutexUnlock(&listLock);
164    }
165    
166    /*
167     *----------------------------------------------------------------------
168     *
169     * Tcl_SetNotifier --
170     *
171     *      Install a set of alternate functions for use with the notifier.
172     #      In particular, this can be used to install the Xt-based
173     *      notifier for use with the Browser plugin.
174     *
175     * Results:
176     *      None.
177     *
178     * Side effects:
179     *      Overstomps part of the stub vector.  This relies on hooks
180     *      added to the default procedures in case those are called
181     *      directly (i.e., not through the stub table.)
182     *
183     *----------------------------------------------------------------------
184     */
185    
186    void
187    Tcl_SetNotifier(notifierProcPtr)
188        Tcl_NotifierProcs *notifierProcPtr;
189    {
190    #if !defined(__WIN32__) && !defined(MAC_TCL) /* UNIX */
191        tclStubs.tcl_CreateFileHandler = notifierProcPtr->createFileHandlerProc;
192        tclStubs.tcl_DeleteFileHandler = notifierProcPtr->deleteFileHandlerProc;
193    #endif
194        tclStubs.tcl_SetTimer = notifierProcPtr->setTimerProc;
195        tclStubs.tcl_WaitForEvent = notifierProcPtr->waitForEventProc;
196    }
197    
198    /*
199     *----------------------------------------------------------------------
200     *
201     * Tcl_CreateEventSource --
202     *
203     *      This procedure is invoked to create a new source of events.
204     *      The source is identified by a procedure that gets invoked
205     *      during Tcl_DoOneEvent to check for events on that source
206     *      and queue them.
207     *
208     *
209     * Results:
210     *      None.
211     *
212     * Side effects:
213     *      SetupProc and checkProc will be invoked each time that Tcl_DoOneEvent
214     *      runs out of things to do.  SetupProc will be invoked before
215     *      Tcl_DoOneEvent calls select or whatever else it uses to wait
216     *      for events.  SetupProc typically calls functions like
217     *      Tcl_SetMaxBlockTime to indicate what to wait for.
218     *
219     *      CheckProc is called after select or whatever operation was actually
220     *      used to wait.  It figures out whether anything interesting actually
221     *      happened (e.g. by calling Tcl_AsyncReady), and then calls
222     *      Tcl_QueueEvent to queue any events that are ready.
223     *
224     *      Each of these procedures is passed two arguments, e.g.
225     *              (*checkProc)(ClientData clientData, int flags));
226     *      ClientData is the same as the clientData argument here, and flags
227     *      is a combination of things like TCL_FILE_EVENTS that indicates
228     *      what events are of interest:  setupProc and checkProc use flags
229     *      to figure out whether their events are relevant or not.
230     *
231     *----------------------------------------------------------------------
232     */
233    
234    void
235    Tcl_CreateEventSource(setupProc, checkProc, clientData)
236        Tcl_EventSetupProc *setupProc;      /* Procedure to invoke to figure out
237                                             * what to wait for. */
238        Tcl_EventCheckProc *checkProc;      /* Procedure to call after waiting
239                                             * to see what happened. */
240        ClientData clientData;              /* One-word argument to pass to
241                                             * setupProc and checkProc. */
242    {
243        ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
244        EventSource *sourcePtr = (EventSource *) ckalloc(sizeof(EventSource));
245    
246        sourcePtr->setupProc = setupProc;
247        sourcePtr->checkProc = checkProc;
248        sourcePtr->clientData = clientData;
249        sourcePtr->nextPtr = tsdPtr->firstEventSourcePtr;
250        tsdPtr->firstEventSourcePtr = sourcePtr;
251    }
252    
253    /*
254     *----------------------------------------------------------------------
255     *
256     * Tcl_DeleteEventSource --
257     *
258     *      This procedure is invoked to delete the source of events
259     *      given by proc and clientData.
260     *
261     * Results:
262     *      None.
263     *
264     * Side effects:
265     *      The given event source is cancelled, so its procedure will
266     *      never again be called.  If no such source exists, nothing
267     *      happens.
268     *
269     *----------------------------------------------------------------------
270     */
271    
272    void
273    Tcl_DeleteEventSource(setupProc, checkProc, clientData)
274        Tcl_EventSetupProc *setupProc;      /* Procedure to invoke to figure out
275                                             * what to wait for. */
276        Tcl_EventCheckProc *checkProc;      /* Procedure to call after waiting
277                                             * to see what happened. */
278        ClientData clientData;              /* One-word argument to pass to
279                                             * setupProc and checkProc. */
280    {
281        ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
282        EventSource *sourcePtr, *prevPtr;
283    
284        for (sourcePtr = tsdPtr->firstEventSourcePtr, prevPtr = NULL;
285                sourcePtr != NULL;
286                prevPtr = sourcePtr, sourcePtr = sourcePtr->nextPtr) {
287            if ((sourcePtr->setupProc != setupProc)
288                    || (sourcePtr->checkProc != checkProc)
289                    || (sourcePtr->clientData != clientData)) {
290                continue;
291            }
292            if (prevPtr == NULL) {
293                tsdPtr->firstEventSourcePtr = sourcePtr->nextPtr;
294            } else {
295                prevPtr->nextPtr = sourcePtr->nextPtr;
296            }
297            ckfree((char *) sourcePtr);
298            return;
299        }
300    }
301    
302    /*
303     *----------------------------------------------------------------------
304     *
305     * Tcl_QueueEvent --
306     *
307     *      Queue an event on the event queue associated with the
308     *      current thread.
309     *
310     * Results:
311     *      None.
312     *
313     * Side effects:
314     *      None.
315     *
316     *----------------------------------------------------------------------
317     */
318    
319    void
320    Tcl_QueueEvent(evPtr, position)
321        Tcl_Event* evPtr;           /* Event to add to queue.  The storage
322                                     * space must have been allocated the caller
323                                     * with malloc (ckalloc), and it becomes
324                                     * the property of the event queue.  It
325                                     * will be freed after the event has been
326                                     * handled. */
327        Tcl_QueuePosition position; /* One of TCL_QUEUE_TAIL, TCL_QUEUE_HEAD,
328                                     * TCL_QUEUE_MARK. */
329    {
330        ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
331        QueueEvent(tsdPtr, evPtr, position);
332    }
333    
334    /*
335     *----------------------------------------------------------------------
336     *
337     * Tcl_ThreadQueueEvent --
338     *
339     *      Queue an event on the specified thread's event queue.
340     *
341     * Results:
342     *      None.
343     *
344     * Side effects:
345     *      None.
346     *
347     *----------------------------------------------------------------------
348     */
349    
350    void
351    Tcl_ThreadQueueEvent(threadId, evPtr, position)
352        Tcl_ThreadId threadId;      /* Identifier for thread to use. */
353        Tcl_Event* evPtr;           /* Event to add to queue.  The storage
354                                     * space must have been allocated the caller
355                                     * with malloc (ckalloc), and it becomes
356                                     * the property of the event queue.  It
357                                     * will be freed after the event has been
358                                     * handled. */
359        Tcl_QueuePosition position; /* One of TCL_QUEUE_TAIL, TCL_QUEUE_HEAD,
360                                     * TCL_QUEUE_MARK. */
361    {
362        ThreadSpecificData *tsdPtr;
363    
364        /*
365         * Find the notifier associated with the specified thread.
366         */
367    
368        Tcl_MutexLock(&listLock);
369        for (tsdPtr = firstNotifierPtr; tsdPtr && tsdPtr->threadId != threadId;
370                 tsdPtr = tsdPtr->nextPtr) {
371            /* Empty loop body. */
372        }
373    
374        /*
375         * Queue the event if there was a notifier associated with the thread.
376         */
377    
378        if (tsdPtr) {
379            QueueEvent(tsdPtr, evPtr, position);
380        }
381        Tcl_MutexUnlock(&listLock);
382    }
383    
384    /*
385     *----------------------------------------------------------------------
386     *
387     * QueueEvent --
388     *
389     *      Insert an event into the specified thread's event queue at one
390     *      of three positions: the head, the tail, or before a floating
391     *      marker. Events inserted before the marker will be processed in
392     *      first-in-first-out order, but before any events inserted at
393     *      the tail of the queue.  Events inserted at the head of the
394     *      queue will be processed in last-in-first-out order.
395     *
396     * Results:
397     *      None.
398     *
399     * Side effects:
400     *      None.
401     *
402     *----------------------------------------------------------------------
403     */
404    
405    static void
406    QueueEvent(tsdPtr, evPtr, position)
407        ThreadSpecificData *tsdPtr; /* Handle to thread local data that indicates
408                                     * which event queue to use. */
409        Tcl_Event* evPtr;           /* Event to add to queue.  The storage
410                                     * space must have been allocated the caller
411                                     * with malloc (ckalloc), and it becomes
412                                     * the property of the event queue.  It
413                                     * will be freed after the event has been
414                                     * handled. */
415        Tcl_QueuePosition position; /* One of TCL_QUEUE_TAIL, TCL_QUEUE_HEAD,
416                                     * TCL_QUEUE_MARK. */
417    {
418        Tcl_MutexLock(&(tsdPtr->queueMutex));
419        if (position == TCL_QUEUE_TAIL) {
420            /*
421             * Append the event on the end of the queue.
422             */
423    
424            evPtr->nextPtr = NULL;
425            if (tsdPtr->firstEventPtr == NULL) {
426                tsdPtr->firstEventPtr = evPtr;
427            } else {
428                tsdPtr->lastEventPtr->nextPtr = evPtr;
429            }
430            tsdPtr->lastEventPtr = evPtr;
431        } else if (position == TCL_QUEUE_HEAD) {
432            /*
433             * Push the event on the head of the queue.
434             */
435    
436            evPtr->nextPtr = tsdPtr->firstEventPtr;
437            if (tsdPtr->firstEventPtr == NULL) {
438                tsdPtr->lastEventPtr = evPtr;
439            }          
440            tsdPtr->firstEventPtr = evPtr;
441        } else if (position == TCL_QUEUE_MARK) {
442            /*
443             * Insert the event after the current marker event and advance
444             * the marker to the new event.
445             */
446    
447            if (tsdPtr->markerEventPtr == NULL) {
448                evPtr->nextPtr = tsdPtr->firstEventPtr;
449                tsdPtr->firstEventPtr = evPtr;
450            } else {
451                evPtr->nextPtr = tsdPtr->markerEventPtr->nextPtr;
452                tsdPtr->markerEventPtr->nextPtr = evPtr;
453            }
454            tsdPtr->markerEventPtr = evPtr;
455            if (evPtr->nextPtr == NULL) {
456                tsdPtr->lastEventPtr = evPtr;
457            }
458        }
459        Tcl_MutexUnlock(&(tsdPtr->queueMutex));
460    }
461    
462    /*
463     *----------------------------------------------------------------------
464     *
465     * Tcl_DeleteEvents --
466     *
467     *      Calls a procedure for each event in the queue and deletes those
468     *      for which the procedure returns 1. Events for which the
469     *      procedure returns 0 are left in the queue.  Operates on the
470     *      queue associated with the current thread.
471     *
472     * Results:
473     *      None.
474     *
475     * Side effects:
476     *      Potentially removes one or more events from the event queue.
477     *
478     *----------------------------------------------------------------------
479     */
480    
481    void
482    Tcl_DeleteEvents(proc, clientData)
483        Tcl_EventDeleteProc *proc;          /* The procedure to call. */
484        ClientData clientData;              /* type-specific data. */
485    {
486        Tcl_Event *evPtr, *prevPtr, *hold;
487        ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
488    
489        Tcl_MutexLock(&(tsdPtr->queueMutex));
490        for (prevPtr = (Tcl_Event *) NULL, evPtr = tsdPtr->firstEventPtr;
491                 evPtr != (Tcl_Event *) NULL;
492                 ) {
493            if ((*proc) (evPtr, clientData) == 1) {
494                if (tsdPtr->firstEventPtr == evPtr) {
495                    tsdPtr->firstEventPtr = evPtr->nextPtr;
496                    if (evPtr->nextPtr == (Tcl_Event *) NULL) {
497                        tsdPtr->lastEventPtr = prevPtr;
498                    }
499                    if (tsdPtr->markerEventPtr == evPtr) {
500                        tsdPtr->markerEventPtr = prevPtr;
501                    }
502                } else {
503                    prevPtr->nextPtr = evPtr->nextPtr;
504                }
505                hold = evPtr;
506                evPtr = evPtr->nextPtr;
507                ckfree((char *) hold);
508            } else {
509                prevPtr = evPtr;
510                evPtr = evPtr->nextPtr;
511            }
512        }
513        Tcl_MutexUnlock(&(tsdPtr->queueMutex));
514    }
515    
516    /*
517     *----------------------------------------------------------------------
518     *
519     * Tcl_ServiceEvent --
520     *
521     *      Process one event from the event queue, or invoke an
522     *      asynchronous event handler.  Operates on event queue for
523     *      current thread.
524     *
525     * Results:
526     *      The return value is 1 if the procedure actually found an event
527     *      to process.  If no processing occurred, then 0 is returned.
528     *
529     * Side effects:
530     *      Invokes all of the event handlers for the highest priority
531     *      event in the event queue.  May collapse some events into a
532     *      single event or discard stale events.
533     *
534     *----------------------------------------------------------------------
535     */
536    
537    int
538    Tcl_ServiceEvent(flags)
539        int flags;                  /* Indicates what events should be processed.
540                                     * May be any combination of TCL_WINDOW_EVENTS
541                                     * TCL_FILE_EVENTS, TCL_TIMER_EVENTS, or other
542                                     * flags defined elsewhere.  Events not
543                                     * matching this will be skipped for processing
544                                     * later. */
545    {
546        Tcl_Event *evPtr, *prevPtr;
547        Tcl_EventProc *proc;
548        int result;
549        ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
550    
551        /*
552         * Asynchronous event handlers are considered to be the highest
553         * priority events, and so must be invoked before we process events
554         * on the event queue.
555         */
556        
557        if (Tcl_AsyncReady()) {
558            (void) Tcl_AsyncInvoke((Tcl_Interp *) NULL, 0);
559            return 1;
560        }
561    
562        /*
563         * No event flags is equivalent to TCL_ALL_EVENTS.
564         */
565        
566        if ((flags & TCL_ALL_EVENTS) == 0) {
567            flags |= TCL_ALL_EVENTS;
568        }
569    
570        /*
571         * Loop through all the events in the queue until we find one
572         * that can actually be handled.
573         */
574    
575        Tcl_MutexLock(&(tsdPtr->queueMutex));
576        for (evPtr = tsdPtr->firstEventPtr; evPtr != NULL;
577             evPtr = evPtr->nextPtr) {
578            /*
579             * Call the handler for the event.  If it actually handles the
580             * event then free the storage for the event.  There are two
581             * tricky things here, both stemming from the fact that the event
582             * code may be re-entered while servicing the event:
583             *
584             * 1. Set the "proc" field to NULL.  This is a signal to ourselves
585             *    that we shouldn't reexecute the handler if the event loop
586             *    is re-entered.
587             * 2. When freeing the event, must search the queue again from the
588             *    front to find it.  This is because the event queue could
589             *    change almost arbitrarily while handling the event, so we
590             *    can't depend on pointers found now still being valid when
591             *    the handler returns.
592             */
593    
594            proc = evPtr->proc;
595            if (proc == NULL) {
596                continue;
597            }
598            evPtr->proc = NULL;
599    
600            /*
601             * Release the lock before calling the event procedure.  This
602             * allows other threads to post events if we enter a recursive
603             * event loop in this thread.  Note that we are making the assumption
604             * that if the proc returns 0, the event is still in the list.
605             */
606    
607            Tcl_MutexUnlock(&(tsdPtr->queueMutex));
608            result = (*proc)(evPtr, flags);
609            Tcl_MutexLock(&(tsdPtr->queueMutex));
610    
611            if (result) {
612                /*
613                 * The event was processed, so remove it from the queue.
614                 */
615    
616                if (tsdPtr->firstEventPtr == evPtr) {
617                    tsdPtr->firstEventPtr = evPtr->nextPtr;
618                    if (evPtr->nextPtr == NULL) {
619                        tsdPtr->lastEventPtr = NULL;
620                    }
621                    if (tsdPtr->markerEventPtr == evPtr) {
622                        tsdPtr->markerEventPtr = NULL;
623                    }
624                } else {
625                    for (prevPtr = tsdPtr->firstEventPtr;
626                         prevPtr && prevPtr->nextPtr != evPtr;
627                         prevPtr = prevPtr->nextPtr) {
628                        /* Empty loop body. */
629                    }
630                    if (prevPtr) {
631                        prevPtr->nextPtr = evPtr->nextPtr;
632                        if (evPtr->nextPtr == NULL) {
633                            tsdPtr->lastEventPtr = prevPtr;
634                        }
635                        if (tsdPtr->markerEventPtr == evPtr) {
636                            tsdPtr->markerEventPtr = prevPtr;
637                        }
638                    } else {
639                        evPtr = NULL;
640                    }
641                }
642                if (evPtr) {
643                    ckfree((char *) evPtr);
644                }
645                Tcl_MutexUnlock(&(tsdPtr->queueMutex));
646                return 1;
647            } else {
648                /*
649                 * The event wasn't actually handled, so we have to restore
650                 * the proc field to allow the event to be attempted again.
651                 */
652    
653                evPtr->proc = proc;
654            }
655        }
656        Tcl_MutexUnlock(&(tsdPtr->queueMutex));
657        return 0;
658    }
659    
660    /*
661     *----------------------------------------------------------------------
662     *
663     * Tcl_GetServiceMode --
664     *
665     *      This routine returns the current service mode of the notifier.
666     *
667     * Results:
668     *      Returns either TCL_SERVICE_ALL or TCL_SERVICE_NONE.
669     *
670     * Side effects:
671     *      None.
672     *
673     *----------------------------------------------------------------------
674     */
675    
676    int
677    Tcl_GetServiceMode()
678    {
679        ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
680    
681        return tsdPtr->serviceMode;
682    }
683    
684    /*
685     *----------------------------------------------------------------------
686     *
687     * Tcl_SetServiceMode --
688     *
689     *      This routine sets the current service mode of the tsdPtr->
690     *
691     * Results:
692     *      Returns the previous service mode.
693     *
694     * Side effects:
695     *      Invokes the notifier service mode hook procedure.
696     *
697     *----------------------------------------------------------------------
698     */
699    
700    int
701    Tcl_SetServiceMode(mode)
702        int mode;                   /* New service mode: TCL_SERVICE_ALL or
703                                     * TCL_SERVICE_NONE */
704    {
705        int oldMode;
706        ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
707    
708        oldMode = tsdPtr->serviceMode;
709        tsdPtr->serviceMode = mode;
710        Tcl_ServiceModeHook(mode);
711        return oldMode;
712    }
713    
714    /*
715     *----------------------------------------------------------------------
716     *
717     * Tcl_SetMaxBlockTime --
718     *
719     *      This procedure is invoked by event sources to tell the notifier
720     *      how long it may block the next time it blocks.  The timePtr
721     *      argument gives a maximum time;  the actual time may be less if
722     *      some other event source requested a smaller time.
723     *
724     * Results:
725     *      None.
726     *
727     * Side effects:
728     *      May reduce the length of the next sleep in the tsdPtr->
729     *
730     *----------------------------------------------------------------------
731     */
732    
733    void
734    Tcl_SetMaxBlockTime(timePtr)
735        Tcl_Time *timePtr;          /* Specifies a maximum elapsed time for
736                                     * the next blocking operation in the
737                                     * event tsdPtr-> */
738    {
739        ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
740    
741        if (!tsdPtr->blockTimeSet || (timePtr->sec < tsdPtr->blockTime.sec)
742                || ((timePtr->sec == tsdPtr->blockTime.sec)
743                && (timePtr->usec < tsdPtr->blockTime.usec))) {
744            tsdPtr->blockTime = *timePtr;
745            tsdPtr->blockTimeSet = 1;
746        }
747    
748        /*
749         * If we are called outside an event source traversal, set the
750         * timeout immediately.
751         */
752    
753        if (!tsdPtr->inTraversal) {
754            if (tsdPtr->blockTimeSet) {
755                Tcl_SetTimer(&tsdPtr->blockTime);
756            } else {
757                Tcl_SetTimer(NULL);
758            }
759        }
760    }
761    
762    /*
763     *----------------------------------------------------------------------
764     *
765     * Tcl_DoOneEvent --
766     *
767     *      Process a single event of some sort.  If there's no work to
768     *      do, wait for an event to occur, then process it.
769     *
770     * Results:
771     *      The return value is 1 if the procedure actually found an event
772     *      to process.  If no processing occurred, then 0 is returned (this
773     *      can happen if the TCL_DONT_WAIT flag is set or if there are no
774     *      event handlers to wait for in the set specified by flags).
775     *
776     * Side effects:
777     *      May delay execution of process while waiting for an event,
778     *      unless TCL_DONT_WAIT is set in the flags argument.  Event
779     *      sources are invoked to check for and queue events.  Event
780     *      handlers may produce arbitrary side effects.
781     *
782     *----------------------------------------------------------------------
783     */
784    
785    int
786    Tcl_DoOneEvent(flags)
787        int flags;                  /* Miscellaneous flag values:  may be any
788                                     * combination of TCL_DONT_WAIT,
789                                     * TCL_WINDOW_EVENTS, TCL_FILE_EVENTS,
790                                     * TCL_TIMER_EVENTS, TCL_IDLE_EVENTS, or
791                                     * others defined by event sources. */
792    {
793        int result = 0, oldMode;
794        EventSource *sourcePtr;
795        Tcl_Time *timePtr;
796        ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
797    
798        /*
799         * The first thing we do is to service any asynchronous event
800         * handlers.
801         */
802        
803        if (Tcl_AsyncReady()) {
804            (void) Tcl_AsyncInvoke((Tcl_Interp *) NULL, 0);
805            return 1;
806        }
807    
808        /*
809         * No event flags is equivalent to TCL_ALL_EVENTS.
810         */
811        
812        if ((flags & TCL_ALL_EVENTS) == 0) {
813            flags |= TCL_ALL_EVENTS;
814        }
815    
816        /*
817         * Set the service mode to none so notifier event routines won't
818         * try to service events recursively.
819         */
820    
821        oldMode = tsdPtr->serviceMode;
822        tsdPtr->serviceMode = TCL_SERVICE_NONE;
823    
824        /*
825         * The core of this procedure is an infinite loop, even though
826         * we only service one event.  The reason for this is that we
827         * may be processing events that don't do anything inside of Tcl.
828         */
829    
830        while (1) {
831    
832            /*
833             * If idle events are the only things to service, skip the
834             * main part of the loop and go directly to handle idle
835             * events (i.e. don't wait even if TCL_DONT_WAIT isn't set).
836             */
837    
838            if ((flags & TCL_ALL_EVENTS) == TCL_IDLE_EVENTS) {
839                flags = TCL_IDLE_EVENTS|TCL_DONT_WAIT;
840                goto idleEvents;
841            }
842    
843            /*
844             * Ask Tcl to service a queued event, if there are any.
845             */
846    
847            if (Tcl_ServiceEvent(flags)) {
848                result = 1;    
849                break;
850            }
851    
852            /*
853             * If TCL_DONT_WAIT is set, be sure to poll rather than
854             * blocking, otherwise reset the block time to infinity.
855             */
856    
857            if (flags & TCL_DONT_WAIT) {
858                tsdPtr->blockTime.sec = 0;
859                tsdPtr->blockTime.usec = 0;
860                tsdPtr->blockTimeSet = 1;
861            } else {
862                tsdPtr->blockTimeSet = 0;
863            }
864    
865            /*
866             * Set up all the event sources for new events.  This will
867             * cause the block time to be updated if necessary.
868             */
869    
870            tsdPtr->inTraversal = 1;
871            for (sourcePtr = tsdPtr->firstEventSourcePtr; sourcePtr != NULL;
872                 sourcePtr = sourcePtr->nextPtr) {
873                if (sourcePtr->setupProc) {
874                    (sourcePtr->setupProc)(sourcePtr->clientData, flags);
875                }
876            }
877            tsdPtr->inTraversal = 0;
878    
879            if ((flags & TCL_DONT_WAIT) || tsdPtr->blockTimeSet) {
880                timePtr = &tsdPtr->blockTime;
881            } else {
882                timePtr = NULL;
883            }
884    
885            /*
886             * Wait for a new event or a timeout.  If Tcl_WaitForEvent
887             * returns -1, we should abort Tcl_DoOneEvent.
888             */
889    
890            result = Tcl_WaitForEvent(timePtr);
891            if (result < 0) {
892                result = 0;
893                break;
894            }
895    
896            /*
897             * Check all the event sources for new events.
898             */
899    
900            for (sourcePtr = tsdPtr->firstEventSourcePtr; sourcePtr != NULL;
901                 sourcePtr = sourcePtr->nextPtr) {
902                if (sourcePtr->checkProc) {
903                    (sourcePtr->checkProc)(sourcePtr->clientData, flags);
904                }
905            }
906    
907            /*
908             * Check for events queued by the notifier or event sources.
909             */
910    
911            if (Tcl_ServiceEvent(flags)) {
912                result = 1;
913                break;
914            }
915    
916            /*
917             * We've tried everything at this point, but nobody we know
918             * about had anything to do.  Check for idle events.  If none,
919             * either quit or go back to the top and try again.
920             */
921    
922            idleEvents:
923            if (flags & TCL_IDLE_EVENTS) {
924                if (TclServiceIdle()) {
925                    result = 1;
926                    break;
927                }
928            }
929            if (flags & TCL_DONT_WAIT) {
930                break;
931            }
932    
933            /*
934             * If Tcl_WaitForEvent has returned 1,
935             * indicating that one system event has been dispatched
936             * (and thus that some Tcl code might have been indirectly executed),
937             * we break out of the loop.
938             * We do this to give VwaitCmd for instance a chance to check
939             * if that system event had the side effect of changing the
940             * variable (so the vwait can return and unwind properly).
941             *
942             * NB: We will process idle events if any first, because
943             *     otherwise we might never do the idle events if the notifier
944             *     always gets system events.
945             */
946    
947            if (result) {
948                break;
949            }
950    
951        }
952    
953        tsdPtr->serviceMode = oldMode;
954        return result;
955    }
956    
957    /*
958     *----------------------------------------------------------------------
959     *
960     * Tcl_ServiceAll --
961     *
962     *      This routine checks all of the event sources, processes
963     *      events that are on the Tcl event queue, and then calls the
964     *      any idle handlers.  Platform specific notifier callbacks that
965     *      generate events should call this routine before returning to
966     *      the system in order to ensure that Tcl gets a chance to
967     *      process the new events.
968     *
969     * Results:
970     *      Returns 1 if an event or idle handler was invoked, else 0.
971     *
972     * Side effects:
973     *      Anything that an event or idle handler may do.
974     *
975     *----------------------------------------------------------------------
976     */
977    
978    int
979    Tcl_ServiceAll()
980    {
981        int result = 0;
982        EventSource *sourcePtr;
983        ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
984    
985        if (tsdPtr->serviceMode == TCL_SERVICE_NONE) {
986            return result;
987        }
988    
989        /*
990         * We need to turn off event servicing like we to in Tcl_DoOneEvent,
991         * to avoid recursive calls.
992         */
993        
994        tsdPtr->serviceMode = TCL_SERVICE_NONE;
995    
996        /*
997         * Check async handlers first.
998         */
999    
1000        if (Tcl_AsyncReady()) {
1001            (void) Tcl_AsyncInvoke((Tcl_Interp *) NULL, 0);
1002        }
1003    
1004        /*
1005         * Make a single pass through all event sources, queued events,
1006         * and idle handlers.  Note that we wait to update the notifier
1007         * timer until the end so we can avoid multiple changes.
1008         */
1009    
1010        tsdPtr->inTraversal = 1;
1011        tsdPtr->blockTimeSet = 0;
1012    
1013        for (sourcePtr = tsdPtr->firstEventSourcePtr; sourcePtr != NULL;
1014             sourcePtr = sourcePtr->nextPtr) {
1015            if (sourcePtr->setupProc) {
1016                (sourcePtr->setupProc)(sourcePtr->clientData, TCL_ALL_EVENTS);
1017            }
1018        }
1019        for (sourcePtr = tsdPtr->firstEventSourcePtr; sourcePtr != NULL;
1020             sourcePtr = sourcePtr->nextPtr) {
1021            if (sourcePtr->checkProc) {
1022                (sourcePtr->checkProc)(sourcePtr->clientData, TCL_ALL_EVENTS);
1023            }
1024        }
1025    
1026        while (Tcl_ServiceEvent(0)) {
1027            result = 1;
1028        }
1029        if (TclServiceIdle()) {
1030            result = 1;
1031        }
1032    
1033        if (!tsdPtr->blockTimeSet) {
1034            Tcl_SetTimer(NULL);
1035        } else {
1036            Tcl_SetTimer(&tsdPtr->blockTime);
1037        }
1038        tsdPtr->inTraversal = 0;
1039        tsdPtr->serviceMode = TCL_SERVICE_ALL;
1040        return result;
1041    }
1042    
1043    /*
1044     *----------------------------------------------------------------------
1045     *
1046     * Tcl_ThreadAlert --
1047     *
1048     *      This function wakes up the notifier associated with the
1049     *      specified thread (if there is one).  
1050     *
1051     * Results:
1052     *      None.
1053     *
1054     * Side effects:
1055     *      None.
1056     *
1057     *----------------------------------------------------------------------
1058     */
1059    
1060    void
1061    Tcl_ThreadAlert(threadId)
1062        Tcl_ThreadId threadId;      /* Identifier for thread to use. */
1063    {
1064        ThreadSpecificData *tsdPtr;
1065    
1066        /*
1067         * Find the notifier associated with the specified thread.
1068         * Note that we need to hold the listLock while calling
1069         * Tcl_AlertNotifier to avoid a race condition where
1070         * the specified thread might destroy its notifier.
1071         */
1072    
1073        Tcl_MutexLock(&listLock);
1074        for (tsdPtr = firstNotifierPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) {
1075            if (tsdPtr->threadId == threadId) {
1076                Tcl_AlertNotifier(tsdPtr->clientData);
1077                break;
1078            }
1079        }
1080        Tcl_MutexUnlock(&listLock);
1081    }
1082    
1083    /* End of tclnotify.c */

Legend:
Removed from v.42  
changed lines
  Added in v.71

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25