Tcl package Thread source code

Artifact [64a382bde4]
Login

Artifact 64a382bde40fbff72ae365aab44d95d6226282af72f1ce599aa277d5a37b0e64:

Attachment "tclNotify.patch" to ticket [9e69e2c602] added by chw 2024-12-06 08:21:26.
Index: generic/tclNotify.c
==================================================================
--- generic/tclNotify.c
+++ generic/tclNotify.c
@@ -55,12 +55,14 @@
 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. */
+    int eventCount;		/* Number of entries, but refer to comments in
+				 * Tcl_ServiceEvent(). */
     Tcl_Mutex queueMutex;	/* Mutex to protect access to the previous
-				 * three fields. */
+				 * four fields. */
     int serviceMode;		/* One of TCL_SERVICE_NONE or
 				 * TCL_SERVICE_ALL. */
     int blockTimeSet;		/* 0 means there is no maximum block time:
 				 * block forever. */
     Tcl_Time blockTime;		/* If blockTimeSet is 1, gives the maximum
@@ -184,10 +186,11 @@
 	evPtr = evPtr->nextPtr;
 	Tcl_Free(hold);
     }
     tsdPtr->firstEventPtr = NULL;
     tsdPtr->lastEventPtr = NULL;
+    tsdPtr->eventCount = 0;
     Tcl_MutexUnlock(&(tsdPtr->queueMutex));
 
     Tcl_MutexLock(&listLock);
 
     Tcl_FinalizeNotifier(tsdPtr->clientData);
@@ -484,14 +487,13 @@
 				 * property of the event queue. It will be
 				 * freed after the event has been handled. */
     int position) /* One of TCL_QUEUE_TAIL, TCL_QUEUE_HEAD, TCL_QUEUE_MARK,
 				 * possibly combined with TCL_QUEUE_ALERT_IF_EMPTY */
 {
+    int wasEmpty = 0;
+
     Tcl_MutexLock(&(tsdPtr->queueMutex));
-    if (tsdPtr->firstEventPtr != NULL) {
-	position &= ~TCL_QUEUE_ALERT_IF_EMPTY;
-    }
     if ((position & 3) == TCL_QUEUE_TAIL) {
 	/*
 	 * Append the event on the end of the queue.
 	 */
 
@@ -528,12 +530,16 @@
 	tsdPtr->markerEventPtr = evPtr;
 	if (evPtr->nextPtr == NULL) {
 	    tsdPtr->lastEventPtr = evPtr;
 	}
     }
+    if (position & TCL_QUEUE_ALERT_IF_EMPTY) {
+	wasEmpty = (tsdPtr->eventCount <= 0);
+    }
+    tsdPtr->eventCount++;
     Tcl_MutexUnlock(&(tsdPtr->queueMutex));
-    return position & TCL_QUEUE_ALERT_IF_EMPTY;
+    return wasEmpty;
 }
 
 /*
  *----------------------------------------------------------------------
  *
@@ -602,10 +608,11 @@
 	     */
 
 	    hold = evPtr;
 	    evPtr = evPtr->nextPtr;
 	    Tcl_Free(hold);
+	    tsdPtr->eventCount--;
 	} else {
 	    /*
 	     * Event is to be retained.
 	     */
 
@@ -645,11 +652,11 @@
 				 * matching this will be skipped for
 				 * processing later. */
 {
     Tcl_Event *evPtr, *prevPtr;
     Tcl_EventProc *proc;
-    int result;
+    int result, eventCount;
     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
 
     /*
      * Asynchronous event handlers are considered to be the highest priority
      * events, and so must be invoked before we process events on the event
@@ -702,15 +709,23 @@
 	/*
 	 * Release the lock before calling the event function. 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.
+	 *
+	 * The eventCount is remembered and set to zero that the next
+	 * level of Tcl_ServiceEvent() gets an empty condition for the
+	 * Tcl_ThreadQueueEvent() to perform optional wakeups.
+	 * On exit of the next level, the eventCount is readjusted.
 	 */
 
+	eventCount = tsdPtr->eventCount;
+	tsdPtr->eventCount = 0;
 	Tcl_MutexUnlock(&(tsdPtr->queueMutex));
 	result = proc(evPtr, flags);
 	Tcl_MutexLock(&(tsdPtr->queueMutex));
+	tsdPtr->eventCount += eventCount;
 
 	if (result) {
 	    /*
 	     * The event was processed, so remove it from the queue.
 	     */
@@ -741,10 +756,11 @@
 		    evPtr = NULL;
 		}
 	    }
 	    if (evPtr) {
 		Tcl_Free(evPtr);
+		tsdPtr->eventCount--;
 	    }
 	    Tcl_MutexUnlock(&(tsdPtr->queueMutex));
 	    return 1;
 	} else {
 	    /*