summaryrefslogtreecommitdiffstats
path: root/unix/tclUnixNotfy.c
diff options
context:
space:
mode:
Diffstat (limited to 'unix/tclUnixNotfy.c')
-rw-r--r--unix/tclUnixNotfy.c365
1 files changed, 228 insertions, 137 deletions
diff --git a/unix/tclUnixNotfy.c b/unix/tclUnixNotfy.c
index e21e744..5c7f8b5 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 --
*
@@ -105,10 +107,11 @@ 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. */
#endif /* __CYGWIN__ */
+ int waitCVinitialized; /* Variable to flag initialization of the structure */
int eventReady; /* True if an event is ready to be processed.
* Used as condition flag together with waitCV
* above. */
@@ -128,15 +131,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.
@@ -166,7 +160,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
@@ -174,7 +176,7 @@ TCL_DECLARE_MUTEX(notifierMutex)
* terminates.
*/
-static Tcl_Condition notifierCV;
+static pthread_cond_t notifierCV = PTHREAD_COND_INITIALIZER;
/*
* The pollState bits
@@ -202,8 +204,8 @@ 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;
+#if defined(HAVE_PTHREAD_ATFORK)
+static int atForkInit = AT_FORK_INIT_VALUE;
static void AtForkPrepare(void);
static void AtForkParent(void);
static void AtForkChild(void);
@@ -266,6 +268,50 @@ 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(const char *proc)
+{
+ if (!notifierThreadRunning) {
+ pthread_mutex_lock(&notifierInitMutex);
+ if (!notifierThreadRunning) {
+ if (TclpThreadCreate(&notifierThread, NotifierThreadProc, NULL,
+ TCL_THREAD_STACK_DEFAULT, TCL_THREAD_JOINABLE) != TCL_OK) {
+ Tcl_Panic("%s: unable to start notifier thread", proc);
+ }
+
+ 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 */
+
/*
*----------------------------------------------------------------------
*
@@ -291,13 +337,39 @@ Tcl_InitNotifier(void)
tsdPtr->eventReady = 0;
/*
- * Start the Notifier thread if necessary.
+ * Initialize thread specific condition variable for this thread.
*/
+ if (tsdPtr->waitCVinitialized == 0) {
+#ifdef __CYGWIN__
+ WNDCLASS class;
+
+ class.style = 0;
+ class.cbClsExtra = 0;
+ class.cbWndExtra = 0;
+ class.hInstance = TclWinGetTclInstance();
+ class.hbrBackground = NULL;
+ class.lpszMenuName = NULL;
+ class.lpszClassName = L"TclNotifier";
+ class.lpfnWndProc = NotifierProc;
+ class.hIcon = NULL;
+ class.hCursor = NULL;
+
+ RegisterClassW(&class);
+ tsdPtr->hwnd = CreateWindowExW(NULL, class.lpszClassName,
+ class.lpszClassName, 0, 0, 0, 0, 0, NULL, NULL,
+ TclWinGetTclInstance(), NULL);
+ tsdPtr->event = CreateEventW(NULL, 1 /* manual */,
+ 0 /* !signaled */, NULL);
+#else
+ pthread_cond_init(&tsdPtr->waitCV, NULL);
+#endif /* __CYGWIN__ */
+ tsdPtr->waitCVinitialized = 1;
+ }
- Tcl_MutexLock(&notifierMutex);
-#if defined(HAVE_PTHREAD_ATFORK) && !defined(__APPLE__) && !defined(__hpux)
+ pthread_mutex_lock(&notifierInitMutex);
+#if defined(HAVE_PTHREAD_ATFORK)
/*
- * 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.
*/
@@ -310,36 +382,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 (ClientData) tsdPtr;
}
@@ -369,7 +416,7 @@ Tcl_FinalizeNotifier(
#ifdef TCL_THREADS
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
- Tcl_MutexLock(&notifierMutex);
+ pthread_mutex_lock(&notifierInitMutex);
notifierCount--;
/*
@@ -378,37 +425,25 @@ Tcl_FinalizeNotifier(
*/
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;
+ }
}
}
@@ -417,12 +452,14 @@ Tcl_FinalizeNotifier(
*/
#ifdef __CYGWIN__
+ DestroyWindow(tsdPtr->hwnd);
CloseHandle(tsdPtr->event);
#else /* __CYGWIN__ */
- Tcl_ConditionFinalize(&(tsdPtr->waitCV));
+ pthread_cond_destroy(&tsdPtr->waitCV);
#endif /* __CYGWIN__ */
+ tsdPtr->waitCVinitialized = 0;
- Tcl_MutexUnlock(&notifierMutex);
+ pthread_mutex_unlock(&notifierInitMutex);
#endif /* TCL_THREADS */
}
@@ -452,14 +489,14 @@ Tcl_AlertNotifier(
#ifdef TCL_THREADS
ThreadSpecificData *tsdPtr = (ThreadSpecificData *) clientData;
- Tcl_MutexLock(&notifierMutex);
+ pthread_mutex_lock(&notifierMutex);
tsdPtr->eventReady = 1;
#ifdef __CYGWIN__
PostMessageW(tsdPtr->hwnd, 1024, 0, 0);
#else /* __CYGWIN__ */
- Tcl_ConditionNotify(&tsdPtr->waitCV);
+ pthread_cond_broadcast(&tsdPtr->waitCV);
#endif /* __CYGWIN__ */
- Tcl_MutexUnlock(&notifierMutex);
+ pthread_mutex_unlock(&notifierMutex);
#endif /* TCL_THREADS */
}
@@ -517,6 +554,11 @@ Tcl_ServiceModeHook(
int mode) /* Either TCL_SERVICE_ALL, or
* TCL_SERVICE_NONE. */
{
+#if TCL_THREADS
+ if (mode == TCL_SERVICE_ALL) {
+ StartNotifierThread("Tcl_ServiceModeHook");
+ }
+#endif
}
/*
@@ -730,6 +772,7 @@ FileHandlerEventProc(
*/
tsdPtr = TCL_TSD_INIT(&dataKey);
+
for (filePtr = tsdPtr->firstFileHandlerPtr; filePtr != NULL;
filePtr = filePtr->nextPtr) {
if (filePtr->fd != fileEvPtr->fd) {
@@ -853,11 +896,11 @@ Tcl_WaitForEvent(
timeoutPtr = &timeout;
} else if (tsdPtr->numFdBits == 0) {
/*
- * If there are no threads, no timeout, and no fds registered,
- * then there are no events possible and we must avoid deadlock.
- * Note that this is not entirely correct because there might be a
- * signal that could interrupt the select call, but we don't
- * handle that case if we aren't using threads.
+ * If there are no threads, no timeout, and no fds registered,
+ * then there are no events possible and we must avoid deadlock.
+ * Note that this is not entirely correct because there might be a
+ * signal that could interrupt the select call, but we don't
+ * handle that case if we aren't using threads.
*/
return -1;
@@ -868,35 +911,13 @@ 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("Tcl_WaitForEvent");
-#ifdef __CYGWIN__
- if (!tsdPtr->hwnd) {
- WNDCLASS class;
-
- class.style = 0;
- class.cbClsExtra = 0;
- class.cbWndExtra = 0;
- class.hInstance = TclWinGetTclInstance();
- class.hbrBackground = NULL;
- class.lpszMenuName = NULL;
- class.lpszClassName = L"TclNotifier";
- class.lpfnWndProc = NotifierProc;
- class.hIcon = NULL;
- class.hCursor = NULL;
-
- RegisterClassW(&class);
- tsdPtr->hwnd = CreateWindowExW(NULL, class.lpszClassName,
- class.lpszClassName, 0, 0, 0, 0, 0, NULL, NULL,
- TclWinGetTclInstance(), NULL);
- tsdPtr->event = CreateEventW(NULL, 1 /* manual */,
- 0 /* !signaled */, NULL);
- }
-#endif /* __CYGWIN__ */
-
- Tcl_MutexLock(&notifierMutex);
+ pthread_mutex_lock(&notifierMutex);
if (timePtr != NULL && timePtr->sec == 0 && (timePtr->usec == 0
#if defined(__APPLE__) && defined(__LP64__)
@@ -961,12 +982,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;
@@ -1061,6 +1093,7 @@ Tcl_WaitForEvent(
if (filePtr->readyMask == 0) {
FileHandlerEvent *fileEvPtr =
(FileHandlerEvent *) ckalloc(sizeof(FileHandlerEvent));
+
fileEvPtr->header.proc = FileHandlerEventProc;
fileEvPtr->fd = filePtr->fd;
Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL);
@@ -1068,7 +1101,7 @@ Tcl_WaitForEvent(
filePtr->readyMask = mask;
}
#ifdef TCL_THREADS
- Tcl_MutexUnlock(&notifierMutex);
+ pthread_mutex_unlock(&notifierMutex);
#endif /* TCL_THREADS */
return 0;
}
@@ -1139,15 +1172,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.
@@ -1163,7 +1196,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) {
@@ -1190,7 +1223,7 @@ NotifierThreadProc(
timePtr = &poll;
}
}
- Tcl_MutexUnlock(&notifierMutex);
+ pthread_mutex_unlock(&notifierMutex);
/*
* Set up the select mask to include the receive pipe.
@@ -1214,7 +1247,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;
@@ -1261,11 +1294,11 @@ NotifierThreadProc(
#ifdef __CYGWIN__
PostMessageW(tsdPtr->hwnd, 1024, 0, 0);
#else /* __CYGWIN__ */
- 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
@@ -1294,15 +1327,15 @@ 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);
}
-#if defined(HAVE_PTHREAD_ATFORK) && !defined(__APPLE__) && !defined(__hpux)
+#if defined(HAVE_PTHREAD_ATFORK)
/*
*----------------------------------------------------------------------
*
@@ -1322,9 +1355,9 @@ NotifierThreadProc(
static void
AtForkPrepare(void)
{
- Tcl_MutexLock(&notifierMutex);
- TclpMasterLock();
- TclpMutexLock();
+#if RESET_ATFORK_MUTEX == 0
+ pthread_mutex_lock(&notifierInitMutex);
+#endif
}
/*
@@ -1346,9 +1379,9 @@ AtForkPrepare(void)
static void
AtForkParent(void)
{
- TclpMutexUnlock();
- TclpMasterUnlock();
- Tcl_MutexUnlock(&notifierMutex);
+#if RESET_ATFORK_MUTEX == 0
+ pthread_mutex_unlock(&notifierInitMutex);
+#endif
}
/*
@@ -1370,9 +1403,67 @@ 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) {
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+ 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.
+ */
+#ifdef __CYGWIN__
+ DestroyWindow(tsdPtr->hwnd);
+ tsdPtr->hwnd = CreateWindowExW(NULL, L"TclNotifier",
+ L"TclNotifier", 0, 0, 0, 0, 0, NULL, NULL,
+ TclWinGetTclInstance(), NULL);
+ ResetEvent(tsdPtr->event);
+#else
+ pthread_cond_destroy(&tsdPtr->waitCV);
+ pthread_cond_init(&tsdPtr->waitCV, NULL);
+#endif
+ /*
+ * In case, we had multiple threads running before the fork,
+ * make sure, we don't try to reach out to their thread local data.
+ */
+ tsdPtr->nextPtr = tsdPtr->prevPtr = NULL;
+
+ /*
+ * The list of registered event handlers at fork time is in
+ * tsdPtr->firstFileHandlerPtr;
+ */
+ }
+ }
+
Tcl_InitNotifier();
}
#endif /* HAVE_PTHREAD_ATFORK */