diff options
Diffstat (limited to 'generic/tclNotify.c')
-rw-r--r-- | generic/tclNotify.c | 508 |
1 files changed, 337 insertions, 171 deletions
diff --git a/generic/tclNotify.c b/generic/tclNotify.c index 13c5a13..15553b9 100644 --- a/generic/tclNotify.c +++ b/generic/tclNotify.c @@ -13,19 +13,13 @@ * 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.3 1998/10/14 21:12:09 rjohnson Exp $ + * RCS: @(#) $Id: tclNotify.c,v 1.4 1999/04/16 00:46:50 stanton Exp $ */ #include "tclInt.h" #include "tclPort.h" /* - * The following static indicates whether this module has been initialized. - */ - -static int initialized = 0; - -/* * For each event source (created with Tcl_CreateEventSource) there * is a structure of the following type: */ @@ -38,21 +32,25 @@ typedef struct EventSource { } EventSource; /* - * The following structure keeps track of the state of the notifier. - * 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). + * 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. */ -static struct { +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 @@ -63,63 +61,103 @@ static struct { * called during an event source traversal. */ EventSource *firstEventSourcePtr; /* Pointer to first event source in - * global list of event sources. */ -} notifier; + * 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; /* - * Declarations for functions used in this file. + * 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 void InitNotifier _ANSI_ARGS_((void)); -static void NotifierExitHandler _ANSI_ARGS_((ClientData clientData)); +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)); /* *---------------------------------------------------------------------- * - * InitNotifier -- + * TclInitNotifier -- * - * This routine is called to initialize the notifier module. + * Initialize the thread local data structures for the notifier + * subsystem. * * Results: * None. * * Side effects: - * Creates an exit handler and initializes static data. + * Adds the current thread to the global list of notifiers. * *---------------------------------------------------------------------- */ -static void -InitNotifier() +void +TclInitNotifier() { - initialized = 1; - memset(¬ifier, 0, sizeof(notifier)); - notifier.serviceMode = TCL_SERVICE_NONE; - Tcl_CreateExitHandler(NotifierExitHandler, NULL); + 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); } /* *---------------------------------------------------------------------- * - * NotifierExitHandler -- + * TclFinalizeNotifier -- * - * This routine is called during Tcl finalization. + * Finalize the thread local data structures for the notifier + * subsystem. * * Results: - * None. + * None. * * Side effects: - * Clears the notifier intialization flag. + * Removes the notifier associated with the current thread from + * the global notifier list. * *---------------------------------------------------------------------- */ -static void -NotifierExitHandler(clientData) - ClientData clientData; /* Not used. */ +void +TclFinalizeNotifier() { - initialized = 0; + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + ThreadSpecificData **prevPtrPtr; + + Tcl_MutexLock(&listLock); + + Tcl_FinalizeNotifier(tsdPtr->clientData); + TclFinalizeMutex(&(tsdPtr->queueMutex)); + for (prevPtrPtr = &firstNotifierPtr; *prevPtrPtr != NULL; + prevPtrPtr = &((*prevPtrPtr)->nextPtr)) { + if (*prevPtrPtr == tsdPtr) { + *prevPtrPtr = tsdPtr->nextPtr; + break; + } + } + + Tcl_MutexUnlock(&listLock); } /* @@ -140,12 +178,12 @@ NotifierExitHandler(clientData) * 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_WatchFile - * or Tcl_SetMaxBlockTime to indicate what to wait for. + * 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_FileReady), and then calls + * 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. @@ -167,18 +205,14 @@ Tcl_CreateEventSource(setupProc, checkProc, clientData) ClientData clientData; /* One-word argument to pass to * setupProc and checkProc. */ { - EventSource *sourcePtr; - - if (!initialized) { - InitNotifier(); - } + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + EventSource *sourcePtr = (EventSource *) ckalloc(sizeof(EventSource)); - sourcePtr = (EventSource *) ckalloc(sizeof(EventSource)); sourcePtr->setupProc = setupProc; sourcePtr->checkProc = checkProc; sourcePtr->clientData = clientData; - sourcePtr->nextPtr = notifier.firstEventSourcePtr; - notifier.firstEventSourcePtr = sourcePtr; + sourcePtr->nextPtr = tsdPtr->firstEventSourcePtr; + tsdPtr->firstEventSourcePtr = sourcePtr; } /* @@ -209,9 +243,10 @@ Tcl_DeleteEventSource(setupProc, checkProc, clientData) ClientData clientData; /* One-word argument to pass to * setupProc and checkProc. */ { + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); EventSource *sourcePtr, *prevPtr; - for (sourcePtr = notifier.firstEventSourcePtr, prevPtr = NULL; + for (sourcePtr = tsdPtr->firstEventSourcePtr, prevPtr = NULL; sourcePtr != NULL; prevPtr = sourcePtr, sourcePtr = sourcePtr->nextPtr) { if ((sourcePtr->setupProc != setupProc) @@ -220,7 +255,7 @@ Tcl_DeleteEventSource(setupProc, checkProc, clientData) continue; } if (prevPtr == NULL) { - notifier.firstEventSourcePtr = sourcePtr->nextPtr; + tsdPtr->firstEventSourcePtr = sourcePtr->nextPtr; } else { prevPtr->nextPtr = sourcePtr->nextPtr; } @@ -234,12 +269,8 @@ Tcl_DeleteEventSource(setupProc, checkProc, clientData) * * Tcl_QueueEvent -- * - * Insert an event into the Tk 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. + * Queue an event on the event queue associated with the + * current thread. * * Results: * None. @@ -261,50 +292,136 @@ Tcl_QueueEvent(evPtr, position) Tcl_QueuePosition position; /* One of TCL_QUEUE_TAIL, TCL_QUEUE_HEAD, * TCL_QUEUE_MARK. */ { - if (!initialized) { - InitNotifier(); + 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 (notifier.firstEventPtr == NULL) { - notifier.firstEventPtr = evPtr; + if (tsdPtr->firstEventPtr == NULL) { + tsdPtr->firstEventPtr = evPtr; } else { - notifier.lastEventPtr->nextPtr = evPtr; + tsdPtr->lastEventPtr->nextPtr = evPtr; } - notifier.lastEventPtr = evPtr; + tsdPtr->lastEventPtr = evPtr; } else if (position == TCL_QUEUE_HEAD) { /* * Push the event on the head of the queue. */ - evPtr->nextPtr = notifier.firstEventPtr; - if (notifier.firstEventPtr == NULL) { - notifier.lastEventPtr = evPtr; + evPtr->nextPtr = tsdPtr->firstEventPtr; + if (tsdPtr->firstEventPtr == NULL) { + tsdPtr->lastEventPtr = evPtr; } - notifier.firstEventPtr = 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 (notifier.markerEventPtr == NULL) { - evPtr->nextPtr = notifier.firstEventPtr; - notifier.firstEventPtr = evPtr; + if (tsdPtr->markerEventPtr == NULL) { + evPtr->nextPtr = tsdPtr->firstEventPtr; + tsdPtr->firstEventPtr = evPtr; } else { - evPtr->nextPtr = notifier.markerEventPtr->nextPtr; - notifier.markerEventPtr->nextPtr = evPtr; + evPtr->nextPtr = tsdPtr->markerEventPtr->nextPtr; + tsdPtr->markerEventPtr->nextPtr = evPtr; } - notifier.markerEventPtr = evPtr; + tsdPtr->markerEventPtr = evPtr; if (evPtr->nextPtr == NULL) { - notifier.lastEventPtr = evPtr; + tsdPtr->lastEventPtr = evPtr; } } + Tcl_MutexUnlock(&(tsdPtr->queueMutex)); } /* @@ -314,7 +431,8 @@ Tcl_QueueEvent(evPtr, position) * * 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. + * procedure returns 0 are left in the queue. Operates on the + * queue associated with the current thread. * * Results: * None. @@ -331,22 +449,20 @@ Tcl_DeleteEvents(proc, clientData) ClientData clientData; /* type-specific data. */ { Tcl_Event *evPtr, *prevPtr, *hold; + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); - if (!initialized) { - InitNotifier(); - } - - for (prevPtr = (Tcl_Event *) NULL, evPtr = notifier.firstEventPtr; + Tcl_MutexLock(&(tsdPtr->queueMutex)); + for (prevPtr = (Tcl_Event *) NULL, evPtr = tsdPtr->firstEventPtr; evPtr != (Tcl_Event *) NULL; ) { if ((*proc) (evPtr, clientData) == 1) { - if (notifier.firstEventPtr == evPtr) { - notifier.firstEventPtr = evPtr->nextPtr; - if (evPtr->nextPtr == NULL) { - notifier.lastEventPtr = prevPtr; + if (tsdPtr->firstEventPtr == evPtr) { + tsdPtr->firstEventPtr = evPtr->nextPtr; + if (evPtr->nextPtr == (Tcl_Event *) NULL) { + tsdPtr->lastEventPtr = prevPtr; } - if (notifier.markerEventPtr == evPtr) { - notifier.markerEventPtr = prevPtr; + if (tsdPtr->markerEventPtr == evPtr) { + tsdPtr->markerEventPtr = prevPtr; } } else { prevPtr->nextPtr = evPtr->nextPtr; @@ -359,6 +475,7 @@ Tcl_DeleteEvents(proc, clientData) evPtr = evPtr->nextPtr; } } + Tcl_MutexUnlock(&(tsdPtr->queueMutex)); } /* @@ -367,7 +484,8 @@ Tcl_DeleteEvents(proc, clientData) * Tcl_ServiceEvent -- * * Process one event from the event queue, or invoke an - * asynchronous event handler. + * asynchronous event handler. Operates on event queue for + * current thread. * * Results: * The return value is 1 if the procedure actually found an event @@ -392,10 +510,8 @@ Tcl_ServiceEvent(flags) { Tcl_Event *evPtr, *prevPtr; Tcl_EventProc *proc; - - if (!initialized) { - InitNotifier(); - } + int result; + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); /* * Asynchronous event handlers are considered to be the highest @@ -421,12 +537,13 @@ Tcl_ServiceEvent(flags) * that can actually be handled. */ - for (evPtr = notifier.firstEventPtr; evPtr != NULL; + 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, but stemming from the fact that the event + * 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 @@ -440,30 +557,57 @@ Tcl_ServiceEvent(flags) */ proc = evPtr->proc; + if (proc == NULL) { + continue; + } evPtr->proc = NULL; - if ((proc != NULL) && (*proc)(evPtr, flags)) { - if (notifier.firstEventPtr == evPtr) { - notifier.firstEventPtr = evPtr->nextPtr; + + /* + * 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) { - notifier.lastEventPtr = NULL; + tsdPtr->lastEventPtr = NULL; } - if (notifier.markerEventPtr == evPtr) { - notifier.markerEventPtr = NULL; + if (tsdPtr->markerEventPtr == evPtr) { + tsdPtr->markerEventPtr = NULL; } } else { - for (prevPtr = notifier.firstEventPtr; - prevPtr->nextPtr != evPtr; prevPtr = prevPtr->nextPtr) { + for (prevPtr = tsdPtr->firstEventPtr; + prevPtr && prevPtr->nextPtr != evPtr; + prevPtr = prevPtr->nextPtr) { /* Empty loop body. */ } - prevPtr->nextPtr = evPtr->nextPtr; - if (evPtr->nextPtr == NULL) { - notifier.lastEventPtr = prevPtr; - } - if (notifier.markerEventPtr == evPtr) { - notifier.markerEventPtr = prevPtr; + if (prevPtr) { + prevPtr->nextPtr = evPtr->nextPtr; + if (evPtr->nextPtr == NULL) { + tsdPtr->lastEventPtr = prevPtr; + } + if (tsdPtr->markerEventPtr == evPtr) { + tsdPtr->markerEventPtr = prevPtr; + } + } else { + evPtr = NULL; } } - ckfree((char *) evPtr); + if (evPtr) { + ckfree((char *) evPtr); + } + Tcl_MutexUnlock(&(tsdPtr->queueMutex)); return 1; } else { /* @@ -473,14 +617,8 @@ Tcl_ServiceEvent(flags) evPtr->proc = proc; } - - /* - * The handler for this event asked to defer it. Just go on to - * the next event. - */ - - continue; } + Tcl_MutexUnlock(&(tsdPtr->queueMutex)); return 0; } @@ -503,11 +641,9 @@ Tcl_ServiceEvent(flags) int Tcl_GetServiceMode() { - if (!initialized) { - InitNotifier(); - } + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); - return notifier.serviceMode; + return tsdPtr->serviceMode; } /* @@ -515,13 +651,13 @@ Tcl_GetServiceMode() * * Tcl_SetServiceMode -- * - * This routine sets the current service mode of the notifier. + * This routine sets the current service mode of the tsdPtr-> * * Results: * Returns the previous service mode. * * Side effects: - * None. + * Invokes the notifier service mode hook procedure. * *---------------------------------------------------------------------- */ @@ -532,13 +668,11 @@ Tcl_SetServiceMode(mode) * TCL_SERVICE_NONE */ { int oldMode; + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); - if (!initialized) { - InitNotifier(); - } - - oldMode = notifier.serviceMode; - notifier.serviceMode = mode; + oldMode = tsdPtr->serviceMode; + tsdPtr->serviceMode = mode; + Tcl_ServiceModeHook(mode); return oldMode; } @@ -556,7 +690,7 @@ Tcl_SetServiceMode(mode) * None. * * Side effects: - * May reduce the length of the next sleep in the notifier. + * May reduce the length of the next sleep in the tsdPtr-> * *---------------------------------------------------------------------- */ @@ -565,17 +699,15 @@ void Tcl_SetMaxBlockTime(timePtr) Tcl_Time *timePtr; /* Specifies a maximum elapsed time for * the next blocking operation in the - * event notifier. */ + * event tsdPtr-> */ { - if (!initialized) { - InitNotifier(); - } + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); - if (!notifier.blockTimeSet || (timePtr->sec < notifier.blockTime.sec) - || ((timePtr->sec == notifier.blockTime.sec) - && (timePtr->usec < notifier.blockTime.usec))) { - notifier.blockTime = *timePtr; - notifier.blockTimeSet = 1; + if (!tsdPtr->blockTimeSet || (timePtr->sec < tsdPtr->blockTime.sec) + || ((timePtr->sec == tsdPtr->blockTime.sec) + && (timePtr->usec < tsdPtr->blockTime.usec))) { + tsdPtr->blockTime = *timePtr; + tsdPtr->blockTimeSet = 1; } /* @@ -583,9 +715,9 @@ Tcl_SetMaxBlockTime(timePtr) * timeout immediately. */ - if (!notifier.inTraversal) { - if (notifier.blockTimeSet) { - Tcl_SetTimer(¬ifier.blockTime); + if (!tsdPtr->inTraversal) { + if (tsdPtr->blockTimeSet) { + Tcl_SetTimer(&tsdPtr->blockTime); } else { Tcl_SetTimer(NULL); } @@ -626,10 +758,7 @@ Tcl_DoOneEvent(flags) int result = 0, oldMode; EventSource *sourcePtr; Tcl_Time *timePtr; - - if (!initialized) { - InitNotifier(); - } + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); /* * The first thing we do is to service any asynchronous event @@ -654,8 +783,8 @@ Tcl_DoOneEvent(flags) * try to service events recursively. */ - oldMode = notifier.serviceMode; - notifier.serviceMode = TCL_SERVICE_NONE; + oldMode = tsdPtr->serviceMode; + tsdPtr->serviceMode = TCL_SERVICE_NONE; /* * The core of this procedure is an infinite loop, even though @@ -691,11 +820,11 @@ Tcl_DoOneEvent(flags) */ if (flags & TCL_DONT_WAIT) { - notifier.blockTime.sec = 0; - notifier.blockTime.usec = 0; - notifier.blockTimeSet = 1; + tsdPtr->blockTime.sec = 0; + tsdPtr->blockTime.usec = 0; + tsdPtr->blockTimeSet = 1; } else { - notifier.blockTimeSet = 0; + tsdPtr->blockTimeSet = 0; } /* @@ -703,17 +832,17 @@ Tcl_DoOneEvent(flags) * cause the block time to be updated if necessary. */ - notifier.inTraversal = 1; - for (sourcePtr = notifier.firstEventSourcePtr; sourcePtr != NULL; + tsdPtr->inTraversal = 1; + for (sourcePtr = tsdPtr->firstEventSourcePtr; sourcePtr != NULL; sourcePtr = sourcePtr->nextPtr) { if (sourcePtr->setupProc) { (sourcePtr->setupProc)(sourcePtr->clientData, flags); } } - notifier.inTraversal = 0; + tsdPtr->inTraversal = 0; - if ((flags & TCL_DONT_WAIT) || notifier.blockTimeSet) { - timePtr = ¬ifier.blockTime; + if ((flags & TCL_DONT_WAIT) || tsdPtr->blockTimeSet) { + timePtr = &tsdPtr->blockTime; } else { timePtr = NULL; } @@ -733,7 +862,7 @@ Tcl_DoOneEvent(flags) * Check all the event sources for new events. */ - for (sourcePtr = notifier.firstEventSourcePtr; sourcePtr != NULL; + for (sourcePtr = tsdPtr->firstEventSourcePtr; sourcePtr != NULL; sourcePtr = sourcePtr->nextPtr) { if (sourcePtr->checkProc) { (sourcePtr->checkProc)(sourcePtr->clientData, flags); @@ -786,7 +915,7 @@ Tcl_DoOneEvent(flags) } - notifier.serviceMode = oldMode; + tsdPtr->serviceMode = oldMode; return result; } @@ -816,12 +945,9 @@ Tcl_ServiceAll() { int result = 0; EventSource *sourcePtr; + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); - if (!initialized) { - InitNotifier(); - } - - if (notifier.serviceMode == TCL_SERVICE_NONE) { + if (tsdPtr->serviceMode == TCL_SERVICE_NONE) { return result; } @@ -830,7 +956,7 @@ Tcl_ServiceAll() * to avoid recursive calls. */ - notifier.serviceMode = TCL_SERVICE_NONE; + tsdPtr->serviceMode = TCL_SERVICE_NONE; /* * Check async handlers first. @@ -846,16 +972,16 @@ Tcl_ServiceAll() * timer until the end so we can avoid multiple changes. */ - notifier.inTraversal = 1; - notifier.blockTimeSet = 0; + tsdPtr->inTraversal = 1; + tsdPtr->blockTimeSet = 0; - for (sourcePtr = notifier.firstEventSourcePtr; sourcePtr != NULL; + for (sourcePtr = tsdPtr->firstEventSourcePtr; sourcePtr != NULL; sourcePtr = sourcePtr->nextPtr) { if (sourcePtr->setupProc) { (sourcePtr->setupProc)(sourcePtr->clientData, TCL_ALL_EVENTS); } } - for (sourcePtr = notifier.firstEventSourcePtr; sourcePtr != NULL; + for (sourcePtr = tsdPtr->firstEventSourcePtr; sourcePtr != NULL; sourcePtr = sourcePtr->nextPtr) { if (sourcePtr->checkProc) { (sourcePtr->checkProc)(sourcePtr->clientData, TCL_ALL_EVENTS); @@ -869,12 +995,52 @@ Tcl_ServiceAll() result = 1; } - if (!notifier.blockTimeSet) { + if (!tsdPtr->blockTimeSet) { Tcl_SetTimer(NULL); } else { - Tcl_SetTimer(¬ifier.blockTime); + Tcl_SetTimer(&tsdPtr->blockTime); } - notifier.inTraversal = 0; - notifier.serviceMode = TCL_SERVICE_ALL; + 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); +} |