From 82d5e7d59540bfd95e3258032a92d4607752d9cd Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Sun, 15 Aug 2021 20:35:59 +0000 Subject: Proposed TIP #609 implementation --- doc/Notifier.3 | 23 ++++++++++++++++++----- generic/tcl.h | 17 +++++++++++++++++ generic/tclIORChan.c | 8 ++++---- generic/tclIORTrans.c | 4 ++-- generic/tclNotify.c | 35 ++++++++++++++++++++++++----------- generic/tclThreadTest.c | 3 +-- 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); -- cgit v0.12