diff options
Diffstat (limited to 'unix/tclUnixNotfy.c')
| -rw-r--r-- | unix/tclUnixNotfy.c | 292 |
1 files changed, 210 insertions, 82 deletions
diff --git a/unix/tclUnixNotfy.c b/unix/tclUnixNotfy.c index ca95f40..b234667 100644 --- a/unix/tclUnixNotfy.c +++ b/unix/tclUnixNotfy.c @@ -104,7 +104,7 @@ typedef struct ThreadSpecificData { int eventReady; /* True if an event is ready to be processed. * Used as condition flag together with waitCV * above. */ -#endif +#endif /* TCL_THREADS */ } ThreadSpecificData; static Tcl_ThreadDataKey dataKey; @@ -120,6 +120,15 @@ 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. @@ -184,28 +193,21 @@ static Tcl_ThreadId notifierThread; */ #ifdef TCL_THREADS -static void NotifierThreadProc(ClientData clientData); -#endif -static int FileHandlerEventProc(Tcl_Event *evPtr, int flags); - +static void NotifierThreadProc(ClientData clientData); +#if defined(HAVE_PTHREAD_ATFORK) && !defined(__APPLE__) && !defined(__hpux) +static int atForkInit = 0; +static void AtForkPrepare(void); +static void AtForkParent(void); +static void AtForkChild(void); +#endif /* HAVE_PTHREAD_ATFORK */ +#endif /* TCL_THREADS */ +static int FileHandlerEventProc(Tcl_Event *evPtr, int flags); + /* - *---------------------------------------------------------------------- - * - * Tcl_InitNotifier -- - * - * Initializes the platform specific notifier state. - * - * Results: - * Returns a handle to the notifier state for this thread. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- + * Import of Windows API when building threaded with Cygwin. */ #if defined(TCL_THREADS) && defined(__CYGWIN__) - typedef struct { void *hwnd; unsigned int *message; @@ -217,34 +219,60 @@ typedef struct { } MSG; typedef struct { - unsigned int style; - void *lpfnWndProc; - int cbClsExtra; - int cbWndExtra; - void *hInstance; - void *hIcon; - void *hCursor; - void *hbrBackground; - void *lpszMenuName; - void *lpszClassName; + unsigned int style; + void *lpfnWndProc; + int cbClsExtra; + int cbWndExtra; + void *hInstance; + void *hIcon; + void *hCursor; + void *hbrBackground; + void *lpszMenuName; + const void *lpszClassName; } WNDCLASS; -extern unsigned char __stdcall PeekMessageW(MSG *, void *, int, int, int); -extern unsigned char __stdcall GetMessageW(MSG *, void *, int, int); -extern unsigned char __stdcall TranslateMessage(const MSG *); -extern int __stdcall DispatchMessageW(const MSG *); -extern void __stdcall PostQuitMessage(int); -extern void * __stdcall CreateWindowExW(void *, void *, void *, DWORD, int, int, int, int, void *, void *, void *, void *); -extern unsigned char __stdcall DestroyWindow(void *); -extern unsigned char __stdcall PostMessageW(void *, unsigned int, void *, void *); -extern void *__stdcall RegisterClassW(const WNDCLASS *); -extern DWORD __stdcall DefWindowProcW(void *, int, void *, void *); -extern void *__stdcall CreateEventW(void *, unsigned char, unsigned char, void *); -extern void __stdcall CloseHandle(void *); -extern void __stdcall MsgWaitForMultipleObjects(DWORD, void *, unsigned char, DWORD, DWORD); -extern unsigned char __stdcall ResetEvent(void *); - -#endif +extern void __stdcall CloseHandle(void *); +extern void *__stdcall CreateEventW(void *, unsigned char, unsigned char, + void *); +extern void * __stdcall CreateWindowExW(void *, const void *, const void *, + DWORD, int, int, int, int, void *, void *, void *, void *); +extern DWORD __stdcall DefWindowProcW(void *, int, void *, void *); +extern unsigned char __stdcall DestroyWindow(void *); +extern int __stdcall DispatchMessageW(const MSG *); +extern unsigned char __stdcall GetMessageW(MSG *, void *, int, int); +extern void __stdcall MsgWaitForMultipleObjects(DWORD, void *, + unsigned char, DWORD, DWORD); +extern unsigned char __stdcall PeekMessageW(MSG *, void *, int, int, int); +extern unsigned char __stdcall PostMessageW(void *, unsigned int, void *, + void *); +extern void __stdcall PostQuitMessage(int); +extern void *__stdcall RegisterClassW(const WNDCLASS *); +extern unsigned char __stdcall ResetEvent(void *); +extern unsigned char __stdcall TranslateMessage(const MSG *); + +/* + * Threaded-cygwin specific functions in this file: + */ + +static DWORD __stdcall NotifierProc(void *hwnd, unsigned int message, + void *wParam, void *lParam); +#endif /* TCL_THREADS && __CYGWIN__ */ + +/* + *---------------------------------------------------------------------- + * + * Tcl_InitNotifier -- + * + * Initializes the platform specific notifier state. + * + * Results: + * Returns a handle to the notifier state for this thread. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ ClientData Tcl_InitNotifier(void) @@ -262,11 +290,38 @@ Tcl_InitNotifier(void) */ Tcl_MutexLock(¬ifierMutex); +#if defined(HAVE_PTHREAD_ATFORK) && !defined(__APPLE__) && !defined(__hpux) + /* + * Install pthread_atfork handlers to reinitialize the notifier in the + * child of a fork. + */ + + if (!atForkInit) { + int result = pthread_atfork(AtForkPrepare, AtForkParent, AtForkChild); + + if (result) { + Tcl_Panic("Tcl_InitNotifier: pthread_atfork failed"); + } + 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()) { + notifierCount = 0; + processIDInitialized = 0; + close(triggerPipe); + triggerPipe = -1; + } if (notifierCount == 0) { if (TclpThreadCreate(¬ifierThread, NotifierThreadProc, NULL, TCL_THREAD_STACK_DEFAULT, TCL_THREAD_JOINABLE) != TCL_OK) { Tcl_Panic("Tcl_InitNotifier: unable to start notifier thread"); } + processIDInitialized = getpid(); } notifierCount++; @@ -403,11 +458,11 @@ Tcl_AlertNotifier( Tcl_MutexLock(¬ifierMutex); tsdPtr->eventReady = 1; -#ifdef __CYGWIN__ +# ifdef __CYGWIN__ PostMessageW(tsdPtr->hwnd, 1024, 0, 0); -#else +# else Tcl_ConditionNotify(&tsdPtr->waitCV); -#endif +# endif /* __CYGWIN__ */ Tcl_MutexUnlock(¬ifierMutex); #endif /* TCL_THREADS */ } @@ -732,12 +787,12 @@ NotifierProc( * Process all of the runnable events. */ - tsdPtr->eventReady = 1; + tsdPtr->eventReady = 1; Tcl_ServiceAll(); return 0; } -#endif /* __CYGWIN__ */ - +#endif /* TCL_THREADS && __CYGWIN__ */ + /* *---------------------------------------------------------------------- * @@ -768,9 +823,9 @@ Tcl_WaitForEvent( Tcl_Time vTime; #ifdef TCL_THREADS int waitForFiles; -# ifdef __CYGWIN__ - MSG msg; -# endif +# ifdef __CYGWIN__ + MSG msg; +# endif /* __CYGWIN__ */ #else /* * Impl. notes: timeout & timeoutPtr are used if, and only if threads @@ -792,8 +847,8 @@ Tcl_WaitForEvent( if (timePtr != NULL) { /* * TIP #233 (Virtualized Time). Is virtual time in effect? And do - * we actually have something to scale? If yes to both then we call - * the handler to do this scaling. + * we actually have something to scale? If yes to both then we + * call the handler to do this scaling. */ if (timePtr->sec != 0 || timePtr->usec != 0) { @@ -807,17 +862,17 @@ 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; } else { timeoutPtr = NULL; -#endif /* TCL_THREADS */ +#endif /* !TCL_THREADS */ } #ifdef TCL_THREADS @@ -828,7 +883,7 @@ Tcl_WaitForEvent( #ifdef __CYGWIN__ if (!tsdPtr->hwnd) { - WNDCLASS class; + WNDCLASS class; class.style = 0; class.cbClsExtra = 0; @@ -842,24 +897,24 @@ Tcl_WaitForEvent( class.hCursor = NULL; RegisterClassW(&class); - tsdPtr->hwnd = CreateWindowExW(NULL, class.lpszClassName, class.lpszClassName, - 0, 0, 0, 0, 0, NULL, NULL, TclWinGetTclInstance(), NULL); + 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 + } +#endif /* __CYGWIN */ Tcl_MutexLock(¬ifierMutex); if (timePtr != NULL && timePtr->sec == 0 && (timePtr->usec == 0 #if defined(__APPLE__) && defined(__LP64__) /* - * On 64-bit Darwin, pthread_cond_timedwait() appears to have a - * bug that causes it to wait forever when passed an absolute - * time which has already been exceeded by the system time; as - * a workaround, when given a very brief timeout, just do a - * poll. [Bug 1457797] + * On 64-bit Darwin, pthread_cond_timedwait() appears to have + * a bug that causes it to wait forever when passed an + * absolute time which has already been exceeded by the system + * time; as a workaround, when given a very brief timeout, + * just do a poll. [Bug 1457797] */ || timePtr->usec < 10 #endif /* __APPLE__ && __LP64__ */ @@ -883,8 +938,8 @@ Tcl_WaitForEvent( if (waitForFiles) { /* * Add the ThreadSpecificData structure of this thread to the list - * of ThreadSpecificData structures of all threads that are waiting - * on file events. + * of ThreadSpecificData structures of all threads that are + * waiting on file events. */ tsdPtr->nextPtr = waitingListPtr; @@ -909,6 +964,7 @@ Tcl_WaitForEvent( #ifdef __CYGWIN__ if (!PeekMessageW(&msg, NULL, 0, 0, 0)) { DWORD timeout; + if (timePtr) { timeout = timePtr->sec * 1000 + timePtr->usec / 1000; } else { @@ -920,7 +976,7 @@ Tcl_WaitForEvent( } #else Tcl_ConditionWait(&tsdPtr->waitCV, ¬ifierMutex, timePtr); -#endif +#endif /* __CYGWIN__ */ } tsdPtr->eventReady = 0; @@ -929,17 +985,20 @@ Tcl_WaitForEvent( /* * Retrieve and dispatch the message. */ + DWORD result = GetMessageW(&msg, NULL, 0, 0); + if (result == 0) { PostQuitMessage(msg.wParam); /* What to do here? */ - } else if (result != (DWORD)-1) { + } else if (result != (DWORD) -1) { TranslateMessage(&msg); DispatchMessageW(&msg); } } ResetEvent(tsdPtr->event); -#endif +#endif /* __CYGWIN__ */ + if (waitForFiles && tsdPtr->onList) { /* * Remove the ThreadSpecificData structure of this thread from the @@ -1211,9 +1270,9 @@ NotifierThreadProc( tsdPtr->pollState = 0; } #ifdef __CYGWIN__ - PostMessageW(tsdPtr->hwnd, 1024, 0, 0); -#else /* __CYGWIN__ */ - Tcl_ConditionNotify(&tsdPtr->waitCV); + PostMessageW(tsdPtr->hwnd, 1024, 0, 0); +#else + Tcl_ConditionNotify(&tsdPtr->waitCV); #endif /* __CYGWIN__ */ } } @@ -1253,9 +1312,78 @@ NotifierThreadProc( TclpThreadExit(0); } + +#if defined(HAVE_PTHREAD_ATFORK) && !defined(__APPLE__) && !defined(__hpux) +/* + *---------------------------------------------------------------------- + * + * AtForkPrepare -- + * + * Lock the notifier in preparation for a fork. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static void +AtForkPrepare(void) +{ +} + +/* + *---------------------------------------------------------------------- + * + * AtForkParent -- + * + * Unlock the notifier in the parent after a fork. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static void +AtForkParent(void) +{ +} + +/* + *---------------------------------------------------------------------- + * + * AtForkChild -- + * + * Unlock and reinstall the notifier in the child after a fork. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static void +AtForkChild(void) +{ + notifierMutex = NULL; + notifierCV = NULL; + Tcl_InitNotifier(); +} +#endif /* HAVE_PTHREAD_ATFORK */ + #endif /* TCL_THREADS */ -#endif /* HAVE_COREFOUNDATION */ +#endif /* !HAVE_COREFOUNDATION */ /* * Local Variables: |
