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: | 
