summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjan.nijtmans <nijtmans@users.sourceforge.net>2021-08-15 20:35:59 (GMT)
committerjan.nijtmans <nijtmans@users.sourceforge.net>2021-08-15 20:35:59 (GMT)
commit82d5e7d59540bfd95e3258032a92d4607752d9cd (patch)
treeae114ad5fe1486943aab75c49b19f470bcb14fc5
parent16105b98334db5910809fd5ead357bafe92bc6fe (diff)
downloadtcl-82d5e7d59540bfd95e3258032a92d4607752d9cd.zip
tcl-82d5e7d59540bfd95e3258032a92d4607752d9cd.tar.gz
tcl-82d5e7d59540bfd95e3258032a92d4607752d9cd.tar.bz2
Proposed TIP #609 implementation
-rw-r--r--doc/Notifier.323
-rw-r--r--generic/tcl.h17
-rw-r--r--generic/tclIORChan.c8
-rw-r--r--generic/tclIORTrans.c4
-rw-r--r--generic/tclNotify.c35
-rw-r--r--generic/tclThreadTest.c3
6 files changed, 66 insertions, 24 deletions
diff --git a/doc/Notifier.3 b/doc/Notifier.3
index ec9f910..755930f 100644
--- a/doc/Notifier.3
+++ b/doc/Notifier.3
@@ -92,7 +92,9 @@ 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
@@ -340,14 +342,14 @@ 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.
@@ -355,6 +357,14 @@ 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
@@ -408,7 +418,10 @@ 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
diff --git a/generic/tcl.h b/generic/tcl.h
index 2d529b7..1ce68b4 100644
--- a/generic/tcl.h
+++ b/generic/tcl.h
@@ -1337,6 +1337,23 @@ typedef enum {
} 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.
*/
diff --git a/generic/tclIORChan.c b/generic/tclIORChan.c
index cc45873..b473417 100644
--- a/generic/tclIORChan.c
+++ b/generic/tclIORChan.c
@@ -994,8 +994,8 @@ TclChanPostEventObjCmd(
* 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
@@ -2996,8 +2996,8 @@ ForwardOpToHandlerThread(
* 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
diff --git a/generic/tclIORTrans.c b/generic/tclIORTrans.c
index b06bd45..eda72ba 100644
--- a/generic/tclIORTrans.c
+++ b/generic/tclIORTrans.c
@@ -2452,8 +2452,8 @@ ForwardOpToOwnerThread(
* 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
diff --git a/generic/tclNotify.c b/generic/tclNotify.c
index 12b40b1..99aceec 100644
--- a/generic/tclNotify.c
+++ b/generic/tclNotify.c
@@ -95,8 +95,8 @@ TCL_DECLARE_MUTEX(listLock)
* 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);
/*
*----------------------------------------------------------------------
@@ -397,7 +397,7 @@ Tcl_QueueEvent(
{
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
- QueueEvent(tsdPtr, evPtr, position);
+ (void) QueueEvent(tsdPtr, evPtr, (Tcl_QueuePositionEx) position);
}
/*
@@ -444,7 +444,9 @@ Tcl_ThreadQueueEvent(
*/
if (tsdPtr) {
- QueueEvent(tsdPtr, evPtr, position);
+ if (QueueEvent(tsdPtr, evPtr, (Tcl_QueuePositionEx) position)) {
+ Tcl_AlertNotifier(tsdPtr->clientData);
+ }
} else {
ckfree(evPtr);
}
@@ -464,7 +466,8 @@ Tcl_ThreadQueueEvent(
* 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.
@@ -472,7 +475,7 @@ Tcl_ThreadQueueEvent(
*----------------------------------------------------------------------
*/
-static void
+static int
QueueEvent(
ThreadSpecificData *tsdPtr, /* Handle to thread local data that indicates
* which event queue to use. */
@@ -481,11 +484,17 @@ QueueEvent(
* 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.
*/
@@ -493,11 +502,13 @@ QueueEvent(
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.
*/
@@ -505,9 +516,10 @@ QueueEvent(
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.
@@ -526,6 +538,7 @@ QueueEvent(
}
}
Tcl_MutexUnlock(&(tsdPtr->queueMutex));
+ return wasEmpty;
}
/*
diff --git a/generic/tclThreadTest.c b/generic/tclThreadTest.c
index 9f08d83..887f645 100644
--- a/generic/tclThreadTest.c
+++ b/generic/tclThreadTest.c
@@ -878,8 +878,7 @@ ThreadSend(
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);