Tcl Source Code

Artifact [bab7331250]
Login

Artifact bab7331250a09f5c664c7f4f8c6f4339e3876cad74800d09d58565f525f205ec:

Attachment "tip511.diff3" to ticket [d1bf3f53a6] added by chw 2021-08-04 22:24:44.
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);
 }
 
 /*
  *----------------------------------------------------------------------
  *
@@ -199,14 +302,14 @@
 				 * completed. */
 {
     AsyncHandler *asyncPtr;
     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
 
-    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 +325,26 @@
      * 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->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 +372,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, &notifierSigMask);
+
 	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(&notifierThread, &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, &notifierSigMask, 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, &notifierSigMask, 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(&notifierMutex);
     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, &notifierSigMask);
+	}
+#else
+	pthread_sigmask(SIG_SETMASK, &notifierSigMask, 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(&notifierMutex);
     triggerPipe = -1;
+    otherPipe = -1;
     pthread_cond_broadcast(&notifierCV);
     pthread_mutex_unlock(&notifierMutex);
 
     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(&notifierInitMutex, NULL);
     pthread_mutex_init(&notifierMutex, NULL);
     pthread_cond_init(&notifierCV, 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 --