Attachment "tip511.diff4" to
ticket [d1bf3f53a6]
added by
chw
2021-08-05 17:53:00.
Index: doc/Async.3
==================================================================
--- doc/Async.3
+++ doc/Async.3
@@ -7,23 +7,28 @@
'\"
.TH Tcl_AsyncCreate 3 7.0 Tcl "Tcl Library Procedures"
.so man.macros
.BS
.SH NAME
-Tcl_AsyncCreate, Tcl_AsyncMark, Tcl_AsyncInvoke, Tcl_AsyncDelete, Tcl_AsyncReady \- handle asynchronous events
+Tcl_AsyncCreate, Tcl_AsyncMark, Tcl_AsyncMarkFromSignal, Tcl_AsyncInvoke, Tcl_AsyncDelete, Tcl_AsyncReady \- handle asynchronous events
.SH SYNOPSIS
.nf
\fB#include <tcl.h>\fR
.sp
Tcl_AsyncHandler
\fBTcl_AsyncCreate\fR(\fIproc, clientData\fR)
.sp
+void
\fBTcl_AsyncMark\fR(\fIasync\fR)
.sp
+int
+\fBTcl_AsyncMarkFromSignal\fR(\fIasync\fR, \fIsigNumber\fR)
+.sp
int
\fBTcl_AsyncInvoke\fR(\fIinterp, code\fR)
.sp
+void
\fBTcl_AsyncDelete\fR(\fIasync\fR)
.sp
int
\fBTcl_AsyncReady\fR()
.SH ARGUMENTS
@@ -32,10 +37,12 @@
Procedure to invoke to handle an asynchronous event.
.AP ClientData clientData in
One-word value to pass to \fIproc\fR.
.AP Tcl_AsyncHandler async in
Token for asynchronous event handler.
+.AP int sigNumber in
+POSIX signal number, when used in a signal context.
.AP Tcl_Interp *interp in
Tcl interpreter in which command was being evaluated when handler was
invoked, or NULL if handler was invoked when there was no interpreter
active.
.AP int code in
@@ -58,29 +65,33 @@
occurred, then handle the event later when the world has returned
to a clean state, such as after the current Tcl command completes.
.PP
\fBTcl_AsyncCreate\fR, \fBTcl_AsyncDelete\fR, and \fBTcl_AsyncReady\fR
are thread sensitive. They access and/or set a thread-specific data
-structure in the event of a core built with \fI\-\-enable\-threads\fR. The token
-created by \fBTcl_AsyncCreate\fR contains the needed thread information it
-was called from so that calling \fBTcl_AsyncMark\fR(\fItoken\fR) will only yield
-the origin thread into the asynchronous handler.
+structure in the event of a core built with \fI\-\-enable\-threads\fR.
+The token created by \fBTcl_AsyncCreate\fR contains the needed thread
+information it was called from so that calling \fBTcl_AsyncMarkFromSignal\fR
+or \fBTcl_AsyncMark\fR with this token will only yield the origin
+thread into the asynchronous handler.
.PP
\fBTcl_AsyncCreate\fR creates an asynchronous handler and returns
a token for it.
The asynchronous handler must be created before
any occurrences of the asynchronous event that it is intended
to handle (it is not safe to create a handler at the time of
an event).
When an asynchronous event occurs the code that detects the event
-(such as a signal handler) should call \fBTcl_AsyncMark\fR with the
-token for the handler.
-\fBTcl_AsyncMark\fR will mark the handler as ready to execute, but it
-will not invoke the handler immediately.
-Tcl will call the \fIproc\fR associated with the handler later, when
-the world is in a safe state, and \fIproc\fR can then carry out
-the actions associated with the asynchronous event.
+(such as a POSIX signal handler) should call \fBTcl_AsyncMarkFromSignal\fR
+with the token for the handler and the POSIX signal number. The
+return value of this function is true, when the handler will be
+marked, false otherwise.
+For non-signal contexts, \fBTcl_AsyncMark\fR serves the same purpose.
+\fBTcl_AsyncMarkFromSignal\fR and \fBTcl_AsyncMark\fR will mark
+the handler as ready to execute, but will not invoke the handler
+immediately. Tcl will call the \fIproc\fR associated with the
+handler later, when the world is in a safe state, and \fIproc\fR
+can then carry out the actions associated with the asynchronous event.
\fIProc\fR should have arguments and result that match the
type \fBTcl_AsyncProc\fR:
.PP
.CS
typedef int \fBTcl_AsyncProc\fR(
Index: generic/tcl.decls
==================================================================
--- generic/tcl.decls
+++ generic/tcl.decls
@@ -2399,10 +2399,15 @@
}
declare 648 {
int *Tcl_UtfToUniCharDString(const char *src,
int length, Tcl_DString *dsPtr)
}
+
+# TIP #511
+declare 649 {
+ int Tcl_AsyncMarkFromSignal(Tcl_AsyncHandler async, int sigNumber)
+}
# TIP #481
declare 651 {
char *TclGetStringFromObj(Tcl_Obj *objPtr, size_t *lengthPtr)
}
Index: generic/tclAsync.c
==================================================================
--- generic/tclAsync.c
+++ generic/tclAsync.c
@@ -23,13 +23,13 @@
typedef struct AsyncHandler {
int ready; /* Non-zero means this handler should be
* invoked in the next call to
* Tcl_AsyncInvoke. */
- struct AsyncHandler *nextPtr;
- /* Next in list of all handlers for the
- * process. */
+ struct AsyncHandler *nextPtr, *prevPtr;
+ /* Next, previous in list of all handlers
+ * for the process. */
Tcl_AsyncProc *proc; /* Procedure to call when handler is
* invoked. */
ClientData clientData; /* Value to pass to handler when it is
* invoked. */
struct ThreadSpecificData *originTsd;
@@ -36,20 +36,14 @@
/* Used in Tcl_AsyncMark to modify thread-
* specific data from outside the thread it is
* associated to. */
Tcl_ThreadId originThrdId; /* Origin thread where this token was created
* and where it will be yielded. */
+ ClientData notifierData; /* Platform notifier data or NULL. */
} AsyncHandler;
typedef struct ThreadSpecificData {
- /*
- * The variables below maintain a list of all existing handlers specific
- * to the calling thread.
- */
- AsyncHandler *firstHandler; /* First handler defined for process, or NULL
- * if none. */
- AsyncHandler *lastHandler; /* Last handler or NULL. */
int asyncReady; /* This is set to 1 whenever a handler becomes
* ready and it is cleared to zero whenever
* Tcl_AsyncInvoke is called. It can be
* checked elsewhere in the application by
* calling Tcl_AsyncReady to see if
@@ -56,39 +50,74 @@
* Tcl_AsyncInvoke should be invoked. */
int asyncActive; /* Indicates whether Tcl_AsyncInvoke is
* currently working. If so then we won't set
* asyncReady again until Tcl_AsyncInvoke
* returns. */
- Tcl_Mutex asyncMutex; /* Thread-specific AsyncHandler linked-list
- * lock */
} ThreadSpecificData;
static Tcl_ThreadDataKey dataKey;
+
+/* Mutex to protect linked-list of AsyncHandlers in the process. */
+TCL_DECLARE_MUTEX(asyncMutex)
+
+/* List of all existing handlers of the process. */
+static AsyncHandler *firstHandler = NULL;
+static AsyncHandler *lastHandler = NULL;
/*
*----------------------------------------------------------------------
*
* TclFinalizeAsync --
*
- * Finalizes the mutex in the thread local data structure for the async
+ * Finalizes the thread local data structure for the async
* subsystem.
*
* Results:
* None.
*
* Side effects:
- * Forgets knowledge of the mutex should it have been created.
+ * Cleans up left-over async handlers for the calling thread.
*
*----------------------------------------------------------------------
*/
void
TclFinalizeAsync(void)
{
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+ AsyncHandler *token, *toDelete = NULL;
+ Tcl_ThreadId self = Tcl_GetCurrentThread();
- if (tsdPtr->asyncMutex != NULL) {
- Tcl_MutexFinalize(&tsdPtr->asyncMutex);
+ Tcl_MutexLock(&asyncMutex);
+ for (token = firstHandler; token != NULL;) {
+ AsyncHandler *nextToken = token->nextPtr;
+
+ if (token->originThrdId == self) {
+ if (token->prevPtr == NULL) {
+ firstHandler = token->nextPtr;
+ if (firstHandler == NULL) {
+ lastHandler = NULL;
+ break;
+ }
+ } else {
+ token->prevPtr->nextPtr = token->nextPtr;
+ if (token == lastHandler) {
+ lastHandler = token->prevPtr;
+ }
+ }
+ if (token->nextPtr != NULL) {
+ token->nextPtr->prevPtr = token->prevPtr;
+ }
+ token->nextPtr = toDelete;
+ token->prevPtr = NULL;
+ toDelete = token;
+ }
+ token = nextToken;
+ }
+ Tcl_MutexUnlock(&asyncMutex);
+ while (toDelete != NULL) {
+ token = toDelete;
+ toDelete = toDelete->nextPtr;
+ ckfree(token);
}
}
/*
*----------------------------------------------------------------------
@@ -119,23 +148,26 @@
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
asyncPtr = (AsyncHandler*)ckalloc(sizeof(AsyncHandler));
asyncPtr->ready = 0;
asyncPtr->nextPtr = NULL;
+ asyncPtr->prevPtr = NULL;
asyncPtr->proc = proc;
asyncPtr->clientData = clientData;
asyncPtr->originTsd = tsdPtr;
asyncPtr->originThrdId = Tcl_GetCurrentThread();
+ asyncPtr->notifierData = TclpNotifierData();
- Tcl_MutexLock(&tsdPtr->asyncMutex);
- if (tsdPtr->firstHandler == NULL) {
- tsdPtr->firstHandler = asyncPtr;
+ Tcl_MutexLock(&asyncMutex);
+ if (firstHandler == NULL) {
+ firstHandler = asyncPtr;
} else {
- tsdPtr->lastHandler->nextPtr = asyncPtr;
+ asyncPtr->prevPtr = lastHandler;
+ lastHandler->nextPtr = asyncPtr;
}
- tsdPtr->lastHandler = asyncPtr;
- Tcl_MutexUnlock(&tsdPtr->asyncMutex);
+ lastHandler = asyncPtr;
+ Tcl_MutexUnlock(&asyncMutex);
return (Tcl_AsyncHandler) asyncPtr;
}
/*
*----------------------------------------------------------------------
@@ -160,17 +192,88 @@
Tcl_AsyncMark(
Tcl_AsyncHandler async) /* Token for handler. */
{
AsyncHandler *token = (AsyncHandler *) async;
- Tcl_MutexLock(&token->originTsd->asyncMutex);
+ Tcl_MutexLock(&asyncMutex);
token->ready = 1;
if (!token->originTsd->asyncActive) {
token->originTsd->asyncReady = 1;
Tcl_ThreadAlert(token->originThrdId);
}
- Tcl_MutexUnlock(&token->originTsd->asyncMutex);
+ Tcl_MutexUnlock(&asyncMutex);
+
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_AsyncMarkFromSignal --
+ *
+ * This procedure is similar to Tcl_AsyncMark but must be used
+ * in POSIX signal contexts. In addition to Tcl_AsyncMark the
+ * signal number is passed.
+ *
+ * Results:
+ * True, when the handler will be marked, false otherwise.
+ *
+ * Side effects:
+ * The handler gets marked for invocation later.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tcl_AsyncMarkFromSignal(
+ Tcl_AsyncHandler async, /* Token for handler. */
+ int sigNumber) /* Signal number. */
+{
+#ifdef TCL_THREADS
+ AsyncHandler *token = (AsyncHandler *) async;
+
+ return TclAsyncNotifier(sigNumber, token->originThrdId,
+ token->notifierData, &token->ready, -1);
+#else
+ Tcl_AsyncMark(async);
+ return 1;
+#endif
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclAsyncMarkFromNotifier --
+ *
+ * This procedure is called from the notifier thread and
+ * invokes Tcl_AsyncMark for specifically marked handlers.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Handlers get marked for invocation later.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TclAsyncMarkFromNotifier(void)
+{
+ AsyncHandler *token;
+
+ Tcl_MutexLock(&asyncMutex);
+ for (token = firstHandler; token != NULL;
+ token = token->nextPtr) {
+ if (token->ready == -1) {
+ token->ready = 1;
+ if (!token->originTsd->asyncActive) {
+ token->originTsd->asyncReady = 1;
+ Tcl_ThreadAlert(token->originThrdId);
+ }
+ }
+ }
+ Tcl_MutexUnlock(&asyncMutex);
}
/*
*----------------------------------------------------------------------
*
@@ -198,15 +301,16 @@
* completion code from command that just
* completed. */
{
AsyncHandler *asyncPtr;
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+ Tcl_ThreadId self = Tcl_GetCurrentThreadId();
- Tcl_MutexLock(&tsdPtr->asyncMutex);
+ Tcl_MutexLock(&asyncMutex);
if (tsdPtr->asyncReady == 0) {
- Tcl_MutexUnlock(&tsdPtr->asyncMutex);
+ Tcl_MutexUnlock(&asyncMutex);
return code;
}
tsdPtr->asyncReady = 0;
tsdPtr->asyncActive = 1;
if (interp == NULL) {
@@ -222,26 +326,29 @@
* execution of a handler, then the list structure may change so it isn't
* safe to continue down the list anyway.
*/
while (1) {
- for (asyncPtr = tsdPtr->firstHandler; asyncPtr != NULL;
+ for (asyncPtr = firstHandler; asyncPtr != NULL;
asyncPtr = asyncPtr->nextPtr) {
+ if (asyncPtr->originThrdId != self) {
+ continue;
+ }
if (asyncPtr->ready) {
break;
}
}
if (asyncPtr == NULL) {
break;
}
asyncPtr->ready = 0;
- Tcl_MutexUnlock(&tsdPtr->asyncMutex);
+ Tcl_MutexUnlock(&asyncMutex);
code = asyncPtr->proc(asyncPtr->clientData, interp, code);
- Tcl_MutexLock(&tsdPtr->asyncMutex);
+ Tcl_MutexLock(&asyncMutex);
}
tsdPtr->asyncActive = 0;
- Tcl_MutexUnlock(&tsdPtr->asyncMutex);
+ Tcl_MutexUnlock(&asyncMutex);
return code;
}
/*
*----------------------------------------------------------------------
@@ -269,49 +376,36 @@
void
Tcl_AsyncDelete(
Tcl_AsyncHandler async) /* Token for handler to delete. */
{
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
AsyncHandler *asyncPtr = (AsyncHandler *) async;
- AsyncHandler *prevPtr, *thisPtr;
/*
* Assure early handling of the constraint
*/
if (asyncPtr->originThrdId != Tcl_GetCurrentThread()) {
Tcl_Panic("Tcl_AsyncDelete: async handler deleted by the wrong thread");
}
- /*
- * If we come to this point when TSD's for the current
- * thread have already been garbage-collected, we are
- * in the _serious_ trouble. OTOH, we tolerate calling
- * with already cleaned-up handler list (should we?).
- */
-
- Tcl_MutexLock(&tsdPtr->asyncMutex);
- if (tsdPtr->firstHandler != NULL) {
- prevPtr = thisPtr = tsdPtr->firstHandler;
- while (thisPtr != NULL && thisPtr != asyncPtr) {
- prevPtr = thisPtr;
- thisPtr = thisPtr->nextPtr;
+ Tcl_MutexLock(&asyncMutex);
+ if (asyncPtr->prevPtr == NULL) {
+ firstHandler = asyncPtr->nextPtr;
+ if (firstHandler == NULL) {
+ lastHandler = NULL;
}
- if (thisPtr == NULL) {
- Tcl_Panic("Tcl_AsyncDelete: cannot find async handler");
- }
- if (asyncPtr == tsdPtr->firstHandler) {
- tsdPtr->firstHandler = asyncPtr->nextPtr;
- } else {
- prevPtr->nextPtr = asyncPtr->nextPtr;
- }
- if (asyncPtr == tsdPtr->lastHandler) {
- tsdPtr->lastHandler = prevPtr;
+ } else {
+ asyncPtr->prevPtr->nextPtr = asyncPtr->nextPtr;
+ if (asyncPtr == lastHandler) {
+ lastHandler = asyncPtr->prevPtr;
}
}
- Tcl_MutexUnlock(&tsdPtr->asyncMutex);
+ if (asyncPtr->nextPtr != NULL) {
+ asyncPtr->nextPtr->prevPtr = asyncPtr->prevPtr;
+ }
+ Tcl_MutexUnlock(&asyncMutex);
ckfree(asyncPtr);
}
/*
*----------------------------------------------------------------------
Index: generic/tclDecls.h
==================================================================
--- generic/tclDecls.h
+++ generic/tclDecls.h
@@ -1918,11 +1918,13 @@
EXTERN char * Tcl_UniCharToUtfDString(const int *uniStr,
int uniLength, Tcl_DString *dsPtr);
/* 648 */
EXTERN int * Tcl_UtfToUniCharDString(const char *src, int length,
Tcl_DString *dsPtr);
-/* Slot 649 is reserved */
+/* 649 */
+EXTERN int Tcl_AsyncMarkFromSignal(Tcl_AsyncHandler async,
+ int sigNumber);
/* Slot 650 is reserved */
/* 651 */
EXTERN char * TclGetStringFromObj(Tcl_Obj *objPtr,
size_t *lengthPtr);
/* 652 */
@@ -2621,11 +2623,11 @@
int (*tcl_LinkArray) (Tcl_Interp *interp, const char *varName, void *addr, int type, int size); /* 644 */
int (*tcl_GetIntForIndex) (Tcl_Interp *interp, Tcl_Obj *objPtr, int endValue, int *indexPtr); /* 645 */
int (*tcl_UtfToUniChar) (const char *src, int *chPtr); /* 646 */
char * (*tcl_UniCharToUtfDString) (const int *uniStr, int uniLength, Tcl_DString *dsPtr); /* 647 */
int * (*tcl_UtfToUniCharDString) (const char *src, int length, Tcl_DString *dsPtr); /* 648 */
- void (*reserved649)(void);
+ int (*tcl_AsyncMarkFromSignal) (Tcl_AsyncHandler async, int sigNumber); /* 649 */
void (*reserved650)(void);
char * (*tclGetStringFromObj) (Tcl_Obj *objPtr, size_t *lengthPtr); /* 651 */
Tcl_UniChar * (*tclGetUnicodeFromObj) (Tcl_Obj *objPtr, size_t *lengthPtr); /* 652 */
unsigned char * (*tclGetByteArrayFromObj) (Tcl_Obj *objPtr, size_t *lengthPtr); /* 653 */
int (*tcl_UtfCharComplete) (const char *src, int length); /* 654 */
@@ -3958,11 +3960,12 @@
(tclStubsPtr->tcl_UtfToUniChar) /* 646 */
#define Tcl_UniCharToUtfDString \
(tclStubsPtr->tcl_UniCharToUtfDString) /* 647 */
#define Tcl_UtfToUniCharDString \
(tclStubsPtr->tcl_UtfToUniCharDString) /* 648 */
-/* Slot 649 is reserved */
+#define Tcl_AsyncMarkFromSignal \
+ (tclStubsPtr->tcl_AsyncMarkFromSignal) /* 649 */
/* Slot 650 is reserved */
#define TclGetStringFromObj \
(tclStubsPtr->tclGetStringFromObj) /* 651 */
#define TclGetUnicodeFromObj \
(tclStubsPtr->tclGetUnicodeFromObj) /* 652 */
Index: generic/tclInt.h
==================================================================
--- generic/tclInt.h
+++ generic/tclInt.h
@@ -2924,10 +2924,13 @@
void *codePtr, CmdFrame *cfPtr, int cmd, int pc);
MODULE_SCOPE void TclArgumentBCRelease(Tcl_Interp *interp,
CmdFrame *cfPtr);
MODULE_SCOPE void TclArgumentGet(Tcl_Interp *interp, Tcl_Obj *obj,
CmdFrame **cfPtrPtr, int *wordPtr);
+MODULE_SCOPE int TclAsyncNotifier(int sigNumber, Tcl_ThreadId threadId,
+ ClientData clientData, int *flagPtr, int value);
+MODULE_SCOPE void TclAsyncMarkFromNotifier(void);
MODULE_SCOPE double TclBignumToDouble(const void *bignum);
MODULE_SCOPE int TclByteArrayMatch(const unsigned char *string,
int strLen, const unsigned char *pattern,
int ptnLen, int flags);
MODULE_SCOPE double TclCeil(const void *a);
@@ -3139,10 +3142,11 @@
MODULE_SCOPE Tcl_Obj * TclpTempFileNameForLibrary(Tcl_Interp *interp,
Tcl_Obj* pathPtr);
MODULE_SCOPE Tcl_Obj * TclNewFSPathObj(Tcl_Obj *dirPtr, const char *addStrRep,
int len);
MODULE_SCOPE void TclpAlertNotifier(ClientData clientData);
+MODULE_SCOPE ClientData TclpNotifierData(void);
MODULE_SCOPE void TclpServiceModeHook(int mode);
MODULE_SCOPE void TclpSetTimer(const Tcl_Time *timePtr);
MODULE_SCOPE int TclpWaitForEvent(const Tcl_Time *timePtr);
MODULE_SCOPE void TclpCreateFileHandler(int fd, int mask,
Tcl_FileProc *proc, ClientData clientData);
Index: generic/tclStubInit.c
==================================================================
--- generic/tclStubInit.c
+++ generic/tclStubInit.c
@@ -1931,11 +1931,11 @@
Tcl_LinkArray, /* 644 */
Tcl_GetIntForIndex, /* 645 */
Tcl_UtfToUniChar, /* 646 */
Tcl_UniCharToUtfDString, /* 647 */
Tcl_UtfToUniCharDString, /* 648 */
- 0, /* 649 */
+ Tcl_AsyncMarkFromSignal, /* 649 */
0, /* 650 */
TclGetStringFromObj, /* 651 */
TclGetUnicodeFromObj, /* 652 */
TclGetByteArrayFromObj, /* 653 */
Tcl_UtfCharComplete, /* 654 */
Index: macosx/tclMacOSXNotify.c
==================================================================
--- macosx/tclMacOSXNotify.c
+++ macosx/tclMacOSXNotify.c
@@ -455,10 +455,24 @@
* You must hold the notifierInitLock before accessing this variable.
*/
static int notifierThreadRunning;
+/*
+ * The following static flag indicates that async handlers are pending.
+ */
+
+#ifdef TCL_THREADS
+static int asyncPending = 0;
+#endif
+
+/*
+ * Signal mask information for notifier thread.
+ */
+static sigset_t notifierSigMask;
+static sigset_t allSigMask;
+
/*
* This is the thread ID of the notifier thread that does select. Only valid
* when notifierThreadRunning is non-zero.
*
* You must hold the notifierInitLock before accessing this variable.
@@ -801,10 +815,19 @@
}
if (!notifierThreadRunning) {
int result;
pthread_attr_t attr;
+ /*
+ * Arrange for the notifier thread to start with all
+ * signals blocked. In its mainloop it unblocks the
+ * signals at safe points.
+ */
+
+ sigfillset(&allSigMask);
+ pthread_sigmask(SIG_BLOCK, &allSigMask, ¬ifierSigMask);
+
pthread_attr_init(&attr);
pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
pthread_attr_setstacksize(&attr, 60 * 1024);
result = pthread_create(¬ifierThread, &attr,
@@ -812,10 +835,16 @@
pthread_attr_destroy(&attr);
if (result) {
Tcl_Panic("StartNotifierThread: unable to start notifier thread");
}
notifierThreadRunning = 1;
+
+ /*
+ * Restore original signal mask.
+ */
+
+ pthread_sigmask(SIG_SETMASK, ¬ifierSigMask, NULL);
}
UNLOCK_NOTIFIER_INIT;
}
@@ -874,10 +903,18 @@
if (result) {
Tcl_Panic("Tcl_FinalizeNotifier: unable to join notifier "
"thread");
}
notifierThreadRunning = 0;
+
+ /*
+ * If async marks are outstanding, perform actions now.
+ */
+ if (asyncPending) {
+ asyncPending = 0;
+ TclAsyncMarkFromNotifier();
+ }
}
close(receivePipe);
triggerPipe = -1;
}
@@ -1277,10 +1314,33 @@
filePtr->proc(filePtr->clientData, mask);
}
}
return 1;
}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclpNotifierData --
+ *
+ * This function returns a ClientData pointer to be associated
+ * with a Tcl_AsyncHandler.
+ *
+ * Results:
+ * On MacOSX, returns always NULL.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+ClientData
+TclpNotifierData(void)
+{
+ return NULL;
+}
/*
*----------------------------------------------------------------------
*
* TclpWaitForEvent --
@@ -1827,10 +1887,65 @@
}
/*
*----------------------------------------------------------------------
*
+ * TclAsyncNotifier --
+ *
+ * This procedure sets the async mark of an async handler to a
+ * given value, if it is called from the notifier thread.
+ *
+ * Result:
+ * True, when the handler will be marked, false otherwise.
+ *
+ * Side effetcs:
+ * The trigger pipe is written when called from the notifier
+ * thread.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclAsyncNotifier(
+ int sigNumber, /* Signal number. */
+ Tcl_ThreadId threadId, /* Target thread. */
+ TCL_UNUSED(ClientData), /* Notifier data. */
+ int *flagPtr, /* Flag to mark. */
+ int value) /* Value of mark. */
+{
+#ifdef TCL_THREADS
+ /*
+ * WARNING:
+ * This code most likely runs in a signal handler. Thus,
+ * only few async-signal-safe system calls are allowed,
+ * e.g. pthread_self(), sem_post(), write().
+ */
+
+ if (pthread_equal(pthread_self(), (pthread_t) notifierThread)) {
+ if (notifierThreadRunning) {
+ *flagPtr = value;
+ if (!asyncPending) {
+ asyncPending = 1;
+ write(triggerPipe, "S", 1);
+ }
+ return 1;
+ }
+ return 0;
+ }
+
+ /*
+ * Re-send the signal to the notifier thread.
+ */
+
+ pthread_kill((pthread_t) notifierThread, sigNumber);
+#endif
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* NotifierThreadProc --
*
* This routine is the initial (and only) function executed by the
* special notifier thread. Its job is to wait for file descriptors to
* become readable or writable or to have an exception condition and then
@@ -1854,11 +1969,11 @@
NotifierThreadProc(
TCL_UNUSED(ClientData))
{
ThreadSpecificData *tsdPtr;
fd_set readableMask, writableMask, exceptionalMask;
- int i, numFdBits = 0, polling;
+ int i, ret, numFdBits = 0, polling;
struct timeval poll = {0., 0.}, *timePtr;
char buf[2];
/*
* Look for file events and report them to interested threads.
@@ -1907,12 +2022,29 @@
if (receivePipe >= numFdBits) {
numFdBits = receivePipe + 1;
}
FD_SET(receivePipe, &readableMask);
- if (select(numFdBits, &readableMask, &writableMask, &exceptionalMask,
- timePtr) == -1) {
+ /*
+ * Signals are unblocked only during select().
+ */
+
+ pthread_sigmask(SIG_SETMASK, ¬ifierSigMask, NULL);
+ ret = select(numFdBits, &readableMask, &writableMask, &exceptionalMask,
+ timePtr);
+ pthread_sigmask(SIG_BLOCK, &allSigMask, NULL);
+
+ if (ret == -1) {
+ /*
+ * In case a signal was caught during select(),
+ * perform work on async handlers now.
+ */
+ if (errno == EINTR && asyncPending) {
+ asyncPending = 0;
+ TclAsyncMarkFromNotifier();
+ }
+
/*
* Try again immediately on an error.
*/
continue;
@@ -1996,10 +2128,15 @@
* pipe so we need to shut down the notifier thread.
*/
break;
}
+
+ if (asyncPending) {
+ asyncPending = 0;
+ TclAsyncMarkFromNotifier();
+ }
}
}
pthread_exit(0);
}
@@ -2118,10 +2255,16 @@
if (!noCFafterFork) {
Tcl_InitNotifier();
}
}
+
+ /*
+ * Restart the notifier thread for signal handling.
+ */
+
+ StartNotifierThread();
}
#endif /* HAVE_PTHREAD_ATFORK */
#else /* HAVE_COREFOUNDATION */
Index: unix/configure.ac
==================================================================
--- unix/configure.ac
+++ unix/configure.ac
@@ -315,10 +315,18 @@
fi
fi
if test $tcl_ok = no; then
AC_DEFINE(NO_FD_SET, 1, [Do we have fd_set?])
fi
+
+AC_CACHE_CHECK([for pselect], tcl_cv_func_pselect, [
+ AC_TRY_COMPILE([#include <sys/types.h>],[void *func = pselect;],
+ tcl_cv_func_pselect=yes, tcl_cv_func_pselect=no)])
+tcl_ok=$tcl_cv_func_pselect
+if test $tcl_ok = yes; then
+ AC_DEFINE(HAVE_PSELECT, 1, [Should we use pselect()?])
+fi
#------------------------------------------------------------------------
# Options for the notifier. Checks for epoll(7) on Linux, and
# kqueue(2) on {DragonFly,Free,Net,Open}BSD
#------------------------------------------------------------------------
Index: unix/tclConfig.h.in
==================================================================
--- unix/tclConfig.h.in
+++ unix/tclConfig.h.in
@@ -187,10 +187,13 @@
/* Define to 1 if you have the `pthread_atfork' function. */
#undef HAVE_PTHREAD_ATFORK
/* Define to 1 if you have the `pthread_attr_setstacksize' function. */
#undef HAVE_PTHREAD_ATTR_SETSTACKSIZE
+
+/* Define to 1 if you have the `pselect' function */
+#undef HAVE_PSELECT
/* Does putenv() copy strings or incorporate them by reference? */
#undef HAVE_PUTENV_THAT_COPIES
/* Are characters signed? */
Index: unix/tclEpollNotfy.c
==================================================================
--- unix/tclEpollNotfy.c
+++ unix/tclEpollNotfy.c
@@ -109,10 +109,11 @@
* fds */
struct epoll_event *readyEvents;
/* Pointer to at most maxReadyEvents events
* returned by epoll_wait(2). */
size_t maxReadyEvents; /* Count of epoll_events in readyEvents. */
+ int asyncPending; /* True when signal triggered thread. */
} ThreadSpecificData;
static Tcl_ThreadDataKey dataKey;
/*
@@ -476,10 +477,14 @@
} else {
timePtr->tv_sec = 0;
timePtr->tv_usec = 0;
}
}
+ if (tsdPtr->asyncPending) {
+ tsdPtr->asyncPending = 0;
+ TclAsyncMarkFromNotifier();
+ }
return numFound;
}
/*
*----------------------------------------------------------------------
@@ -762,10 +767,64 @@
}
filePtr->readyMask = mask;
}
return 0;
}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclAsyncNotifier --
+ *
+ * This procedure sets the async mark of an async handler to a
+ * given value, if it is called from the target thread.
+ *
+ * Result:
+ * True, when the handler will be marked, false otherwise.
+ *
+ * Side effects:
+ * The signal may be resent to the target thread.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclAsyncNotifier(
+ int sigNumber, /* Signal number. */
+ Tcl_ThreadId threadId, /* Target thread. */
+ ClientData clientData, /* Notifier data. */
+ int *flagPtr, /* Flag to mark. */
+ int value) /* Value of mark. */
+{
+#ifdef TCL_THREADS
+ /*
+ * WARNING:
+ * This code most likely runs in a signal handler. Thus,
+ * only few async-signal-safe system calls are allowed,
+ * e.g. pthread_self(), sem_post(), write().
+ */
+
+ if (pthread_equal(pthread_self(), (pthread_t) threadId)) {
+ ThreadSpecificData *tsdPtr = (ThreadSpecificData *) clientData;
+
+ *flagPtr = value;
+ if (tsdPtr != NULL && !tsdPtr->asyncPending) {
+ tsdPtr->asyncPending = 1;
+ TclpAlertNotifier(tsdPtr);
+ return 1;
+ }
+ return 0;
+ }
+
+ /*
+ * Re-send the signal to the proper target thread.
+ */
+
+ pthread_kill((pthread_t) threadId, sigNumber);
+#endif
+ return 0;
+}
#endif /* NOTIFIER_EPOLL && TCL_THREADS */
#else
TCL_MAC_EMPTY_FILE(unix_tclEpollNotfy_c)
#endif /* !HAVE_COREFOUNDATION */
Index: unix/tclKqueueNotfy.c
==================================================================
--- unix/tclKqueueNotfy.c
+++ unix/tclKqueueNotfy.c
@@ -100,10 +100,11 @@
int eventsFd; /* kqueue(2) file descriptor used to wait for
* fds. */
struct kevent *readyEvents; /* Pointer to at most maxReadyEvents events
* returned by kevent(2). */
size_t maxReadyEvents; /* Count of kevents in readyEvents. */
+ int asyncPending; /* True when signal triggered thread. */
} ThreadSpecificData;
static Tcl_ThreadDataKey dataKey;
/*
@@ -163,15 +164,15 @@
struct kevent changeList[2];
struct PlatformEventData *newPedPtr;
Tcl_StatBuf fdStat;
if (isNew) {
- newPedPtr = (struct PlatformEventData *)
+ newPedPtr = (struct PlatformEventData *)
ckalloc(sizeof(struct PlatformEventData));
- newPedPtr->filePtr = filePtr;
- newPedPtr->tsdPtr = tsdPtr;
- filePtr->pedPtr = newPedPtr;
+ newPedPtr->filePtr = filePtr;
+ newPedPtr->tsdPtr = tsdPtr;
+ filePtr->pedPtr = newPedPtr;
}
/*
* N.B. As discussed in Tcl_WaitForEvent(), kqueue(2) does not reproduce
* the `always ready' {select,poll}(2) behaviour for regular files
@@ -211,11 +212,11 @@
if (filePtr->mask & TCL_WRITABLE) {
EV_SET(&changeList[numChanges], (uintptr_t) filePtr->fd,
EVFILT_WRITE, op, 0, 0, filePtr->pedPtr);
numChanges++;
}
- if (numChanges) {
+ if (numChanges) {
if (kevent(tsdPtr->eventsFd, changeList, numChanges, NULL, 0,
NULL) == -1) {
Tcl_Panic("kevent: %s", strerror(errno));
}
}
@@ -361,11 +362,11 @@
filePtr = (FileHandler *) ckalloc(sizeof(FileHandler));
filePtr->fd = tsdPtr->triggerPipe[0];
filePtr->mask = TCL_READABLE;
PlatformEventsControl(filePtr, tsdPtr, EV_ADD, 1);
if (!tsdPtr->readyEvents) {
- tsdPtr->maxReadyEvents = 512;
+ tsdPtr->maxReadyEvents = 512;
tsdPtr->readyEvents = (struct kevent *) ckalloc(
tsdPtr->maxReadyEvents * sizeof(tsdPtr->readyEvents[0]));
}
LIST_INIT(&tsdPtr->firstReadyFileHandlerPtr);
@@ -480,10 +481,14 @@
timersub(timePtr, &tv_delta, timePtr);
} else {
timePtr->tv_sec = 0;
timePtr->tv_usec = 0;
}
+ }
+ if (tsdPtr->asyncPending) {
+ tsdPtr->asyncPending = 0;
+ TclAsyncMarkFromNotifier();
}
return numFound;
}
/*
@@ -758,10 +763,64 @@
}
filePtr->readyMask |= mask;
}
return 0;
}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclAsyncNotifier --
+ *
+ * This procedure sets the async mark of an async handler to a
+ * given value, if it is called from the target thread.
+ *
+ * Result:
+ * True, when the handler will be marked, false otherwise.
+ *
+ * Side effects:
+ * The signal may be resent to the target thread.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclAsyncNotifier(
+ int sigNumber, /* Signal number. */
+ Tcl_ThreadId threadId, /* Target thread. */
+ ClientData clientData, /* Notifier data. */
+ int *flagPtr, /* Flag to mark. */
+ int value) /* Value of mark. */
+{
+#ifdef TCL_THREADS
+ /*
+ * WARNING:
+ * This code most likely runs in a signal handler. Thus,
+ * only few async-signal-safe system calls are allowed,
+ * e.g. pthread_self(), sem_post(), write().
+ */
+
+ if (pthread_equal(pthread_self(), (pthread_t) threadId)) {
+ ThreadSpecificData *tsdPtr = (ThreadSpecificData *) clientData;
+
+ *flagPtr = value;
+ if (tsdPtr != NULL && !tsdPtr->asyncPending) {
+ tsdPtr->asyncPending = 1;
+ TclpAlertNotifier(tsdPtr);
+ return 1;
+ }
+ return 0;
+ }
+
+ /*
+ * Re-send the signal to the proper target thread.
+ */
+
+ pthread_kill((pthread_t) threadId, sigNumber);
+#endif
+ return 0;
+}
#endif /* NOTIFIER_KQUEUE && TCL_THREADS */
#else
TCL_MAC_EMPTY_FILE(unix_tclKqueueNotfy_c)
#endif /* !HAVE_COREFOUNDATION */
Index: unix/tclSelectNotfy.c
==================================================================
--- unix/tclSelectNotfy.c
+++ unix/tclSelectNotfy.c
@@ -146,10 +146,11 @@
*
* You must hold the notifierMutex lock before writing to the pipe.
*/
static int triggerPipe = -1;
+static int otherPipe = -1;
/*
* The notifierMutex locks access to all of the global notifier state.
*/
@@ -161,14 +162,20 @@
* You must hold the notifierInitMutex before accessing this variable.
*/
static int notifierThreadRunning = 0;
+/*
+ * The following static flag indicates that async handlers are pending.
+ */
+
+static int asyncPending = 0;
+
/*
* The notifier thread signals the notifierCV when it has finished
* initializing the triggerPipe and right before the notifier thread
- * terminates.
+ * terminates. This condition is used to deal with the signal mask, too.
*/
static pthread_cond_t notifierCV = PTHREAD_COND_INITIALIZER;
/*
@@ -188,10 +195,18 @@
/*
* This is the thread ID of the notifier thread that does select.
*/
static Tcl_ThreadId notifierThread;
+
+/*
+ * Signal mask information for notifier thread.
+ */
+
+static sigset_t notifierSigMask;
+static sigset_t allSigMask;
+
#endif /* TCL_THREADS */
/*
* Static routines defined in this file.
*/
@@ -407,10 +422,18 @@
if (result) {
Tcl_Panic("Tcl_FinalizeNotifier: %s",
"unable to join notifier thread");
}
notifierThreadRunning = 0;
+
+ /*
+ * If async marks are outstanding, perform actions now.
+ */
+ if (asyncPending) {
+ asyncPending = 0;
+ TclAsyncMarkFromNotifier();
+ }
}
}
/*
* Clean up any synchronization objects in the thread local storage.
@@ -873,10 +896,65 @@
}
/*
*----------------------------------------------------------------------
*
+ * TclAsyncNotifier --
+ *
+ * This procedure sets the async mark of an async handler to a
+ * given value, if it is called from the notifier thread.
+ *
+ * Result:
+ * True, when the handler will be marked, false otherwise.
+ *
+ * Side effetcs:
+ * The trigger pipe is written when called from the notifier
+ * thread.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclAsyncNotifier(
+ int sigNumber, /* Signal number. */
+ Tcl_ThreadId threadId, /* Target thread. */
+ TCL_UNUSED(ClientData), /* Notifier data. */
+ int *flagPtr, /* Flag to mark. */
+ int value) /* Value of mark. */
+{
+#ifdef TCL_THREADS
+ /*
+ * WARNING:
+ * This code most likely runs in a signal handler. Thus,
+ * only few async-signal-safe system calls are allowed,
+ * e.g. pthread_self(), sem_post(), write().
+ */
+
+ if (pthread_equal(pthread_self(), (pthread_t) notifierThread)) {
+ if (notifierThreadRunning) {
+ *flagPtr = value;
+ if (!asyncPending) {
+ asyncPending = 1;
+ write(triggerPipe, "S", 1);
+ }
+ return 1;
+ }
+ return 0;
+ }
+
+ /*
+ * Re-send the signal to the notifier thread.
+ */
+
+ pthread_kill((pthread_t) notifierThread, sigNumber);
+#endif
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* NotifierThreadProc --
*
* This routine is the initial (and only) function executed by the
* special notifier thread. Its job is to wait for file descriptors to
* become readable or writable or to have an exception condition and then
@@ -904,20 +982,27 @@
{
ThreadSpecificData *tsdPtr;
fd_set readableMask;
fd_set writableMask;
fd_set exceptionMask;
- int i;
- int fds[2], receivePipe;
+ int i, fds[2], receivePipe, ret;
long found;
struct timeval poll = {0, 0}, *timePtr;
char buf[2];
int numFdBits = 0;
if (pipe(fds) != 0) {
Tcl_Panic("NotifierThreadProc: %s", "could not create trigger pipe");
}
+
+ /*
+ * Ticket [c6897e6e6a].
+ */
+
+ if (fds[0] >= FD_SETSIZE || fds[1] >= FD_SETSIZE) {
+ Tcl_Panic("NotifierThreadProc: %s", "too many open files");
+ }
receivePipe = fds[0];
if (TclUnixSetBlockingMode(receivePipe, TCL_MODE_NONBLOCKING) < 0) {
Tcl_Panic("NotifierThreadProc: %s",
@@ -940,10 +1025,11 @@
* Install the write end of the pipe into the global variable.
*/
pthread_mutex_lock(¬ifierMutex);
triggerPipe = fds[1];
+ otherPipe = fds[0];
/*
* Signal any threads that are waiting.
*/
@@ -1000,16 +1086,48 @@
if (receivePipe >= numFdBits) {
numFdBits = receivePipe + 1;
}
FD_SET(receivePipe, &readableMask);
- if (select(numFdBits, &readableMask, &writableMask, &exceptionMask,
- timePtr) == -1) {
+ /*
+ * Signals are unblocked only during select().
+ */
+
+#ifdef HAVE_PSELECT
+ {
+ struct timespec tspec, *tspecPtr;
+
+ if (timePtr == NULL) {
+ tspecPtr = NULL;
+ } else {
+ tspecPtr = &tspec;
+ tspecPtr->tv_sec = timePtr->tv_sec;
+ tspecPtr->tv_nsec = timePtr->tv_usec * 1000;
+ }
+ ret = pselect(numFdBits, &readableMask, &writableMask,
+ &exceptionMask, tspecPtr, ¬ifierSigMask);
+ }
+#else
+ pthread_sigmask(SIG_SETMASK, ¬ifierSigMask, NULL);
+ ret = select(numFdBits, &readableMask, &writableMask, &exceptionMask,
+ timePtr);
+ pthread_sigmask(SIG_BLOCK, &allSigMask, NULL);
+#endif
+
+ if (ret == -1) {
/*
- * Try again immediately on an error.
+ * In case a signal was caught during select(),
+ * perform work on async handlers now.
*/
+ if (errno == EINTR && asyncPending) {
+ asyncPending = 0;
+ TclAsyncMarkFromNotifier();
+ }
+ /*
+ * Try again immediately on select() error.
+ */
continue;
}
/*
* Alert any threads that are waiting on a ready file descriptor.
@@ -1061,10 +1179,16 @@
*/
break;
}
} while (1);
+
+ if (asyncPending) {
+ asyncPending = 0;
+ TclAsyncMarkFromNotifier();
+ }
+
if ((i == 0) || (buf[0] == 'q')) {
break;
}
}
@@ -1074,10 +1198,11 @@
*/
close(receivePipe);
pthread_mutex_lock(¬ifierMutex);
triggerPipe = -1;
+ otherPipe = -1;
pthread_cond_broadcast(¬ifierCV);
pthread_mutex_unlock(¬ifierMutex);
TclpThreadExit(0);
}
Index: unix/tclUnixNotfy.c
==================================================================
--- unix/tclUnixNotfy.c
+++ unix/tclUnixNotfy.c
@@ -348,28 +348,28 @@
AlertSingleThread(
ThreadSpecificData *tsdPtr)
{
tsdPtr->eventReady = 1;
if (tsdPtr->onList) {
- /*
- * Remove the ThreadSpecificData structure of this thread from the
- * waiting list. This prevents us from continuously spinning on
- * epoll_wait until the other threads runs and services the file
- * event.
- */
+ /*
+ * Remove the ThreadSpecificData structure of this thread from the
+ * waiting list. This prevents us from continuously spinning on
+ * epoll_wait until the other threads runs and services the file
+ * event.
+ */
- if (tsdPtr->prevPtr) {
+ if (tsdPtr->prevPtr) {
tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr;
- } else {
+ } else {
waitingListPtr = tsdPtr->nextPtr;
- }
- if (tsdPtr->nextPtr) {
+ }
+ if (tsdPtr->nextPtr) {
tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr;
- }
- tsdPtr->nextPtr = tsdPtr->prevPtr = NULL;
- tsdPtr->onList = 0;
- tsdPtr->pollState = 0;
+ }
+ tsdPtr->nextPtr = tsdPtr->prevPtr = NULL;
+ tsdPtr->onList = 0;
+ tsdPtr->pollState = 0;
}
#ifdef __CYGWIN__
PostMessageW(tsdPtr->hwnd, 1024, 0, 0);
#else /* !__CYGWIN__ */
pthread_cond_broadcast(&tsdPtr->waitCV);
@@ -401,10 +401,14 @@
}
pthread_mutex_init(¬ifierInitMutex, NULL);
pthread_mutex_init(¬ifierMutex, NULL);
pthread_cond_init(¬ifierCV, NULL);
+#ifdef NOTIFIER_SELECT
+ asyncPending = 0;
+#endif
+
/*
* notifierThreadRunning == 1: thread is running, (there might be data in
* notifier lists)
* atForkInit == 0: InitNotifier was never called
* notifierCount != 0: unbalanced InitNotifier() / FinalizeNotifier calls
@@ -418,10 +422,14 @@
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
notifierThreadRunning = 0;
close(triggerPipe);
triggerPipe = -1;
+#ifdef NOTIFIER_SELECT
+ close(otherPipe);
+ otherPipe = -1;
+#endif
/*
* The waitingListPtr might contain event info from multiple
* threads, which are invalid here, so setting it to NULL is not
* unreasonable.
*/
@@ -454,14 +462,52 @@
*/
}
}
Tcl_InitNotifier();
+
+#ifdef NOTIFIER_SELECT
+ /*
+ * Restart the notifier thread for signal handling.
+ */
+
+ StartNotifierThread("AtForkChild");
+#endif
}
#endif /* HAVE_PTHREAD_ATFORK */
#endif /* TCL_THREADS */
#endif /* NOTIFIER_SELECT */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclpNotifierData --
+ *
+ * This function returns a ClientData pointer to be associated
+ * with a Tcl_AsyncHandler.
+ *
+ * Results:
+ * For the epoll and kqueue notifiers, this function returns the
+ * thread specific data. Otherwise NULL.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+ClientData
+TclpNotifierData(void)
+{
+#if defined(NOTIFIER_EPOLL) || defined(NOTIFIER_KQUEUE)
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+
+ return (ClientData) tsdPtr;
+#else
+ return NULL;
+#endif
+}
/*
*----------------------------------------------------------------------
*
* TclUnixWaitForFile --
Index: win/tclWinNotify.c
==================================================================
--- win/tclWinNotify.c
+++ win/tclWinNotify.c
@@ -351,10 +351,36 @@
}
/*
*----------------------------------------------------------------------
*
+ * TclAsyncNotifier --
+ *
+ * This procedure is a no-op on Windows.
+ *
+ * Result:
+ * Always true.
+ *
+ * Side effetcs:
+ * None.
+ *----------------------------------------------------------------------
+ */
+
+int
+TclAsyncNotifier(
+ int sigNumber, /* Signal number. */
+ Tcl_ThreadId threadId, /* Target thread. */
+ ClientData clientData, /* Notifier data. */
+ int *flagPtr, /* Flag to mark. */
+ int value) /* Value of mark. */
+{
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* NotifierProc --
*
* This procedure is invoked by Windows to process events on the notifier
* window. Messages will be sent to this window in response to external
* timer events or calls to TclpAlertTsdPtr->
@@ -390,10 +416,33 @@
*/
Tcl_ServiceAll();
return 0;
}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclpNotifierData --
+ *
+ * This function returns a ClientData pointer to be associated
+ * with a Tcl_AsyncHandler.
+ *
+ * Results:
+ * On Windows, returns always NULL.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+ClientData
+TclpNotifierData(void)
+{
+ return NULL;
+}
/*
*----------------------------------------------------------------------
*
* TclpWaitForEvent --