tclNotify.c

Go to the documentation of this file.
00001 /*
00002  * tclNotify.c --
00003  *
00004  *      This file implements the generic portion of the Tcl notifier. The
00005  *      notifier is lowest-level part of the event system. It manages an event
00006  *      queue that holds Tcl_Event structures. The platform specific portion
00007  *      of the notifier is defined in the tcl*Notify.c files in each platform
00008  *      directory.
00009  *
00010  * Copyright (c) 1995-1997 Sun Microsystems, Inc.
00011  * Copyright (c) 1998 by Scriptics Corporation.
00012  * Copyright (c) 2003 by Kevin B. Kenny.  All rights reserved.
00013  *
00014  * See the file "license.terms" for information on usage and redistribution of
00015  * this file, and for a DISCLAIMER OF ALL WARRANTIES.
00016  *
00017  * RCS: @(#) $Id: tclNotify.c,v 1.25 2006/09/25 15:02:54 dkf Exp $
00018  */
00019 
00020 #include "tclInt.h"
00021 
00022 extern TclStubs tclStubs;
00023 
00024 /*
00025  * For each event source (created with Tcl_CreateEventSource) there is a
00026  * structure of the following type:
00027  */
00028 
00029 typedef struct EventSource {
00030     Tcl_EventSetupProc *setupProc;
00031     Tcl_EventCheckProc *checkProc;
00032     ClientData clientData;
00033     struct EventSource *nextPtr;
00034 } EventSource;
00035 
00036 /*
00037  * The following structure keeps track of the state of the notifier on a
00038  * per-thread basis. The first three elements keep track of the event queue.
00039  * In addition to the first (next to be serviced) and last events in the
00040  * queue, we keep track of a "marker" event. This provides a simple priority
00041  * mechanism whereby events can be inserted at the front of the queue but
00042  * behind all other high-priority events already in the queue (this is used
00043  * for things like a sequence of Enter and Leave events generated during a
00044  * grab in Tk). These elements are protected by the queueMutex so that any
00045  * thread can queue an event on any notifier. Note that all of the values in
00046  * this structure will be initialized to 0.
00047  */
00048 
00049 typedef struct ThreadSpecificData {
00050     Tcl_Event *firstEventPtr;   /* First pending event, or NULL if none. */
00051     Tcl_Event *lastEventPtr;    /* Last pending event, or NULL if none. */
00052     Tcl_Event *markerEventPtr;  /* Last high-priority event in queue, or NULL
00053                                  * if none. */
00054     Tcl_Mutex queueMutex;       /* Mutex to protect access to the previous
00055                                  * three fields. */
00056     int serviceMode;            /* One of TCL_SERVICE_NONE or
00057                                  * TCL_SERVICE_ALL. */
00058     int blockTimeSet;           /* 0 means there is no maximum block time:
00059                                  * block forever. */
00060     Tcl_Time blockTime;         /* If blockTimeSet is 1, gives the maximum
00061                                  * elapsed time for the next block. */
00062     int inTraversal;            /* 1 if Tcl_SetMaxBlockTime is being called
00063                                  * during an event source traversal. */
00064     EventSource *firstEventSourcePtr;
00065                                 /* Pointer to first event source in list of
00066                                  * event sources for this thread. */
00067     Tcl_ThreadId threadId;      /* Thread that owns this notifier instance. */
00068     ClientData clientData;      /* Opaque handle for platform specific
00069                                  * notifier. */
00070     int initialized;            /* 1 if notifier has been initialized. */
00071     struct ThreadSpecificData *nextPtr;
00072                                 /* Next notifier in global list of notifiers.
00073                                  * Access is controlled by the listLock global
00074                                  * mutex. */
00075 } ThreadSpecificData;
00076 
00077 static Tcl_ThreadDataKey dataKey;
00078 
00079 /*
00080  * Global list of notifiers. Access to this list is controlled by the listLock
00081  * mutex. If this becomes a performance bottleneck, this could be replaced
00082  * with a hashtable.
00083  */
00084 
00085 static ThreadSpecificData *firstNotifierPtr = NULL;
00086 TCL_DECLARE_MUTEX(listLock)
00087 
00088 /*
00089  * Declarations for routines used only in this file.
00090  */
00091 
00092 static void             QueueEvent(ThreadSpecificData *tsdPtr,
00093                             Tcl_Event* evPtr, Tcl_QueuePosition position);
00094 
00095 /*
00096  *----------------------------------------------------------------------
00097  *
00098  * TclInitNotifier --
00099  *
00100  *      Initialize the thread local data structures for the notifier
00101  *      subsystem.
00102  *
00103  * Results:
00104  *      None.
00105  *
00106  * Side effects:
00107  *      Adds the current thread to the global list of notifiers.
00108  *
00109  *----------------------------------------------------------------------
00110  */
00111 
00112 void
00113 TclInitNotifier(void)
00114 {
00115     ThreadSpecificData *tsdPtr;
00116     Tcl_ThreadId threadId = Tcl_GetCurrentThread();
00117 
00118     Tcl_MutexLock(&listLock);
00119     for (tsdPtr = firstNotifierPtr; tsdPtr && tsdPtr->threadId != threadId;
00120             tsdPtr = tsdPtr->nextPtr) {
00121         /* Empty loop body. */
00122     }
00123 
00124     if (NULL == tsdPtr) {
00125         /*
00126          * Notifier not yet initialized in this thread.
00127          */
00128 
00129         tsdPtr = TCL_TSD_INIT(&dataKey);
00130         tsdPtr->threadId = threadId;
00131         tsdPtr->clientData = tclStubs.tcl_InitNotifier();
00132         tsdPtr->initialized = 1;
00133         tsdPtr->nextPtr = firstNotifierPtr;
00134         firstNotifierPtr = tsdPtr;
00135     }
00136     Tcl_MutexUnlock(&listLock);
00137 }
00138 
00139 /*
00140  *----------------------------------------------------------------------
00141  *
00142  * TclFinalizeNotifier --
00143  *
00144  *      Finalize the thread local data structures for the notifier subsystem.
00145  *
00146  * Results:
00147  *      None.
00148  *
00149  * Side effects:
00150  *      Removes the notifier associated with the current thread from the
00151  *      global notifier list. This is done only if the notifier was
00152  *      initialized for this thread by call to TclInitNotifier(). This is
00153  *      always true for threads which have been seeded with an Tcl
00154  *      interpreter, since the call to Tcl_CreateInterp will, among other
00155  *      things, call TclInitializeSubsystems() and this one will, in turn,
00156  *      call the TclInitNotifier() for the thread. For threads created without
00157  *      the Tcl interpreter, though, nobody is explicitly nor implicitly
00158  *      calling the TclInitNotifier hence, TclFinalizeNotifier should not be
00159  *      performed at all.
00160  *
00161  *----------------------------------------------------------------------
00162  */
00163 
00164 void
00165 TclFinalizeNotifier(void)
00166 {
00167     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
00168     ThreadSpecificData **prevPtrPtr;
00169     Tcl_Event *evPtr, *hold;
00170 
00171     if (!tsdPtr->initialized) {
00172         return;         /* Notifier not initialized for the current thread */
00173     }
00174 
00175     Tcl_MutexLock(&(tsdPtr->queueMutex));
00176     for (evPtr = tsdPtr->firstEventPtr; evPtr != NULL; ) {
00177         hold = evPtr;
00178         evPtr = evPtr->nextPtr;
00179         ckfree((char *) hold);
00180     }
00181     tsdPtr->firstEventPtr = NULL;
00182     tsdPtr->lastEventPtr = NULL;
00183     Tcl_MutexUnlock(&(tsdPtr->queueMutex));
00184 
00185     Tcl_MutexLock(&listLock);
00186 
00187     if (tclStubs.tcl_FinalizeNotifier) {
00188         tclStubs.tcl_FinalizeNotifier(tsdPtr->clientData);
00189     }
00190     Tcl_MutexFinalize(&(tsdPtr->queueMutex));
00191     for (prevPtrPtr = &firstNotifierPtr; *prevPtrPtr != NULL;
00192             prevPtrPtr = &((*prevPtrPtr)->nextPtr)) {
00193         if (*prevPtrPtr == tsdPtr) {
00194             *prevPtrPtr = tsdPtr->nextPtr;
00195             break;
00196         }
00197     }
00198     tsdPtr->initialized = 0;
00199 
00200     Tcl_MutexUnlock(&listLock);
00201 }
00202 
00203 /*
00204  *----------------------------------------------------------------------
00205  *
00206  * Tcl_SetNotifier --
00207  *
00208  *      Install a set of alternate functions for use with the notifier. In
00209  *      particular, this can be used to install the Xt-based notifier for use
00210  *      with the Browser plugin.
00211  *
00212  * Results:
00213  *      None.
00214  *
00215  * Side effects:
00216  *      Overstomps part of the stub vector. This relies on hooks added to the
00217  *      default functions in case those are called directly (i.e., not through
00218  *      the stub table.)
00219  *
00220  *----------------------------------------------------------------------
00221  */
00222 
00223 void
00224 Tcl_SetNotifier(
00225     Tcl_NotifierProcs *notifierProcPtr)
00226 {
00227 #if !defined(__WIN32__) /* UNIX */
00228     tclStubs.tcl_CreateFileHandler = notifierProcPtr->createFileHandlerProc;
00229     tclStubs.tcl_DeleteFileHandler = notifierProcPtr->deleteFileHandlerProc;
00230 #endif
00231     tclStubs.tcl_SetTimer = notifierProcPtr->setTimerProc;
00232     tclStubs.tcl_WaitForEvent = notifierProcPtr->waitForEventProc;
00233     tclStubs.tcl_InitNotifier = notifierProcPtr->initNotifierProc;
00234     tclStubs.tcl_FinalizeNotifier = notifierProcPtr->finalizeNotifierProc;
00235     tclStubs.tcl_AlertNotifier = notifierProcPtr->alertNotifierProc;
00236     tclStubs.tcl_ServiceModeHook = notifierProcPtr->serviceModeHookProc;
00237 }
00238 
00239 /*
00240  *----------------------------------------------------------------------
00241  *
00242  * Tcl_CreateEventSource --
00243  *
00244  *      This function is invoked to create a new source of events. The source
00245  *      is identified by a function that gets invoked during Tcl_DoOneEvent to
00246  *      check for events on that source and queue them.
00247  *
00248  *
00249  * Results:
00250  *      None.
00251  *
00252  * Side effects:
00253  *      SetupProc and checkProc will be invoked each time that Tcl_DoOneEvent
00254  *      runs out of things to do. SetupProc will be invoked before
00255  *      Tcl_DoOneEvent calls select or whatever else it uses to wait for
00256  *      events. SetupProc typically calls functions like Tcl_SetMaxBlockTime
00257  *      to indicate what to wait for.
00258  *
00259  *      CheckProc is called after select or whatever operation was actually
00260  *      used to wait. It figures out whether anything interesting actually
00261  *      happened (e.g. by calling Tcl_AsyncReady), and then calls
00262  *      Tcl_QueueEvent to queue any events that are ready.
00263  *
00264  *      Each of these functions is passed two arguments, e.g.
00265  *              (*checkProc)(ClientData clientData, int flags));
00266  *      ClientData is the same as the clientData argument here, and flags is a
00267  *      combination of things like TCL_FILE_EVENTS that indicates what events
00268  *      are of interest: setupProc and checkProc use flags to figure out
00269  *      whether their events are relevant or not.
00270  *
00271  *----------------------------------------------------------------------
00272  */
00273 
00274 void
00275 Tcl_CreateEventSource(
00276     Tcl_EventSetupProc *setupProc,
00277                                 /* Function to invoke to figure out what to
00278                                  * wait for. */
00279     Tcl_EventCheckProc *checkProc,
00280                                 /* Function to call after waiting to see what
00281                                  * happened. */
00282     ClientData clientData)      /* One-word argument to pass to setupProc and
00283                                  * checkProc. */
00284 {
00285     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
00286     EventSource *sourcePtr = (EventSource *) ckalloc(sizeof(EventSource));
00287 
00288     sourcePtr->setupProc = setupProc;
00289     sourcePtr->checkProc = checkProc;
00290     sourcePtr->clientData = clientData;
00291     sourcePtr->nextPtr = tsdPtr->firstEventSourcePtr;
00292     tsdPtr->firstEventSourcePtr = sourcePtr;
00293 }
00294 
00295 /*
00296  *----------------------------------------------------------------------
00297  *
00298  * Tcl_DeleteEventSource --
00299  *
00300  *      This function is invoked to delete the source of events given by proc
00301  *      and clientData.
00302  *
00303  * Results:
00304  *      None.
00305  *
00306  * Side effects:
00307  *      The given event source is cancelled, so its function will never again
00308  *      be called. If no such source exists, nothing happens.
00309  *
00310  *----------------------------------------------------------------------
00311  */
00312 
00313 void
00314 Tcl_DeleteEventSource(
00315     Tcl_EventSetupProc *setupProc,
00316                                 /* Function to invoke to figure out what to
00317                                  * wait for. */
00318     Tcl_EventCheckProc *checkProc,
00319                                 /* Function to call after waiting to see what
00320                                  * happened. */
00321     ClientData clientData)      /* One-word argument to pass to setupProc and
00322                                  * checkProc. */
00323 {
00324     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
00325     EventSource *sourcePtr, *prevPtr;
00326 
00327     for (sourcePtr = tsdPtr->firstEventSourcePtr, prevPtr = NULL;
00328             sourcePtr != NULL;
00329             prevPtr = sourcePtr, sourcePtr = sourcePtr->nextPtr) {
00330         if ((sourcePtr->setupProc != setupProc)
00331                 || (sourcePtr->checkProc != checkProc)
00332                 || (sourcePtr->clientData != clientData)) {
00333             continue;
00334         }
00335         if (prevPtr == NULL) {
00336             tsdPtr->firstEventSourcePtr = sourcePtr->nextPtr;
00337         } else {
00338             prevPtr->nextPtr = sourcePtr->nextPtr;
00339         }
00340         ckfree((char *) sourcePtr);
00341         return;
00342     }
00343 }
00344 
00345 /*
00346  *----------------------------------------------------------------------
00347  *
00348  * Tcl_QueueEvent --
00349  *
00350  *      Queue an event on the event queue associated with the current thread.
00351  *
00352  * Results:
00353  *      None.
00354  *
00355  * Side effects:
00356  *      None.
00357  *
00358  *----------------------------------------------------------------------
00359  */
00360 
00361 void
00362 Tcl_QueueEvent(
00363     Tcl_Event* evPtr,           /* Event to add to queue. The storage space
00364                                  * must have been allocated the caller with
00365                                  * malloc (ckalloc), and it becomes the
00366                                  * property of the event queue. It will be
00367                                  * freed after the event has been handled. */
00368     Tcl_QueuePosition position) /* One of TCL_QUEUE_TAIL, TCL_QUEUE_HEAD,
00369                                  * TCL_QUEUE_MARK. */
00370 {
00371     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
00372     QueueEvent(tsdPtr, evPtr, position);
00373 }
00374 
00375 /*
00376  *----------------------------------------------------------------------
00377  *
00378  * Tcl_ThreadQueueEvent --
00379  *
00380  *      Queue an event on the specified thread's event queue.
00381  *
00382  * Results:
00383  *      None.
00384  *
00385  * Side effects:
00386  *      None.
00387  *
00388  *----------------------------------------------------------------------
00389  */
00390 
00391 void
00392 Tcl_ThreadQueueEvent(
00393     Tcl_ThreadId threadId,      /* Identifier for thread to use. */
00394     Tcl_Event *evPtr,           /* Event to add to queue. The storage space
00395                                  * must have been allocated the caller with
00396                                  * malloc (ckalloc), and it becomes the
00397                                  * property of the event queue. It will be
00398                                  * freed after the event has been handled. */
00399     Tcl_QueuePosition position) /* One of TCL_QUEUE_TAIL, TCL_QUEUE_HEAD,
00400                                  * TCL_QUEUE_MARK. */
00401 {
00402     ThreadSpecificData *tsdPtr;
00403 
00404     /*
00405      * Find the notifier associated with the specified thread.
00406      */
00407 
00408     Tcl_MutexLock(&listLock);
00409     for (tsdPtr = firstNotifierPtr; tsdPtr && tsdPtr->threadId != threadId;
00410             tsdPtr = tsdPtr->nextPtr) {
00411         /* Empty loop body. */
00412     }
00413 
00414     /*
00415      * Queue the event if there was a notifier associated with the thread.
00416      */
00417 
00418     if (tsdPtr) {
00419         QueueEvent(tsdPtr, evPtr, position);
00420     }
00421     Tcl_MutexUnlock(&listLock);
00422 }
00423 
00424 /*
00425  *----------------------------------------------------------------------
00426  *
00427  * QueueEvent --
00428  *
00429  *      Insert an event into the specified thread's event queue at one of
00430  *      three positions: the head, the tail, or before a floating marker.
00431  *      Events inserted before the marker will be processed in first-in-
00432  *      first-out order, but before any events inserted at the tail of the
00433  *      queue. Events inserted at the head of the queue will be processed in
00434  *      last-in-first-out order.
00435  *
00436  * Results:
00437  *      None.
00438  *
00439  * Side effects:
00440  *      None.
00441  *
00442  *----------------------------------------------------------------------
00443  */
00444 
00445 static void
00446 QueueEvent(
00447     ThreadSpecificData *tsdPtr, /* Handle to thread local data that indicates
00448                                  * which event queue to use. */
00449     Tcl_Event *evPtr,           /* Event to add to queue.  The storage space
00450                                  * must have been allocated the caller with
00451                                  * malloc (ckalloc), and it becomes the
00452                                  * property of the event queue. It will be
00453                                  * freed after the event has been handled. */
00454     Tcl_QueuePosition position) /* One of TCL_QUEUE_TAIL, TCL_QUEUE_HEAD,
00455                                  * TCL_QUEUE_MARK. */
00456 {
00457     Tcl_MutexLock(&(tsdPtr->queueMutex));
00458     if (position == TCL_QUEUE_TAIL) {
00459         /*
00460          * Append the event on the end of the queue.
00461          */
00462 
00463         evPtr->nextPtr = NULL;
00464         if (tsdPtr->firstEventPtr == NULL) {
00465             tsdPtr->firstEventPtr = evPtr;
00466         } else {
00467             tsdPtr->lastEventPtr->nextPtr = evPtr;
00468         }
00469         tsdPtr->lastEventPtr = evPtr;
00470     } else if (position == TCL_QUEUE_HEAD) {
00471         /*
00472          * Push the event on the head of the queue.
00473          */
00474 
00475         evPtr->nextPtr = tsdPtr->firstEventPtr;
00476         if (tsdPtr->firstEventPtr == NULL) {
00477             tsdPtr->lastEventPtr = evPtr;
00478         }
00479         tsdPtr->firstEventPtr = evPtr;
00480     } else if (position == TCL_QUEUE_MARK) {
00481         /*
00482          * Insert the event after the current marker event and advance the
00483          * marker to the new event.
00484          */
00485 
00486         if (tsdPtr->markerEventPtr == NULL) {
00487             evPtr->nextPtr = tsdPtr->firstEventPtr;
00488             tsdPtr->firstEventPtr = evPtr;
00489         } else {
00490             evPtr->nextPtr = tsdPtr->markerEventPtr->nextPtr;
00491             tsdPtr->markerEventPtr->nextPtr = evPtr;
00492         }
00493         tsdPtr->markerEventPtr = evPtr;
00494         if (evPtr->nextPtr == NULL) {
00495             tsdPtr->lastEventPtr = evPtr;
00496         }
00497     }
00498     Tcl_MutexUnlock(&(tsdPtr->queueMutex));
00499 }
00500 
00501 /*
00502  *----------------------------------------------------------------------
00503  *
00504  * Tcl_DeleteEvents --
00505  *
00506  *      Calls a function for each event in the queue and deletes those for
00507  *      which the function returns 1. Events for which the function returns 0
00508  *      are left in the queue. Operates on the queue associated with the
00509  *      current thread.
00510  *
00511  * Results:
00512  *      None.
00513  *
00514  * Side effects:
00515  *      Potentially removes one or more events from the event queue.
00516  *
00517  *----------------------------------------------------------------------
00518  */
00519 
00520 void
00521 Tcl_DeleteEvents(
00522     Tcl_EventDeleteProc *proc,  /* The function to call. */
00523     ClientData clientData)      /* The type-specific data. */
00524 {
00525     Tcl_Event *evPtr;           /* Pointer to the event being examined */
00526     Tcl_Event *prevPtr;         /* Pointer to evPtr's predecessor, or NULL if
00527                                  * evPtr designates the first event in the
00528                                  * queue for the thread. */
00529     Tcl_Event* hold;
00530 
00531     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
00532 
00533     Tcl_MutexLock(&(tsdPtr->queueMutex));
00534 
00535     /*
00536      * Walk the queue of events for the thread, applying 'proc' to each to
00537      * decide whether to eliminate the event.
00538      */
00539 
00540     prevPtr = NULL;
00541     evPtr = tsdPtr->firstEventPtr;
00542     while (evPtr != NULL) {
00543         if ((*proc)(evPtr, clientData) == 1) {
00544             /*
00545              * This event should be deleted. Unlink it.
00546              */
00547 
00548             if (prevPtr == NULL) {
00549                 tsdPtr->firstEventPtr = evPtr->nextPtr;
00550             } else {
00551                 prevPtr->nextPtr = evPtr->nextPtr;
00552             }
00553 
00554             /*
00555              * Update 'last' and 'marker' events if either has been deleted.
00556              */
00557 
00558             if (evPtr->nextPtr == NULL) {
00559                 tsdPtr->lastEventPtr = prevPtr;
00560             }
00561             if (tsdPtr->markerEventPtr == evPtr) {
00562                 tsdPtr->markerEventPtr = prevPtr;
00563             }
00564 
00565             /*
00566              * Delete the event data structure.
00567              */
00568 
00569             hold = evPtr;
00570             evPtr = evPtr->nextPtr;
00571             ckfree((char *) hold);
00572         } else {
00573             /*
00574              * Event is to be retained.
00575              */
00576 
00577             prevPtr = evPtr;
00578             evPtr = evPtr->nextPtr;
00579         }
00580     }
00581     Tcl_MutexUnlock(&(tsdPtr->queueMutex));
00582 }
00583 
00584 /*
00585  *----------------------------------------------------------------------
00586  *
00587  * Tcl_ServiceEvent --
00588  *
00589  *      Process one event from the event queue, or invoke an asynchronous
00590  *      event handler. Operates on event queue for current thread.
00591  *
00592  * Results:
00593  *      The return value is 1 if the function actually found an event to
00594  *      process. If no processing occurred, then 0 is returned.
00595  *
00596  * Side effects:
00597  *      Invokes all of the event handlers for the highest priority event in
00598  *      the event queue. May collapse some events into a single event or
00599  *      discard stale events.
00600  *
00601  *----------------------------------------------------------------------
00602  */
00603 
00604 int
00605 Tcl_ServiceEvent(
00606     int flags)                  /* Indicates what events should be processed.
00607                                  * May be any combination of TCL_WINDOW_EVENTS
00608                                  * TCL_FILE_EVENTS, TCL_TIMER_EVENTS, or other
00609                                  * flags defined elsewhere. Events not
00610                                  * matching this will be skipped for
00611                                  * processing later. */
00612 {
00613     Tcl_Event *evPtr, *prevPtr;
00614     Tcl_EventProc *proc;
00615     int result;
00616     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
00617 
00618     /*
00619      * Asynchronous event handlers are considered to be the highest priority
00620      * events, and so must be invoked before we process events on the event
00621      * queue.
00622      */
00623 
00624     if (Tcl_AsyncReady()) {
00625         (void) Tcl_AsyncInvoke(NULL, 0);
00626         return 1;
00627     }
00628 
00629     /*
00630      * No event flags is equivalent to TCL_ALL_EVENTS.
00631      */
00632 
00633     if ((flags & TCL_ALL_EVENTS) == 0) {
00634         flags |= TCL_ALL_EVENTS;
00635     }
00636 
00637     /*
00638      * Loop through all the events in the queue until we find one that can
00639      * actually be handled.
00640      */
00641 
00642     Tcl_MutexLock(&(tsdPtr->queueMutex));
00643     for (evPtr = tsdPtr->firstEventPtr; evPtr != NULL;
00644             evPtr = evPtr->nextPtr) {
00645         /*
00646          * Call the handler for the event. If it actually handles the event
00647          * then free the storage for the event. There are two tricky things
00648          * here, both stemming from the fact that the event code may be
00649          * re-entered while servicing the event:
00650          *
00651          * 1. Set the "proc" field to NULL.  This is a signal to ourselves
00652          *    that we shouldn't reexecute the handler if the event loop is
00653          *    re-entered.
00654          * 2. When freeing the event, must search the queue again from the
00655          *    front to find it. This is because the event queue could change
00656          *    almost arbitrarily while handling the event, so we can't depend
00657          *    on pointers found now still being valid when the handler
00658          *    returns.
00659          */
00660 
00661         proc = evPtr->proc;
00662         if (proc == NULL) {
00663             continue;
00664         }
00665         evPtr->proc = NULL;
00666 
00667         /*
00668          * Release the lock before calling the event function. This allows
00669          * other threads to post events if we enter a recursive event loop in
00670          * this thread. Note that we are making the assumption that if the
00671          * proc returns 0, the event is still in the list.
00672          */
00673 
00674         Tcl_MutexUnlock(&(tsdPtr->queueMutex));
00675         result = (*proc)(evPtr, flags);
00676         Tcl_MutexLock(&(tsdPtr->queueMutex));
00677 
00678         if (result) {
00679             /*
00680              * The event was processed, so remove it from the queue.
00681              */
00682 
00683             if (tsdPtr->firstEventPtr == evPtr) {
00684                 tsdPtr->firstEventPtr = evPtr->nextPtr;
00685                 if (evPtr->nextPtr == NULL) {
00686                     tsdPtr->lastEventPtr = NULL;
00687                 }
00688                 if (tsdPtr->markerEventPtr == evPtr) {
00689                     tsdPtr->markerEventPtr = NULL;
00690                 }
00691             } else {
00692                 for (prevPtr = tsdPtr->firstEventPtr;
00693                         prevPtr && prevPtr->nextPtr != evPtr;
00694                         prevPtr = prevPtr->nextPtr) {
00695                     /* Empty loop body. */
00696                 }
00697                 if (prevPtr) {
00698                     prevPtr->nextPtr = evPtr->nextPtr;
00699                     if (evPtr->nextPtr == NULL) {
00700                         tsdPtr->lastEventPtr = prevPtr;
00701                     }
00702                     if (tsdPtr->markerEventPtr == evPtr) {
00703                         tsdPtr->markerEventPtr = prevPtr;
00704                     }
00705                 } else {
00706                     evPtr = NULL;
00707                 }
00708             }
00709             if (evPtr) {
00710                 ckfree((char *) evPtr);
00711             }
00712             Tcl_MutexUnlock(&(tsdPtr->queueMutex));
00713             return 1;
00714         } else {
00715             /*
00716              * The event wasn't actually handled, so we have to restore the
00717              * proc field to allow the event to be attempted again.
00718              */
00719 
00720             evPtr->proc = proc;
00721         }
00722     }
00723     Tcl_MutexUnlock(&(tsdPtr->queueMutex));
00724     return 0;
00725 }
00726 
00727 /*
00728  *----------------------------------------------------------------------
00729  *
00730  * Tcl_GetServiceMode --
00731  *
00732  *      This routine returns the current service mode of the notifier.
00733  *
00734  * Results:
00735  *      Returns either TCL_SERVICE_ALL or TCL_SERVICE_NONE.
00736  *
00737  * Side effects:
00738  *      None.
00739  *
00740  *----------------------------------------------------------------------
00741  */
00742 
00743 int
00744 Tcl_GetServiceMode(void)
00745 {
00746     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
00747 
00748     return tsdPtr->serviceMode;
00749 }
00750 
00751 /*
00752  *----------------------------------------------------------------------
00753  *
00754  * Tcl_SetServiceMode --
00755  *
00756  *      This routine sets the current service mode of the tsdPtr->
00757  *
00758  * Results:
00759  *      Returns the previous service mode.
00760  *
00761  * Side effects:
00762  *      Invokes the notifier service mode hook function.
00763  *
00764  *----------------------------------------------------------------------
00765  */
00766 
00767 int
00768 Tcl_SetServiceMode(
00769     int mode)                   /* New service mode: TCL_SERVICE_ALL or
00770                                  * TCL_SERVICE_NONE */
00771 {
00772     int oldMode;
00773     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
00774 
00775     oldMode = tsdPtr->serviceMode;
00776     tsdPtr->serviceMode = mode;
00777     if (tclStubs.tcl_ServiceModeHook) {
00778         tclStubs.tcl_ServiceModeHook(mode);
00779     }
00780     return oldMode;
00781 }
00782 
00783 /*
00784  *----------------------------------------------------------------------
00785  *
00786  * Tcl_SetMaxBlockTime --
00787  *
00788  *      This function is invoked by event sources to tell the notifier how
00789  *      long it may block the next time it blocks. The timePtr argument gives
00790  *      a maximum time; the actual time may be less if some other event source
00791  *      requested a smaller time.
00792  *
00793  * Results:
00794  *      None.
00795  *
00796  * Side effects:
00797  *      May reduce the length of the next sleep in the tsdPtr->
00798  *
00799  *----------------------------------------------------------------------
00800  */
00801 
00802 void
00803 Tcl_SetMaxBlockTime(
00804     Tcl_Time *timePtr)          /* Specifies a maximum elapsed time for the
00805                                  * next blocking operation in the event
00806                                  * tsdPtr-> */
00807 {
00808     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
00809 
00810     if (!tsdPtr->blockTimeSet || (timePtr->sec < tsdPtr->blockTime.sec)
00811             || ((timePtr->sec == tsdPtr->blockTime.sec)
00812             && (timePtr->usec < tsdPtr->blockTime.usec))) {
00813         tsdPtr->blockTime = *timePtr;
00814         tsdPtr->blockTimeSet = 1;
00815     }
00816 
00817     /*
00818      * If we are called outside an event source traversal, set the timeout
00819      * immediately.
00820      */
00821 
00822     if (!tsdPtr->inTraversal) {
00823         if (tsdPtr->blockTimeSet) {
00824             Tcl_SetTimer(&tsdPtr->blockTime);
00825         } else {
00826             Tcl_SetTimer(NULL);
00827         }
00828     }
00829 }
00830 
00831 /*
00832  *----------------------------------------------------------------------
00833  *
00834  * Tcl_DoOneEvent --
00835  *
00836  *      Process a single event of some sort. If there's no work to do, wait
00837  *      for an event to occur, then process it.
00838  *
00839  * Results:
00840  *      The return value is 1 if the function actually found an event to
00841  *      process. If no processing occurred, then 0 is returned (this can
00842  *      happen if the TCL_DONT_WAIT flag is set or if there are no event
00843  *      handlers to wait for in the set specified by flags).
00844  *
00845  * Side effects:
00846  *      May delay execution of process while waiting for an event, unless
00847  *      TCL_DONT_WAIT is set in the flags argument. Event sources are invoked
00848  *      to check for and queue events. Event handlers may produce arbitrary
00849  *      side effects.
00850  *
00851  *----------------------------------------------------------------------
00852  */
00853 
00854 int
00855 Tcl_DoOneEvent(
00856     int flags)                  /* Miscellaneous flag values: may be any
00857                                  * combination of TCL_DONT_WAIT,
00858                                  * TCL_WINDOW_EVENTS, TCL_FILE_EVENTS,
00859                                  * TCL_TIMER_EVENTS, TCL_IDLE_EVENTS, or
00860                                  * others defined by event sources. */
00861 {
00862     int result = 0, oldMode;
00863     EventSource *sourcePtr;
00864     Tcl_Time *timePtr;
00865     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
00866 
00867     /*
00868      * The first thing we do is to service any asynchronous event handlers.
00869      */
00870 
00871     if (Tcl_AsyncReady()) {
00872         (void) Tcl_AsyncInvoke(NULL, 0);
00873         return 1;
00874     }
00875 
00876     /*
00877      * No event flags is equivalent to TCL_ALL_EVENTS.
00878      */
00879 
00880     if ((flags & TCL_ALL_EVENTS) == 0) {
00881         flags |= TCL_ALL_EVENTS;
00882     }
00883 
00884     /*
00885      * Set the service mode to none so notifier event routines won't try to
00886      * service events recursively.
00887      */
00888 
00889     oldMode = tsdPtr->serviceMode;
00890     tsdPtr->serviceMode = TCL_SERVICE_NONE;
00891 
00892     /*
00893      * The core of this function is an infinite loop, even though we only
00894      * service one event. The reason for this is that we may be processing
00895      * events that don't do anything inside of Tcl.
00896      */
00897 
00898     while (1) {
00899         /*
00900          * If idle events are the only things to service, skip the main part
00901          * of the loop and go directly to handle idle events (i.e. don't wait
00902          * even if TCL_DONT_WAIT isn't set).
00903          */
00904 
00905         if ((flags & TCL_ALL_EVENTS) == TCL_IDLE_EVENTS) {
00906             flags = TCL_IDLE_EVENTS | TCL_DONT_WAIT;
00907             goto idleEvents;
00908         }
00909 
00910         /*
00911          * Ask Tcl to service a queued event, if there are any.
00912          */
00913 
00914         if (Tcl_ServiceEvent(flags)) {
00915             result = 1;
00916             break;
00917         }
00918 
00919         /*
00920          * If TCL_DONT_WAIT is set, be sure to poll rather than blocking,
00921          * otherwise reset the block time to infinity.
00922          */
00923 
00924         if (flags & TCL_DONT_WAIT) {
00925             tsdPtr->blockTime.sec = 0;
00926             tsdPtr->blockTime.usec = 0;
00927             tsdPtr->blockTimeSet = 1;
00928         } else {
00929             tsdPtr->blockTimeSet = 0;
00930         }
00931 
00932         /*
00933          * Set up all the event sources for new events. This will cause the
00934          * block time to be updated if necessary.
00935          */
00936 
00937         tsdPtr->inTraversal = 1;
00938         for (sourcePtr = tsdPtr->firstEventSourcePtr; sourcePtr != NULL;
00939                 sourcePtr = sourcePtr->nextPtr) {
00940             if (sourcePtr->setupProc) {
00941                 (sourcePtr->setupProc)(sourcePtr->clientData, flags);
00942             }
00943         }
00944         tsdPtr->inTraversal = 0;
00945 
00946         if ((flags & TCL_DONT_WAIT) || tsdPtr->blockTimeSet) {
00947             timePtr = &tsdPtr->blockTime;
00948         } else {
00949             timePtr = NULL;
00950         }
00951 
00952         /*
00953          * Wait for a new event or a timeout. If Tcl_WaitForEvent returns -1,
00954          * we should abort Tcl_DoOneEvent.
00955          */
00956 
00957         result = Tcl_WaitForEvent(timePtr);
00958         if (result < 0) {
00959             result = 0;
00960             break;
00961         }
00962 
00963         /*
00964          * Check all the event sources for new events.
00965          */
00966 
00967         for (sourcePtr = tsdPtr->firstEventSourcePtr; sourcePtr != NULL;
00968                 sourcePtr = sourcePtr->nextPtr) {
00969             if (sourcePtr->checkProc) {
00970                 (sourcePtr->checkProc)(sourcePtr->clientData, flags);
00971             }
00972         }
00973 
00974         /*
00975          * Check for events queued by the notifier or event sources.
00976          */
00977 
00978         if (Tcl_ServiceEvent(flags)) {
00979             result = 1;
00980             break;
00981         }
00982 
00983         /*
00984          * We've tried everything at this point, but nobody we know about had
00985          * anything to do. Check for idle events. If none, either quit or go
00986          * back to the top and try again.
00987          */
00988 
00989     idleEvents:
00990         if (flags & TCL_IDLE_EVENTS) {
00991             if (TclServiceIdle()) {
00992                 result = 1;
00993                 break;
00994             }
00995         }
00996         if (flags & TCL_DONT_WAIT) {
00997             break;
00998         }
00999 
01000         /*
01001          * If Tcl_WaitForEvent has returned 1, indicating that one system
01002          * event has been dispatched (and thus that some Tcl code might have
01003          * been indirectly executed), we break out of the loop. We do this to
01004          * give VwaitCmd for instance a chance to check if that system event
01005          * had the side effect of changing the variable (so the vwait can
01006          * return and unwind properly).
01007          *
01008          * NB: We will process idle events if any first, because otherwise we
01009          *     might never do the idle events if the notifier always gets
01010          *     system events.
01011          */
01012 
01013         if (result) {
01014             break;
01015         }
01016     }
01017 
01018     tsdPtr->serviceMode = oldMode;
01019     return result;
01020 }
01021 
01022 /*
01023  *----------------------------------------------------------------------
01024  *
01025  * Tcl_ServiceAll --
01026  *
01027  *      This routine checks all of the event sources, processes events that
01028  *      are on the Tcl event queue, and then calls the any idle handlers.
01029  *      Platform specific notifier callbacks that generate events should call
01030  *      this routine before returning to the system in order to ensure that
01031  *      Tcl gets a chance to process the new events.
01032  *
01033  * Results:
01034  *      Returns 1 if an event or idle handler was invoked, else 0.
01035  *
01036  * Side effects:
01037  *      Anything that an event or idle handler may do.
01038  *
01039  *----------------------------------------------------------------------
01040  */
01041 
01042 int
01043 Tcl_ServiceAll(void)
01044 {
01045     int result = 0;
01046     EventSource *sourcePtr;
01047     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
01048 
01049     if (tsdPtr->serviceMode == TCL_SERVICE_NONE) {
01050         return result;
01051     }
01052 
01053     /*
01054      * We need to turn off event servicing like we to in Tcl_DoOneEvent, to
01055      * avoid recursive calls.
01056      */
01057 
01058     tsdPtr->serviceMode = TCL_SERVICE_NONE;
01059 
01060     /*
01061      * Check async handlers first.
01062      */
01063 
01064     if (Tcl_AsyncReady()) {
01065         (void) Tcl_AsyncInvoke(NULL, 0);
01066     }
01067 
01068     /*
01069      * Make a single pass through all event sources, queued events, and idle
01070      * handlers. Note that we wait to update the notifier timer until the end
01071      * so we can avoid multiple changes.
01072      */
01073 
01074     tsdPtr->inTraversal = 1;
01075     tsdPtr->blockTimeSet = 0;
01076 
01077     for (sourcePtr = tsdPtr->firstEventSourcePtr; sourcePtr != NULL;
01078             sourcePtr = sourcePtr->nextPtr) {
01079         if (sourcePtr->setupProc) {
01080             (sourcePtr->setupProc)(sourcePtr->clientData, TCL_ALL_EVENTS);
01081         }
01082     }
01083     for (sourcePtr = tsdPtr->firstEventSourcePtr; sourcePtr != NULL;
01084             sourcePtr = sourcePtr->nextPtr) {
01085         if (sourcePtr->checkProc) {
01086             (sourcePtr->checkProc)(sourcePtr->clientData, TCL_ALL_EVENTS);
01087         }
01088     }
01089 
01090     while (Tcl_ServiceEvent(0)) {
01091         result = 1;
01092     }
01093     if (TclServiceIdle()) {
01094         result = 1;
01095     }
01096 
01097     if (!tsdPtr->blockTimeSet) {
01098         Tcl_SetTimer(NULL);
01099     } else {
01100         Tcl_SetTimer(&tsdPtr->blockTime);
01101     }
01102     tsdPtr->inTraversal = 0;
01103     tsdPtr->serviceMode = TCL_SERVICE_ALL;
01104     return result;
01105 }
01106 
01107 /*
01108  *----------------------------------------------------------------------
01109  *
01110  * Tcl_ThreadAlert --
01111  *
01112  *      This function wakes up the notifier associated with the specified
01113  *      thread (if there is one).
01114  *
01115  * Results:
01116  *      None.
01117  *
01118  * Side effects:
01119  *      None.
01120  *
01121  *----------------------------------------------------------------------
01122  */
01123 
01124 void
01125 Tcl_ThreadAlert(
01126     Tcl_ThreadId threadId)      /* Identifier for thread to use. */
01127 {
01128     ThreadSpecificData *tsdPtr;
01129 
01130     /*
01131      * Find the notifier associated with the specified thread. Note that we
01132      * need to hold the listLock while calling Tcl_AlertNotifier to avoid a
01133      * race condition where the specified thread might destroy its notifier.
01134      */
01135 
01136     Tcl_MutexLock(&listLock);
01137     for (tsdPtr = firstNotifierPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) {
01138         if (tsdPtr->threadId == threadId) {
01139             if (tclStubs.tcl_AlertNotifier) {
01140                 tclStubs.tcl_AlertNotifier(tsdPtr->clientData);
01141             }
01142             break;
01143         }
01144     }
01145     Tcl_MutexUnlock(&listLock);
01146 }
01147 
01148 /*
01149  * Local Variables:
01150  * mode: c
01151  * c-basic-offset: 4
01152  * fill-column: 78
01153  * End:
01154  */



Generated on Wed Mar 12 12:18:19 2008 by  doxygen 1.5.1