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