summaryrefslogtreecommitdiffstats
path: root/unix/tclUnixNotfy.c
diff options
context:
space:
mode:
Diffstat (limited to 'unix/tclUnixNotfy.c')
-rw-r--r--unix/tclUnixNotfy.c333
1 files changed, 226 insertions, 107 deletions
diff --git a/unix/tclUnixNotfy.c b/unix/tclUnixNotfy.c
index 17fdc95..693eeac 100644
--- a/unix/tclUnixNotfy.c
+++ b/unix/tclUnixNotfy.c
@@ -1,3 +1,5 @@
+#define AT_FORK_INIT_VALUE 0
+#define RESET_ATFORK_MUTEX 1
/*
* tclUnixNotify.c --
*
@@ -15,6 +17,7 @@
#ifndef HAVE_COREFOUNDATION /* Darwin/Mac OS X CoreFoundation notifier is
* in tclMacOSXNotify.c */
#include <signal.h>
+#include <assert.h>
/*
* This structure is used to keep track of the notifier info for a registered
@@ -97,9 +100,10 @@ typedef struct ThreadSpecificData {
* by sending this event. */
void *hwnd; /* Messaging window. */
#else /* !__CYGWIN__ */
- Tcl_Condition waitCV; /* Any other thread alerts a notifier that an
+ pthread_cond_t waitCV; /* Any other thread alerts a notifier that an
* event is ready to be processed by signaling
* this condition variable. */
+ int waitCVinitialized; /* Variable to flag initialization of the structure */
#endif /* __CYGWIN__ */
int eventReady; /* True if an event is ready to be processed.
* Used as condition flag together with waitCV
@@ -120,15 +124,6 @@ static Tcl_ThreadDataKey dataKey;
static int notifierCount = 0;
/*
- * The following static stores the process ID of the initialized notifier
- * thread. If it changes, we have passed a fork and we should start a new
- * notifier thread.
- *
- * You must hold the notifierMutex lock before accessing this variable.
- */
-static pid_t processIDInitialized = 0;
-
-/*
* The following variable points to the head of a doubly-linked list of
* ThreadSpecificData structures for all threads that are currently waiting on
* an event.
@@ -158,7 +153,15 @@ static int triggerPipe = -1;
* The notifierMutex locks access to all of the global notifier state.
*/
-TCL_DECLARE_MUTEX(notifierMutex)
+pthread_mutex_t notifierInitMutex = PTHREAD_MUTEX_INITIALIZER;
+pthread_mutex_t notifierMutex = PTHREAD_MUTEX_INITIALIZER;
+/*
+ * The following static indicates if the notifier thread is running.
+ *
+ * You must hold the notifierInitMutex before accessing this variable.
+ */
+
+static int notifierThreadRunning = 0;
/*
* The notifier thread signals the notifierCV when it has finished
@@ -166,7 +169,7 @@ TCL_DECLARE_MUTEX(notifierMutex)
* terminates.
*/
-static Tcl_Condition notifierCV;
+static pthread_cond_t notifierCV = PTHREAD_COND_INITIALIZER;
/*
* The pollState bits
@@ -195,7 +198,7 @@ static Tcl_ThreadId notifierThread;
#ifdef TCL_THREADS
static void NotifierThreadProc(ClientData clientData);
#if defined(HAVE_PTHREAD_ATFORK) && !defined(__APPLE__) && !defined(__hpux)
-static int atForkInit = 0;
+static int atForkInit = AT_FORK_INIT_VALUE;
static void AtForkPrepare(void);
static void AtForkParent(void);
static void AtForkChild(void);
@@ -258,6 +261,49 @@ static DWORD __stdcall NotifierProc(void *hwnd, unsigned int message,
void *wParam, void *lParam);
#endif /* TCL_THREADS && __CYGWIN__ */
+#if TCL_THREADS
+/*
+ *----------------------------------------------------------------------
+ *
+ * StartNotifierThread --
+ *
+ * Start a notfier thread and wait for the notifier pipe to be created.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Running Thread.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+StartNotifierThread(void)
+{
+
+ pthread_mutex_lock(&notifierInitMutex);
+ if (!notifierThreadRunning) {
+ if (TclpThreadCreate(&notifierThread, NotifierThreadProc, NULL,
+ TCL_THREAD_STACK_DEFAULT, TCL_THREAD_JOINABLE) != TCL_OK) {
+ Tcl_Panic("Tcl_InitNotifier: unable to start notifier thread");
+ }
+
+ pthread_mutex_lock(&notifierMutex);
+ /*
+ * Wait for the notifier pipe to be created.
+ */
+
+ while (triggerPipe < 0) {
+ pthread_cond_wait(&notifierCV, &notifierMutex);
+ }
+ pthread_mutex_unlock(&notifierMutex);
+
+ notifierThreadRunning = 1;
+ }
+ pthread_mutex_unlock(&notifierInitMutex);
+}
+#endif /* TCL_THREADS */
+
/*
*----------------------------------------------------------------------
*
@@ -285,14 +331,20 @@ Tcl_InitNotifier(void)
#ifdef TCL_THREADS
tsdPtr->eventReady = 0;
+#ifndef __CYGWIN__
/*
- * Start the Notifier thread if necessary.
+ * Initialize thread specific condition variable for this thread.
*/
+ if (tsdPtr->waitCVinitialized == 0) {
+ pthread_cond_init(&tsdPtr->waitCV, NULL);
+ tsdPtr->waitCVinitialized = 1;
+ }
+#endif
- Tcl_MutexLock(&notifierMutex);
+ pthread_mutex_lock(&notifierInitMutex);
#if defined(HAVE_PTHREAD_ATFORK) && !defined(__APPLE__) && !defined(__hpux)
/*
- * Install pthread_atfork handlers to reinitialize the notifier in the
+ * Install pthread_atfork handlers to clean up the notifier in the
* child of a fork.
*/
@@ -305,36 +357,11 @@ Tcl_InitNotifier(void)
atForkInit = 1;
}
#endif /* HAVE_PTHREAD_ATFORK */
- /*
- * Check if my process id changed, e.g. I was forked
- * In this case, restart the notifier thread and close the
- * pipe to the original notifier thread
- */
- if (notifierCount > 0 && processIDInitialized != getpid()) {
- Tcl_ConditionFinalize(&notifierCV);
- notifierCount = 0;
- processIDInitialized = 0;
- close(triggerPipe);
- triggerPipe = -1;
- }
- if (notifierCount == 0) {
- if (TclpThreadCreate(&notifierThread, NotifierThreadProc, NULL,
- TCL_THREAD_STACK_DEFAULT, TCL_THREAD_JOINABLE) != TCL_OK) {
- Tcl_Panic("Tcl_InitNotifier: unable to start notifier thread");
- }
- processIDInitialized = getpid();
- }
- notifierCount++;
- /*
- * Wait for the notifier pipe to be created.
- */
+ notifierCount++;
- while (triggerPipe < 0) {
- Tcl_ConditionWait(&notifierCV, &notifierMutex, NULL);
- }
+ pthread_mutex_unlock(&notifierInitMutex);
- Tcl_MutexUnlock(&notifierMutex);
#endif /* TCL_THREADS */
return tsdPtr;
}
@@ -369,46 +396,42 @@ Tcl_FinalizeNotifier(
#ifdef TCL_THREADS
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
- Tcl_MutexLock(&notifierMutex);
+ pthread_mutex_lock(&notifierInitMutex);
notifierCount--;
/*
+ * Check if FinializeNotifier was called without a prior InitNotifier
+ * in this thread.
+ */
+#ifndef __CYGWIN__
+ assert(tsdPtr->waitCVinitialized == 1);
+#endif
+
+ /*
* If this is the last thread to use the notifier, close the notifier
* pipe and wait for the background thread to terminate.
*/
if (notifierCount == 0) {
- int result;
-
- if (triggerPipe < 0) {
- Tcl_Panic("Tcl_FinalizeNotifier: %s",
- "notifier pipe not initialized");
- }
-
- /*
- * Send "q" message to the notifier thread so that it will
- * terminate. The notifier will return from its call to select()
- * and notice that a "q" message has arrived, it will then close
- * its side of the pipe and terminate its thread. Note the we can
- * not just close the pipe and check for EOF in the notifier thread
- * because if a background child process was created with exec,
- * select() would not register the EOF on the pipe until the child
- * processes had terminated. [Bug: 4139] [Bug: 1222872]
- */
- if (write(triggerPipe, "q", 1) != 1) {
- Tcl_Panic("Tcl_FinalizeNotifier: %s",
- "unable to write q to triggerPipe");
- }
- close(triggerPipe);
- while(triggerPipe >= 0) {
- Tcl_ConditionWait(&notifierCV, &notifierMutex, NULL);
- }
+ if (triggerPipe != -1) {
+ if (write(triggerPipe, "q", 1) != 1) {
+ Tcl_Panic("Tcl_FinalizeNotifier: %s",
+ "unable to write q to triggerPipe");
+ }
+ close(triggerPipe);
+ while(triggerPipe != -1) {
+ pthread_cond_wait(&notifierCV, &notifierMutex);
+ }
+ if (notifierThreadRunning) {
+ int result = pthread_join((pthread_t) notifierThread, NULL);
- result = Tcl_JoinThread(notifierThread, NULL);
- if (result) {
- Tcl_Panic("Tcl_FinalizeNotifier: %s",
- "unable to join notifier thread");
+ if (result) {
+ Tcl_Panic("Tcl_FinalizeNotifier: unable to join notifier "
+ "thread");
+ }
+ notifierThreadRunning = 0;
+ }
}
}
@@ -419,10 +442,10 @@ Tcl_FinalizeNotifier(
#ifdef __CYGWIN__
CloseHandle(tsdPtr->event);
#else /* __CYGWIN__ */
- Tcl_ConditionFinalize(&(tsdPtr->waitCV));
+ pthread_cond_destroy(&tsdPtr->waitCV);
#endif /* __CYGWIN__ */
- Tcl_MutexUnlock(&notifierMutex);
+ pthread_mutex_unlock(&notifierInitMutex);
#endif /* TCL_THREADS */
}
}
@@ -457,14 +480,15 @@ Tcl_AlertNotifier(
#ifdef TCL_THREADS
ThreadSpecificData *tsdPtr = clientData;
- Tcl_MutexLock(&notifierMutex);
+ pthread_mutex_lock(&notifierMutex);
tsdPtr->eventReady = 1;
+
# ifdef __CYGWIN__
PostMessageW(tsdPtr->hwnd, 1024, 0, 0);
# else
- Tcl_ConditionNotify(&tsdPtr->waitCV);
+ pthread_cond_broadcast(&tsdPtr->waitCV);
# endif /* __CYGWIN__ */
- Tcl_MutexUnlock(&notifierMutex);
+ pthread_mutex_unlock(&notifierMutex);
#endif /* TCL_THREADS */
}
}
@@ -527,8 +551,10 @@ Tcl_ServiceModeHook(
if (tclNotifierHooks.serviceModeHookProc) {
tclNotifierHooks.serviceModeHookProc(mode);
return;
- } else {
- /* Does nothing in this implementation. */
+ } else if (mode == TCL_SERVICE_ALL) {
+#if TCL_THREADS
+ StartNotifierThread();
+#endif
}
}
@@ -566,6 +592,12 @@ Tcl_CreateFileHandler(
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
FileHandler *filePtr;
+ /*
+ * Check if InitNotifier was called before in this thread
+ */
+#ifndef __CYGWIN__
+ assert(tsdPtr->waitCVinitialized == 1);
+#endif
for (filePtr = tsdPtr->firstFileHandlerPtr; filePtr != NULL;
filePtr = filePtr->nextPtr) {
if (filePtr->fd == fd) {
@@ -638,6 +670,13 @@ Tcl_DeleteFileHandler(
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
/*
+ * Check if InitNotifier was called before in this thread
+ */
+#ifndef __CYGWIN__
+ assert(tsdPtr->waitCVinitialized == 1);
+#endif
+
+ /*
* Find the entry for the given file (and return if there isn't one).
*/
@@ -741,6 +780,14 @@ FileHandlerEventProc(
*/
tsdPtr = TCL_TSD_INIT(&dataKey);
+
+ /*
+ * Check if InitNotifier was called before in this thread
+ */
+#ifndef __CYGWIN__
+ assert(tsdPtr->waitCVinitialized == 1);
+#endif
+
for (filePtr = tsdPtr->firstFileHandlerPtr; filePtr != NULL;
filePtr = filePtr->nextPtr) {
if (filePtr->fd != fileEvPtr->fd) {
@@ -840,6 +887,13 @@ Tcl_WaitForEvent(
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
/*
+ * Check if InitNotifier was called before in this thread
+ */
+#ifndef __CYGWIN__
+ assert(tsdPtr->waitCVinitialized == 1);
+#endif
+
+ /*
* Set up the timeout structure. Note that if there are no events to
* check for, we return with a negative result rather than blocking
* forever.
@@ -878,9 +932,11 @@ Tcl_WaitForEvent(
#ifdef TCL_THREADS
/*
- * Place this thread on the list of interested threads, signal the
- * notifier thread, and wait for a response or a timeout.
+ * Start notifier thread and place this thread on the list of
+ * interested threads, signal the notifier thread, and wait for a
+ * response or a timeout.
*/
+ StartNotifierThread();
#ifdef __CYGWIN__
if (!tsdPtr->hwnd) {
@@ -906,7 +962,7 @@ Tcl_WaitForEvent(
}
#endif /* __CYGWIN */
- Tcl_MutexLock(&notifierMutex);
+ pthread_mutex_lock(&notifierInitMutex);
if (timePtr != NULL && timePtr->sec == 0 && (timePtr->usec == 0
#if defined(__APPLE__) && defined(__LP64__)
@@ -971,12 +1027,23 @@ Tcl_WaitForEvent(
} else {
timeout = 0xFFFFFFFF;
}
- Tcl_MutexUnlock(&notifierMutex);
+ pthread_mutex_unlock(&notifierMutex);
MsgWaitForMultipleObjects(1, &tsdPtr->event, 0, timeout, 1279);
- Tcl_MutexLock(&notifierMutex);
+ pthread_mutex_lock(&notifierMutex);
}
#else
- Tcl_ConditionWait(&tsdPtr->waitCV, &notifierMutex, timePtr);
+ if (timePtr != NULL) {
+ Tcl_Time now;
+ struct timespec ptime;
+
+ Tcl_GetTime(&now);
+ ptime.tv_sec = timePtr->sec + now.sec + (timePtr->usec + now.usec) / 1000000;
+ ptime.tv_nsec = 1000 * ((timePtr->usec + now.usec) % 1000000);
+
+ pthread_cond_timedwait(&tsdPtr->waitCV, &notifierMutex, &ptime);
+ } else {
+ pthread_cond_wait(&tsdPtr->waitCV, &notifierMutex);
+ }
#endif /* __CYGWIN__ */
}
tsdPtr->eventReady = 0;
@@ -1079,7 +1146,7 @@ Tcl_WaitForEvent(
filePtr->readyMask = mask;
}
#ifdef TCL_THREADS
- Tcl_MutexUnlock(&notifierMutex);
+ pthread_mutex_unlock(&notifierInitMutex);
#endif /* TCL_THREADS */
return 0;
}
@@ -1151,15 +1218,15 @@ NotifierThreadProc(
* Install the write end of the pipe into the global variable.
*/
- Tcl_MutexLock(&notifierMutex);
+ pthread_mutex_lock(&notifierMutex);
triggerPipe = fds[1];
/*
* Signal any threads that are waiting.
*/
- Tcl_ConditionNotify(&notifierCV);
- Tcl_MutexUnlock(&notifierMutex);
+ pthread_cond_broadcast(&notifierCV);
+ pthread_mutex_unlock(&notifierMutex);
/*
* Look for file events and report them to interested threads.
@@ -1175,7 +1242,7 @@ NotifierThreadProc(
* notifiers.
*/
- Tcl_MutexLock(&notifierMutex);
+ pthread_mutex_lock(&notifierMutex);
timePtr = NULL;
for (tsdPtr = waitingListPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) {
for (i = tsdPtr->numFdBits-1; i >= 0; --i) {
@@ -1202,7 +1269,7 @@ NotifierThreadProc(
timePtr = &poll;
}
}
- Tcl_MutexUnlock(&notifierMutex);
+ pthread_mutex_unlock(&notifierMutex);
/*
* Set up the select mask to include the receive pipe.
@@ -1226,7 +1293,7 @@ NotifierThreadProc(
* Alert any threads that are waiting on a ready file descriptor.
*/
- Tcl_MutexLock(&notifierMutex);
+ pthread_mutex_lock(&notifierMutex);
for (tsdPtr = waitingListPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) {
found = 0;
@@ -1273,11 +1340,11 @@ NotifierThreadProc(
#ifdef __CYGWIN__
PostMessageW(tsdPtr->hwnd, 1024, 0, 0);
#else
- Tcl_ConditionNotify(&tsdPtr->waitCV);
+ pthread_cond_broadcast(&tsdPtr->waitCV);
#endif /* __CYGWIN__ */
}
}
- Tcl_MutexUnlock(&notifierMutex);
+ pthread_mutex_unlock(&notifierMutex);
/*
* Consume the next byte from the notifier pipe if the pipe was
@@ -1306,10 +1373,10 @@ NotifierThreadProc(
*/
close(receivePipe);
- Tcl_MutexLock(&notifierMutex);
+ pthread_mutex_lock(&notifierMutex);
triggerPipe = -1;
- Tcl_ConditionNotify(&notifierCV);
- Tcl_MutexUnlock(&notifierMutex);
+ pthread_cond_broadcast(&notifierCV);
+ pthread_mutex_unlock(&notifierMutex);
TclpThreadExit(0);
}
@@ -1334,9 +1401,9 @@ NotifierThreadProc(
static void
AtForkPrepare(void)
{
- Tcl_MutexLock(&notifierMutex);
- TclpMasterLock();
- TclpMutexLock();
+#if RESET_ATFORK_MUTEX == 0
+ pthread_mutex_lock(&notifierInitMutex);
+#endif
}
/*
@@ -1358,9 +1425,9 @@ AtForkPrepare(void)
static void
AtForkParent(void)
{
- TclpMutexUnlock();
- TclpMasterUnlock();
- Tcl_MutexUnlock(&notifierMutex);
+#if RESET_ATFORK_MUTEX == 0
+ pthread_mutex_unlock(&notifierInitMutex);
+#endif
}
/*
@@ -1382,9 +1449,61 @@ AtForkParent(void)
static void
AtForkChild(void)
{
- TclpMutexUnlock();
- TclpMasterUnlock();
- TclMutexUnlockAndFinalize(&notifierMutex);
+ if (notifierThreadRunning == 1) {
+ pthread_cond_destroy(&notifierCV);
+ }
+#if RESET_ATFORK_MUTEX == 0
+ pthread_mutex_unlock(&notifierInitMutex);
+#else
+ pthread_mutex_init(&notifierInitMutex, NULL);
+ pthread_mutex_init(&notifierMutex, NULL);
+#endif
+ pthread_cond_init(&notifierCV, NULL);
+
+ /*
+ * notifierThreadRunning == 1: thread is running, (there might be data in notifier lists)
+ * atForkInit == 0: InitNotifier was never called
+ * notifierCount != 0: unbalanced InitNotifier() / FinalizeNotifier calls
+ * waitingListPtr != 0: there are threads currently waiting for events.
+ */
+
+ if (atForkInit == 1) {
+
+ notifierCount = 0;
+ if (notifierThreadRunning == 1) {
+#ifndef __CYGWIN__
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+#endif
+ notifierThreadRunning = 0;
+
+ close(triggerPipe);
+ triggerPipe = -1;
+ /*
+ * The waitingListPtr might contain event info from multiple
+ * threads, which are invalid here, so setting it to NULL is not
+ * unreasonable.
+ */
+ waitingListPtr = NULL;
+
+ /*
+ * The tsdPtr from before the fork is copied as well. But since
+ * we are paranoic, we don't trust its condvar and reset it.
+ */
+#ifndef __CYGWIN__
+ assert(tsdPtr->waitCVinitialized == 1);
+ pthread_cond_destroy(&tsdPtr->waitCV);
+ pthread_cond_init(&tsdPtr->waitCV, NULL);
+#endif
+ /*
+ * The list of registered event handlers at fork time is in
+ * tsdPtr->firstFileHandlerPtr;
+ */
+ }
+ }
+ assert(notifierCount == 0);
+ assert(triggerPipe == -1);
+ assert(waitingListPtr == NULL);
+
Tcl_InitNotifier();
}
#endif /* HAVE_PTHREAD_ATFORK */