Tcl Source Code

Artifact [470d2ba492]
Login

Artifact 470d2ba4925df1d5defaf8c63df74abc91aff515c7b48f707033a8e09a97f9d0:

Attachment "tip609.diff" to ticket [236d660fd8] added by chw 2021-08-11 12:23:47.
Index: doc/Notifier.3
==================================================================
--- doc/Notifier.3
+++ doc/Notifier.3
@@ -90,11 +90,13 @@
 .AP Tcl_Event *evPtr in
 An event to add to the event queue.  The storage for the event must
 have been allocated by the caller using \fBTcl_Alloc\fR or \fBckalloc\fR.
 .AP Tcl_QueuePosition position in
 Where to add the new event in the queue:  \fBTCL_QUEUE_TAIL\fR,
-\fBTCL_QUEUE_HEAD\fR, or \fBTCL_QUEUE_MARK\fR.
+\fBTCL_QUEUE_HEAD\fR, \fBTCL_QUEUE_MARK\fR,
+\fBTCL_QUEUE_TAIL_ALERT_IF_EMPTY\fR, or
+\fBTCL_QUEUE_HEAD_ALERT_IF_EMPTY\fR.
 .AP Tcl_ThreadId threadId in
 A unique identifier for a thread.
 .AP Tcl_EventDeleteProc *deleteProc in
 Procedure to invoke for each queued event in \fBTcl_DeleteEvents\fR.
 .AP int flags in
@@ -338,25 +340,33 @@
 The \fInextPtr\fR is used to link together the events in the queue
 and should not be modified by the event source.
 .PP
 An event may be added to the queue at any of three positions, depending
 on the \fIposition\fR argument to \fBTcl_QueueEvent\fR:
-.IP \fBTCL_QUEUE_TAIL\fR 24
+.IP \fBTCL_QUEUE_TAIL\fR 32
 Add the event at the back of the queue, so that all other pending
 events will be serviced first.  This is almost always the right
 place for new events.
-.IP \fBTCL_QUEUE_HEAD\fR 24
+.IP \fBTCL_QUEUE_HEAD\fR 32
 Add the event at the front of the queue, so that it will be serviced
 before all other queued events.
-.IP \fBTCL_QUEUE_MARK\fR 24
+.IP \fBTCL_QUEUE_MARK\fR 32
 Add the event at the front of the queue, unless there are other
 events at the front whose position is \fBTCL_QUEUE_MARK\fR;  if so,
 add the new event just after all other \fBTCL_QUEUE_MARK\fR events.
 This value of \fIposition\fR is used to insert an ordered sequence of
 events at the front of the queue, such as a series of
 Enter and Leave events synthesized during a grab or ungrab operation
 in Tk.
+.IP \fBTCL_QUEUE_TAIL_ALERT_IF_EMPTY\fR 32
+Like \fBTCL_QUEUE_TAIL\fR but when used in \fBTcl_ThreadQueueEvent\fR
+arranges for an automatic call of \fBTcl_ThreadAlert\fR when the queue was
+empty.
+.IP \fBTCL_QUEUE_HEAD_ALERT_IF_EMPTY\fR 32
+Like \fBTCL_QUEUE_HEAD\fR but when used in \fBTcl_ThreadQueueEvent\fR
+arranges for an automatic call of \fBTcl_ThreadAlert\fR when the queue was
+empty.
 .PP
 When it is time to handle an event from the queue (steps 1 and 4
 above) \fBTcl_ServiceEvent\fR will invoke the \fIproc\fR specified
 in the first queued \fBTcl_Event\fR structure.
 \fIProc\fR must match the following prototype:
@@ -406,11 +416,14 @@
 procedure.  (A thread would then need to pass this identifier to other
 threads for those threads to be able to add events to its queue.)
 After adding an event to another thread's queue, you then typically
 need to call \fBTcl_ThreadAlert\fR to
 .QW "wake up"
-that thread's notifier to alert it to the new event.
+that thread's notifier to alert it to the new event. Alternatively,
+the queue positions \fBTCL_QUEUE_TAIL_ALERT_IF_EMPTY\fR and
+\fBTCL_QUEUE_HEAD_ALERT_IF_EMPTY\fR can be used which automatically
+call \fBTcl_ThreadAlert\fR if the thread's queue was empty.
 .PP
 \fBTcl_DeleteEvents\fR can be used to explicitly remove one or more
 events from the event queue.  \fBTcl_DeleteEvents\fR calls \fIproc\fR
 for each event in the queue, deleting those for with the procedure
 returns 1.  Events for which the procedure returns 0 are left in the

Index: generic/tcl.h
==================================================================
--- generic/tcl.h
+++ generic/tcl.h
@@ -1334,10 +1334,27 @@
 
 typedef enum {
     TCL_QUEUE_TAIL, TCL_QUEUE_HEAD, TCL_QUEUE_MARK
 } Tcl_QueuePosition;
 
+/*
+ * Positions for Tcl_ThreadQueueEvent:
+ */
+
+typedef enum {
+    TCL_QUEUE_TAIL_EX = TCL_QUEUE_TAIL,
+    TCL_QUEUE_HEAD_EX = TCL_QUEUE_HEAD,
+    TCL_QUEUE_MARK_EX = TCL_QUEUE_MARK,
+    TCL_QUEUE_TAIL_EX_ALERT_IF_EMPTY,
+    TCL_QUEUE_HEAD_EX_ALERT_IF_EMPTY,
+} Tcl_QueuePositionEx;
+
+#define TCL_QUEUE_TAIL_ALERT_IF_EMPTY \
+    ((Tcl_QueuePosition) TCL_QUEUE_TAIL_EX_ALERT_IF_EMPTY)
+#define TCL_QUEUE_HEAD_ALERT_IF_EMPTY \
+    ((Tcl_QueuePosition) TCL_QUEUE_HEAD_EX_ALERT_IF_EMPTY)
+
 /*
  * Values to pass to Tcl_SetServiceMode to specify the behavior of notifier
  * event routines.
  */
 

Index: generic/tclIORChan.c
==================================================================
--- generic/tclIORChan.c
+++ generic/tclIORChan.c
@@ -992,12 +992,12 @@
          * XXX (Delayed postevent executed after channel got removed).
          * XXX Can we detect this ? (check the validity of the owner threadid ?)
          * XXX Actually, in that case the channel should be dead also !
          */
 
-        Tcl_ThreadQueueEvent(rcPtr->owner, (Tcl_Event *) ev, TCL_QUEUE_TAIL);
-        Tcl_ThreadAlert(rcPtr->owner);
+        Tcl_ThreadQueueEvent(rcPtr->owner, (Tcl_Event *) ev,
+		TCL_QUEUE_TAIL_ALERT_IF_EMPTY);
     }
 #endif
 
     /*
      * Squash interp results left by the event script.
@@ -2994,12 +2994,12 @@
 
     /*
      * Queue the event and poke the other thread's notifier.
      */
 
-    Tcl_ThreadQueueEvent(dst, (Tcl_Event *) evPtr, TCL_QUEUE_TAIL);
-    Tcl_ThreadAlert(dst);
+    Tcl_ThreadQueueEvent(dst, (Tcl_Event *) evPtr,
+	    TCL_QUEUE_TAIL_ALERT_IF_EMPTY);
 
     /*
      * (*) Block until the handler thread has either processed the transfer or
      * rejected it.
      */

Index: generic/tclIORTrans.c
==================================================================
--- generic/tclIORTrans.c
+++ generic/tclIORTrans.c
@@ -2450,12 +2450,12 @@
 
     /*
      * Queue the event and poke the other thread's notifier.
      */
 
-    Tcl_ThreadQueueEvent(dst, (Tcl_Event *) evPtr, TCL_QUEUE_TAIL);
-    Tcl_ThreadAlert(dst);
+    Tcl_ThreadQueueEvent(dst, (Tcl_Event *) evPtr,
+	    TCL_QUEUE_TAIL_ALERT_IF_EMPTY);
 
     /*
      * (*) Block until the other thread has either processed the transfer or
      * rejected it.
      */

Index: generic/tclNotify.c
==================================================================
--- generic/tclNotify.c
+++ generic/tclNotify.c
@@ -93,12 +93,12 @@
 
 /*
  * Declarations for routines used only in this file.
  */
 
-static void		QueueEvent(ThreadSpecificData *tsdPtr,
-			    Tcl_Event *evPtr, Tcl_QueuePosition position);
+static int		QueueEvent(ThreadSpecificData *tsdPtr,
+			    Tcl_Event *evPtr, Tcl_QueuePositionEx position);
 
 /*
  *----------------------------------------------------------------------
  *
  * TclInitNotifier --
@@ -395,11 +395,11 @@
     Tcl_QueuePosition position)	/* One of TCL_QUEUE_TAIL, TCL_QUEUE_HEAD,
 				 * TCL_QUEUE_MARK. */
 {
     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
 
-    QueueEvent(tsdPtr, evPtr, position);
+    (void) QueueEvent(tsdPtr, evPtr, (Tcl_QueuePositionEx) position);
 }
 
 /*
  *----------------------------------------------------------------------
  *
@@ -442,11 +442,13 @@
     /*
      * Queue the event if there was a notifier associated with the thread.
      */
 
     if (tsdPtr) {
-	QueueEvent(tsdPtr, evPtr, position);
+	if (QueueEvent(tsdPtr, evPtr, (Tcl_QueuePositionEx) position)) {
+	    Tcl_AlertNotifier(tsdPtr->clientData);
+	}
     } else {
 	ckfree(evPtr);
     }
     Tcl_MutexUnlock(&listLock);
 }
@@ -462,54 +464,64 @@
  *	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.
+ *	For TCL_QUEUE_(HEAD|TAIL)_ALERT_IF_EMPTY the empty state before the
+ *	operation is returned.
  *
  * Side effects:
  *	None.
  *
  *----------------------------------------------------------------------
  */
 
-static void
+static int
 QueueEvent(
     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_QueuePositionEx position)
+				/* One of TCL_QUEUE_TAIL_EX,
+				 * TCL_QUEUE_HEAD_EX, TCL_QUEUE_MARK_EX,
+				 * TCL_QUEUE_TAIL_ALERT_IF_EMPTY, or
+				 * TCL_QUEUE_HEAD_ALERT_IF_EMPTY. */
 {
+    int wasEmpty = 0;
+
     Tcl_MutexLock(&(tsdPtr->queueMutex));
-    if (position == TCL_QUEUE_TAIL) {
+    if ((position == TCL_QUEUE_TAIL_EX) ||
+	    (position == TCL_QUEUE_TAIL_EX_ALERT_IF_EMPTY)) {
 	/*
 	 * Append the event on the end of the queue.
 	 */
 
 	evPtr->nextPtr = NULL;
 	if (tsdPtr->firstEventPtr == NULL) {
 	    tsdPtr->firstEventPtr = evPtr;
+	    wasEmpty = (position == TCL_QUEUE_TAIL_EX_ALERT_IF_EMPTY) ? 1 : 0;
 	} else {
 	    tsdPtr->lastEventPtr->nextPtr = evPtr;
 	}
 	tsdPtr->lastEventPtr = evPtr;
-    } else if (position == TCL_QUEUE_HEAD) {
+    } else if ((position == TCL_QUEUE_HEAD_EX) ||
+		    (position == TCL_QUEUE_HEAD_EX_ALERT_IF_EMPTY)) {
 	/*
 	 * Push the event on the head of the queue.
 	 */
 
 	evPtr->nextPtr = tsdPtr->firstEventPtr;
 	if (tsdPtr->firstEventPtr == NULL) {
 	    tsdPtr->lastEventPtr = evPtr;
+	    wasEmpty = (position == TCL_QUEUE_HEAD_EX_ALERT_IF_EMPTY) ? 1 : 0;
 	}
 	tsdPtr->firstEventPtr = evPtr;
-    } else if (position == TCL_QUEUE_MARK) {
+    } else if (position == TCL_QUEUE_MARK_EX) {
 	/*
 	 * Insert the event after the current marker event and advance the
 	 * marker to the new event.
 	 */
 
@@ -524,10 +536,11 @@
 	if (evPtr->nextPtr == NULL) {
 	    tsdPtr->lastEventPtr = evPtr;
 	}
     }
     Tcl_MutexUnlock(&(tsdPtr->queueMutex));
+    return wasEmpty;
 }
 
 /*
  *----------------------------------------------------------------------
  *

Index: generic/tclThreadTest.c
==================================================================
--- generic/tclThreadTest.c
+++ generic/tclThreadTest.c
@@ -876,12 +876,11 @@
      * Queue the event and poke the other thread's notifier.
      */
 
     threadEventPtr->event.proc = ThreadEventProc;
     Tcl_ThreadQueueEvent(threadId, (Tcl_Event *) threadEventPtr,
-	    TCL_QUEUE_TAIL);
-    Tcl_ThreadAlert(threadId);
+	    TCL_QUEUE_TAIL_ALERT_IF_EMPTY);
 
     if (!wait) {
 	Tcl_MutexUnlock(&threadMutex);
 	return TCL_OK;
     }