summaryrefslogtreecommitdiff
path: root/tcl/generic/tclNotify.c
diff options
context:
space:
mode:
Diffstat (limited to 'tcl/generic/tclNotify.c')
-rw-r--r--tcl/generic/tclNotify.c537
1 files changed, 369 insertions, 168 deletions
diff --git a/tcl/generic/tclNotify.c b/tcl/generic/tclNotify.c
index 67bf7bfa4e1..2c386ab9e7c 100644
--- a/tcl/generic/tclNotify.c
+++ b/tcl/generic/tclNotify.c
@@ -19,11 +19,7 @@
#include "tclInt.h"
#include "tclPort.h"
-/*
- * The following static indicates whether this module has been initialized.
- */
-
-static int initialized = 0;
+extern TclStubs tclStubs;
/*
* For each event source (created with Tcl_CreateEventSource) there
@@ -38,21 +34,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 +63,135 @@ 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(&notifier, 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.
+ *
+ * Side effects:
+ * Removes the notifier associated with the current thread from
+ * the global notifier list.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TclFinalizeNotifier()
+{
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+ ThreadSpecificData **prevPtrPtr;
+
+ Tcl_MutexLock(&listLock);
+
+ Tcl_FinalizeNotifier(tsdPtr->clientData);
+ Tcl_MutexFinalize(&(tsdPtr->queueMutex));
+ for (prevPtrPtr = &firstNotifierPtr; *prevPtrPtr != NULL;
+ prevPtrPtr = &((*prevPtrPtr)->nextPtr)) {
+ if (*prevPtrPtr == tsdPtr) {
+ *prevPtrPtr = tsdPtr->nextPtr;
+ break;
+ }
+ }
+
+ Tcl_MutexUnlock(&listLock);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_SetNotifier --
+ *
+ * Install a set of alternate functions for use with the notifier.
+ # In particular, this can be used to install the Xt-based
+ * notifier for use with the Browser plugin.
*
* Results:
* None.
*
* Side effects:
- * Clears the notifier intialization flag.
+ * Overstomps part of the stub vector. This relies on hooks
+ * added to the default procedures in case those are called
+ * directly (i.e., not through the stub table.)
*
*----------------------------------------------------------------------
*/
-static void
-NotifierExitHandler(clientData)
- ClientData clientData; /* Not used. */
+void
+Tcl_SetNotifier(notifierProcPtr)
+ Tcl_NotifierProcs *notifierProcPtr;
{
- initialized = 0;
+#if !defined(__WIN32__) && !defined(MAC_TCL) /* UNIX */
+ tclStubs.tcl_CreateFileHandler = notifierProcPtr->createFileHandlerProc;
+ tclStubs.tcl_DeleteFileHandler = notifierProcPtr->deleteFileHandlerProc;
+#endif
+ tclStubs.tcl_SetTimer = notifierProcPtr->setTimerProc;
+ tclStubs.tcl_WaitForEvent = notifierProcPtr->waitForEventProc;
}
/*
@@ -140,12 +212,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 +239,14 @@ Tcl_CreateEventSource(setupProc, checkProc, clientData)
ClientData clientData; /* One-word argument to pass to
* setupProc and checkProc. */
{
- EventSource *sourcePtr;
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+ EventSource *sourcePtr = (EventSource *) ckalloc(sizeof(EventSource));
- if (!initialized) {
- InitNotifier();
- }
-
- 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 +277,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 +289,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 +303,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 +326,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 +465,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 +483,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 +509,7 @@ Tcl_DeleteEvents(proc, clientData)
evPtr = evPtr->nextPtr;
}
}
+ Tcl_MutexUnlock(&(tsdPtr->queueMutex));
}
/*
@@ -367,7 +518,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 +544,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 +571,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 +591,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 +651,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 +675,9 @@ Tcl_ServiceEvent(flags)
int
Tcl_GetServiceMode()
{
- if (!initialized) {
- InitNotifier();
- }
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
- return notifier.serviceMode;
+ return tsdPtr->serviceMode;
}
/*
@@ -515,13 +685,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 +702,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 +724,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 +733,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 +749,9 @@ Tcl_SetMaxBlockTime(timePtr)
* timeout immediately.
*/
- if (!notifier.inTraversal) {
- if (notifier.blockTimeSet) {
- Tcl_SetTimer(&notifier.blockTime);
+ if (!tsdPtr->inTraversal) {
+ if (tsdPtr->blockTimeSet) {
+ Tcl_SetTimer(&tsdPtr->blockTime);
} else {
Tcl_SetTimer(NULL);
}
@@ -626,10 +792,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 +817,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 +854,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 +866,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 = &notifier.blockTime;
+ if ((flags & TCL_DONT_WAIT) || tsdPtr->blockTimeSet) {
+ timePtr = &tsdPtr->blockTime;
} else {
timePtr = NULL;
}
@@ -733,7 +896,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 +949,7 @@ Tcl_DoOneEvent(flags)
}
- notifier.serviceMode = oldMode;
+ tsdPtr->serviceMode = oldMode;
return result;
}
@@ -816,12 +979,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 +990,7 @@ Tcl_ServiceAll()
* to avoid recursive calls.
*/
- notifier.serviceMode = TCL_SERVICE_NONE;
+ tsdPtr->serviceMode = TCL_SERVICE_NONE;
/*
* Check async handlers first.
@@ -846,16 +1006,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 +1029,53 @@ Tcl_ServiceAll()
result = 1;
}
- if (!notifier.blockTimeSet) {
+ if (!tsdPtr->blockTimeSet) {
Tcl_SetTimer(NULL);
} else {
- Tcl_SetTimer(&notifier.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);
+}
+