diff options
Diffstat (limited to 'unix/tclUnixNotfy.c')
| -rw-r--r-- | unix/tclUnixNotfy.c | 1051 | 
1 files changed, 666 insertions, 385 deletions
| diff --git a/unix/tclUnixNotfy.c b/unix/tclUnixNotfy.c index 2574015..b234667 100644 --- a/unix/tclUnixNotfy.c +++ b/unix/tclUnixNotfy.c @@ -7,10 +7,8 @@   *   * Copyright (c) 1995-1997 Sun Microsystems, Inc.   * - * See the file "license.terms" for information on usage and redistribution of - * this file, and for a DISCLAIMER OF ALL WARRANTIES. - * - * RCS: @(#) $Id: tclUnixNotfy.c,v 1.32 2006/08/21 01:08:03 das Exp $ + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES.   */  #include "tclInt.h" @@ -19,14 +17,6 @@  #include <signal.h>  /* - * This code does deep stub magic to allow replacement of the notifier at - * runtime. - */ - -extern TclStubs tclStubs; -extern Tcl_NotifierProcs tclOriginalNotifier; - -/*   * This structure is used to keep track of the notifier info for a registered   * file.   */ @@ -61,13 +51,13 @@ typedef struct FileHandlerEvent {  /*   * The following structure contains a set of select() masks to track readable, - * writable, and exceptional conditions. + * writable, and exception conditions.   */  typedef struct SelectMasks {      fd_set readable;      fd_set writable; -    fd_set exceptional; +    fd_set exception;  } SelectMasks;  /* @@ -101,13 +91,20 @@ typedef struct ThreadSpecificData {  				 * from these pointers. You must hold the  				 * notifierMutex lock before accessing these  				 * fields. */ +#ifdef __CYGWIN__ +    void *event;     /* Any other thread alerts a notifier +	 * that an event is ready to be processed +	 * by sending this event. */ +    void *hwnd;			/* Messaging window. */ +#else /* !__CYGWIN__ */      Tcl_Condition waitCV;	/* Any other thread alerts a notifier that an  				 * event is ready to be processed by signaling  				 * this condition variable. */ +#endif /* __CYGWIN__ */      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; @@ -123,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. @@ -143,7 +149,7 @@ static ThreadSpecificData *waitingListPtr = NULL;   * pipe. Hence writing to this file descriptor will cause the select() system   * call to return and wake up the notifier thread.   * - * You must hold the notifierMutex lock before accessing this list. + * You must hold the notifierMutex lock before writing to the pipe.   */  static int triggerPipe = -1; @@ -180,7 +186,7 @@ static Tcl_Condition notifierCV;  static Tcl_ThreadId notifierThread; -#endif +#endif /* TCL_THREADS */  /*   * Static routines defined in this file. @@ -188,8 +194,69 @@ static Tcl_ThreadId notifierThread;  #ifdef TCL_THREADS  static void	NotifierThreadProc(ClientData clientData); -#endif +#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); + +/* + * Import of Windows API when building threaded with Cygwin. + */ + +#if defined(TCL_THREADS) && defined(__CYGWIN__) +typedef struct { +    void *hwnd; +    unsigned int *message; +    int wParam; +    int lParam; +    int time; +    int x; +    int y; +} MSG; + +typedef struct { +    unsigned int style; +    void *lpfnWndProc; +    int cbClsExtra; +    int cbWndExtra; +    void *hInstance; +    void *hIcon; +    void *hCursor; +    void *hbrBackground; +    void *lpszMenuName; +    const void *lpszClassName; +} WNDCLASS; + +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__ */  /*   *---------------------------------------------------------------------- @@ -199,7 +266,7 @@ static int	FileHandlerEventProc(Tcl_Event *evPtr, int flags);   *	Initializes the platform specific notifier state.   *   * Results: - *	Returns a handle to the notifier state for this thread.. + *	Returns a handle to the notifier state for this thread.   *   * Side effects:   *	None. @@ -210,35 +277,66 @@ static int	FileHandlerEventProc(Tcl_Event *evPtr, int flags);  ClientData  Tcl_InitNotifier(void)  { -    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); +    if (tclNotifierHooks.initNotifierProc) { +	return tclNotifierHooks.initNotifierProc(); +    } else { +	ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);  #ifdef TCL_THREADS -    tsdPtr->eventReady = 0; +	tsdPtr->eventReady = 0; -    /* -     * Start the Notifier thread if necessary. -     */ +	/* +	 * Start the Notifier thread if necessary. +	 */ -    Tcl_MutexLock(¬ifierMutex); -    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"); +	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;  	} -    } -    notifierCount++; +#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++; -    /* -     * Wait for the notifier pipe to be created. -     */ +	/* +	 * Wait for the notifier pipe to be created. +	 */ -    while (triggerPipe < 0) { -	Tcl_ConditionWait(¬ifierCV, ¬ifierMutex, NULL); -    } +	while (triggerPipe < 0) { +	    Tcl_ConditionWait(¬ifierCV, ¬ifierMutex, NULL); +	} -    Tcl_MutexUnlock(¬ifierMutex); -#endif -    return (ClientData) tsdPtr; +	Tcl_MutexUnlock(¬ifierMutex); +#endif /* TCL_THREADS */ +	return tsdPtr; +    }  }  /* @@ -263,55 +361,69 @@ void  Tcl_FinalizeNotifier(      ClientData clientData)		/* Not used. */  { +    if (tclNotifierHooks.finalizeNotifierProc) { +	tclNotifierHooks.finalizeNotifierProc(clientData); +	return; +    } else {  #ifdef TCL_THREADS -    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); +	ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); -    Tcl_MutexLock(¬ifierMutex); -    notifierCount--; +	Tcl_MutexLock(¬ifierMutex); +	notifierCount--; -    /* -     * If this is the last thread to use the notifier, close the notifier pipe -     * and wait for the background thread to terminate. -     */ +	/* +	 * 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 (notifierCount == 0) { +	    int result; -	if (triggerPipe < 0) { -	    Tcl_Panic("Tcl_FinalizeNotifier: notifier pipe not initialized"); -	} +	    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] -	 */ +	    /* +	     * 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] +	     */ -	write(triggerPipe, "q", 1); -	close(triggerPipe); -	while(triggerPipe >= 0) { -	    Tcl_ConditionWait(¬ifierCV, ¬ifierMutex, NULL); -	} +	    if (write(triggerPipe, "q", 1) != 1) { +		Tcl_Panic("Tcl_FinalizeNotifier: %s", +			"unable to write q to triggerPipe"); +	    } +	    close(triggerPipe); +	    while(triggerPipe >= 0) { +		Tcl_ConditionWait(¬ifierCV, ¬ifierMutex, NULL); +	    } -	result = Tcl_JoinThread(notifierThread, NULL); -	if (result) { -	    Tcl_Panic("Tcl_FinalizeNotifier: unable to join notifier thread"); +	    result = Tcl_JoinThread(notifierThread, NULL); +	    if (result) { +		Tcl_Panic("Tcl_FinalizeNotifier: %s", +			"unable to join notifier thread"); +	    }  	} -    } -    /* -     * Clean up any synchronization objects in the thread local storage. -     */ +	/* +	 * Clean up any synchronization objects in the thread local storage. +	 */ -    Tcl_ConditionFinalize(&(tsdPtr->waitCV)); +#ifdef __CYGWIN__ +	CloseHandle(tsdPtr->event); +#else /* __CYGWIN__ */ +	Tcl_ConditionFinalize(&(tsdPtr->waitCV)); +#endif /* __CYGWIN__ */ -    Tcl_MutexUnlock(¬ifierMutex); -#endif +	Tcl_MutexUnlock(¬ifierMutex); +#endif /* TCL_THREADS */ +    }  }  /* @@ -337,13 +449,23 @@ void  Tcl_AlertNotifier(      ClientData clientData)  { +    if (tclNotifierHooks.alertNotifierProc) { +	tclNotifierHooks.alertNotifierProc(clientData); +	return; +    } else {  #ifdef TCL_THREADS -    ThreadSpecificData *tsdPtr = (ThreadSpecificData *) clientData; -    Tcl_MutexLock(¬ifierMutex); -    tsdPtr->eventReady = 1; -    Tcl_ConditionNotify(&tsdPtr->waitCV); -    Tcl_MutexUnlock(¬ifierMutex); -#endif +	ThreadSpecificData *tsdPtr = clientData; + +	Tcl_MutexLock(¬ifierMutex); +	tsdPtr->eventReady = 1; +#   ifdef __CYGWIN__ +	PostMessageW(tsdPtr->hwnd, 1024, 0, 0); +#   else +	Tcl_ConditionNotify(&tsdPtr->waitCV); +#   endif /* __CYGWIN__ */ +	Tcl_MutexUnlock(¬ifierMutex); +#endif /* TCL_THREADS */ +    }  }  /* @@ -366,16 +488,17 @@ Tcl_AlertNotifier(  void  Tcl_SetTimer( -    Tcl_Time *timePtr)		/* Timeout value, may be NULL. */ +    const Tcl_Time *timePtr)		/* Timeout value, may be NULL. */  { -    /* -     * The interval timer doesn't do anything in this implementation, because -     * the only event loop is via Tcl_DoOneEvent, which passes timeout values -     * to Tcl_WaitForEvent. -     */ - -    if (tclStubs.tcl_SetTimer != tclOriginalNotifier.setTimerProc) { -	tclStubs.tcl_SetTimer(timePtr); +    if (tclNotifierHooks.setTimerProc) { +	tclNotifierHooks.setTimerProc(timePtr); +	return; +    } else { +	/* +	 * The interval timer doesn't do anything in this implementation, +	 * because the only event loop is via Tcl_DoOneEvent, which passes +	 * timeout values to Tcl_WaitForEvent. +	 */      }  } @@ -400,6 +523,12 @@ Tcl_ServiceModeHook(      int mode)			/* Either TCL_SERVICE_ALL, or  				 * TCL_SERVICE_NONE. */  { +    if (tclNotifierHooks.serviceModeHookProc) { +	tclNotifierHooks.serviceModeHookProc(mode); +	return; +    } else { +	/* Does nothing in this implementation. */ +    }  }  /* @@ -429,53 +558,52 @@ Tcl_CreateFileHandler(  				 * event. */      ClientData clientData)	/* Arbitrary data to pass to proc. */  { -    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); -    FileHandler *filePtr; - -    if (tclStubs.tcl_CreateFileHandler != -	    tclOriginalNotifier.createFileHandlerProc) { -	tclStubs.tcl_CreateFileHandler(fd, mask, proc, clientData); +    if (tclNotifierHooks.createFileHandlerProc) { +	tclNotifierHooks.createFileHandlerProc(fd, mask, proc, clientData);  	return; -    } +    } else { +	ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); +	FileHandler *filePtr; -    for (filePtr = tsdPtr->firstFileHandlerPtr; filePtr != NULL; -	    filePtr = filePtr->nextPtr) { -	if (filePtr->fd == fd) { -	    break; +	for (filePtr = tsdPtr->firstFileHandlerPtr; filePtr != NULL; +		filePtr = filePtr->nextPtr) { +	    if (filePtr->fd == fd) { +		break; +	    }  	} -    } -    if (filePtr == NULL) { -	filePtr = (FileHandler*) ckalloc(sizeof(FileHandler)); -	filePtr->fd = fd; -	filePtr->readyMask = 0; -	filePtr->nextPtr = tsdPtr->firstFileHandlerPtr; -	tsdPtr->firstFileHandlerPtr = filePtr; -    } -    filePtr->proc = proc; -    filePtr->clientData = clientData; -    filePtr->mask = mask; +	if (filePtr == NULL) { +	    filePtr = ckalloc(sizeof(FileHandler)); +	    filePtr->fd = fd; +	    filePtr->readyMask = 0; +	    filePtr->nextPtr = tsdPtr->firstFileHandlerPtr; +	    tsdPtr->firstFileHandlerPtr = filePtr; +	} +	filePtr->proc = proc; +	filePtr->clientData = clientData; +	filePtr->mask = mask; -    /* -     * Update the check masks for this file. -     */ +	/* +	 * Update the check masks for this file. +	 */ -    if (mask & TCL_READABLE) { -	FD_SET(fd, &(tsdPtr->checkMasks.readable)); -    } else { -	FD_CLR(fd, &(tsdPtr->checkMasks.readable)); -    } -    if (mask & TCL_WRITABLE) { -	FD_SET(fd, &(tsdPtr->checkMasks.writable)); -    } else { -	FD_CLR(fd, &(tsdPtr->checkMasks.writable)); -    } -    if (mask & TCL_EXCEPTION) { -	FD_SET(fd, &(tsdPtr->checkMasks.exceptional)); -    } else { -	FD_CLR(fd, &(tsdPtr->checkMasks.exceptional)); -    } -    if (tsdPtr->numFdBits <= fd) { -	tsdPtr->numFdBits = fd+1; +	if (mask & TCL_READABLE) { +	    FD_SET(fd, &tsdPtr->checkMasks.readable); +	} else { +	    FD_CLR(fd, &tsdPtr->checkMasks.readable); +	} +	if (mask & TCL_WRITABLE) { +	    FD_SET(fd, &tsdPtr->checkMasks.writable); +	} else { +	    FD_CLR(fd, &tsdPtr->checkMasks.writable); +	} +	if (mask & TCL_EXCEPTION) { +	    FD_SET(fd, &tsdPtr->checkMasks.exception); +	} else { +	    FD_CLR(fd, &tsdPtr->checkMasks.exception); +	} +	if (tsdPtr->numFdBits <= fd) { +	    tsdPtr->numFdBits = fd+1; +	}      }  } @@ -500,70 +628,71 @@ Tcl_DeleteFileHandler(      int fd)			/* Stream id for which to remove callback  				 * function. */  { -    FileHandler *filePtr, *prevPtr; -    int i; -    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); - -    if (tclStubs.tcl_DeleteFileHandler != -	    tclOriginalNotifier.deleteFileHandlerProc) { -	tclStubs.tcl_DeleteFileHandler(fd); +    if (tclNotifierHooks.deleteFileHandlerProc) { +	tclNotifierHooks.deleteFileHandlerProc(fd);  	return; -    } +    } else { +	FileHandler *filePtr, *prevPtr; +	int i; +	ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); -    /* -     * Find the entry for the given file (and return if there isn't one). -     */ +	/* +	 * Find the entry for the given file (and return if there isn't one). +	 */ -    for (prevPtr = NULL, filePtr = tsdPtr->firstFileHandlerPtr; ; -	 prevPtr = filePtr, filePtr = filePtr->nextPtr) { -	if (filePtr == NULL) { -	    return; -	} -	if (filePtr->fd == fd) { -	    break; +	for (prevPtr = NULL, filePtr = tsdPtr->firstFileHandlerPtr; ; +		prevPtr = filePtr, filePtr = filePtr->nextPtr) { +	    if (filePtr == NULL) { +		return; +	    } +	    if (filePtr->fd == fd) { +		break; +	    }  	} -    } -    /* -     * Update the check masks for this file. -     */ +	/* +	 * Update the check masks for this file. +	 */ -    if (filePtr->mask & TCL_READABLE) { -	FD_CLR(fd, &(tsdPtr->checkMasks.readable)); -    } -    if (filePtr->mask & TCL_WRITABLE) { -	FD_CLR(fd, &(tsdPtr->checkMasks.writable)); -    } -    if (filePtr->mask & TCL_EXCEPTION) { -	FD_CLR(fd, &(tsdPtr->checkMasks.exceptional)); -    } +	if (filePtr->mask & TCL_READABLE) { +	    FD_CLR(fd, &tsdPtr->checkMasks.readable); +	} +	if (filePtr->mask & TCL_WRITABLE) { +	    FD_CLR(fd, &tsdPtr->checkMasks.writable); +	} +	if (filePtr->mask & TCL_EXCEPTION) { +	    FD_CLR(fd, &tsdPtr->checkMasks.exception); +	} -    /* -     * Find current max fd. -     */ +	/* +	 * Find current max fd. +	 */ -    if (fd+1 == tsdPtr->numFdBits) { -	tsdPtr->numFdBits = 0; -	for (i = fd-1; i >= 0; i--) { -	    if (FD_ISSET(i, &(tsdPtr->checkMasks.readable)) -		    || FD_ISSET(i, &(tsdPtr->checkMasks.writable)) -		    || FD_ISSET(i, &(tsdPtr->checkMasks.exceptional))) { -		tsdPtr->numFdBits = i+1; -		break; +	if (fd+1 == tsdPtr->numFdBits) { +	    int numFdBits = 0; + +	    for (i = fd-1; i >= 0; i--) { +		if (FD_ISSET(i, &tsdPtr->checkMasks.readable) +			|| FD_ISSET(i, &tsdPtr->checkMasks.writable) +			|| FD_ISSET(i, &tsdPtr->checkMasks.exception)) { +		    numFdBits = i+1; +		    break; +		}  	    } +	    tsdPtr->numFdBits = numFdBits;  	} -    } -    /* -     * Clean up information in the callback record. -     */ +	/* +	 * Clean up information in the callback record. +	 */ -    if (prevPtr == NULL) { -	tsdPtr->firstFileHandlerPtr = filePtr->nextPtr; -    } else { -	prevPtr->nextPtr = filePtr->nextPtr; +	if (prevPtr == NULL) { +	    tsdPtr->firstFileHandlerPtr = filePtr->nextPtr; +	} else { +	    prevPtr->nextPtr = filePtr->nextPtr; +	} +	ckfree(filePtr);      } -    ckfree((char *) filePtr);  }  /* @@ -632,13 +761,38 @@ FileHandlerEventProc(  	mask = filePtr->readyMask & filePtr->mask;  	filePtr->readyMask = 0;  	if (mask != 0) { -	    (*filePtr->proc)(filePtr->clientData, mask); +	    filePtr->proc(filePtr->clientData, mask);  	}  	break;      }      return 1;  } +#if defined(TCL_THREADS) && defined(__CYGWIN__) + +static DWORD __stdcall +NotifierProc( +    void *hwnd, +    unsigned int message, +    void *wParam, +    void *lParam) +{ +    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + +    if (message != 1024) { +	return DefWindowProcW(hwnd, message, wParam, lParam); +    } + +    /* +     * Process all of the runnable events. +     */ + +    tsdPtr->eventReady = 1; +    Tcl_ServiceAll(); +    return 0; +} +#endif /* TCL_THREADS && __CYGWIN__ */ +  /*   *----------------------------------------------------------------------   * @@ -659,218 +813,275 @@ FileHandlerEventProc(  int  Tcl_WaitForEvent( -    Tcl_Time *timePtr)		/* Maximum block time, or NULL. */ +    const Tcl_Time *timePtr)		/* Maximum block time, or NULL. */  { -    FileHandler *filePtr; -    FileHandlerEvent *fileEvPtr; -    int mask; -    Tcl_Time myTime; +    if (tclNotifierHooks.waitForEventProc) { +	return tclNotifierHooks.waitForEventProc(timePtr); +    } else { +	FileHandler *filePtr; +	int mask; +	Tcl_Time vTime;  #ifdef TCL_THREADS -    int waitForFiles; -    Tcl_Time *myTimePtr; +	int waitForFiles; +#   ifdef __CYGWIN__ +	MSG msg; +#   endif /* __CYGWIN__ */  #else -    /* -     * Impl. notes: timeout & timeoutPtr are used if, and only if threads are -     * not enabled. They are the arguments for the regular select() used when -     * the core is not thread-enabled. -     */ +	/* +	 * Impl. notes: timeout & timeoutPtr are used if, and only if threads +	 * are not enabled. They are the arguments for the regular select() +	 * used when the core is not thread-enabled. +	 */ -    struct timeval timeout, *timeoutPtr; -    int numFound; +	struct timeval timeout, *timeoutPtr; +	int numFound;  #endif /* TCL_THREADS */ -    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); - -    if (tclStubs.tcl_WaitForEvent != tclOriginalNotifier.waitForEventProc) { -	return tclStubs.tcl_WaitForEvent(timePtr); -    } - -    /* -     * 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. -     */ +	ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); -    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. +	 * 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.  	 */ -	myTime.sec  = timePtr->sec; -	myTime.usec = timePtr->usec; +	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. +	     */ -	if (myTime.sec != 0 || myTime.usec != 0) { -	    (*tclScaleTimeProcPtr) (&myTime, tclTimeClientData); +	    if (timePtr->sec != 0 || timePtr->usec != 0) { +		vTime = *timePtr; +		tclScaleTimeProcPtr(&vTime, tclTimeClientData); +		timePtr = &vTime; +	    } +#ifndef TCL_THREADS +	    timeout.tv_sec = timePtr->sec; +	    timeout.tv_usec = timePtr->usec; +	    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. +	     */ + +	    return -1; +	} else { +	    timeoutPtr = NULL; +#endif /* !TCL_THREADS */  	}  #ifdef TCL_THREADS -	myTimePtr = &myTime; -#else -	timeout.tv_sec = myTime.sec; -	timeout.tv_usec = myTime.usec; -	timeoutPtr = &timeout; -#endif /* TCL_THREADS */ - -#ifndef TCL_THREADS -    } 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. +	 * Place this thread on the list of interested threads, signal the +	 * notifier thread, and wait for a response or a timeout.  	 */ -	return -1; -#endif /* !TCL_THREADS */ -    } else { -#ifdef TCL_THREADS -	myTimePtr = NULL; -#else -	timeoutPtr = NULL; -#endif /* TCL_THREADS */ -    } - -#ifdef TCL_THREADS -    /* -     * Place this thread on the list of interested threads, signal the -     * notifier thread, and wait for a response or a timeout. -     */ +#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(¬ifierMutex); +	Tcl_MutexLock(¬ifierMutex); -    waitForFiles = (tsdPtr->numFdBits > 0); -    if (myTimePtr != NULL && myTimePtr->sec == 0 && (myTimePtr->usec == 0 +	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] +		 */ +		|| timePtr->usec < 10 +#endif /* __APPLE__ && __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] +	     * Cannot emulate a polling select with a polling condition +	     * variable. Instead, pretend to wait for files and tell the +	     * notifier thread what we are doing. The notifier thread makes +	     * sure it goes through select with its select mask in the same +	     * state as ours currently is. We block until that happens.  	     */ -	    || myTimePtr->usec < 10 -#endif -	    )) { -	/* -	 * Cannot emulate a polling select with a polling condition variable. -	 * Instead, pretend to wait for files and tell the notifier thread -	 * what we are doing. The notifier thread makes sure it goes through -	 * select with its select mask in the same state as ours currently is. -	 * We block until that happens. -	 */ -	waitForFiles = 1; -	tsdPtr->pollState = POLL_WANT; -	myTimePtr = NULL; -    } else { -	tsdPtr->pollState = 0; -    } +	    waitForFiles = 1; +	    tsdPtr->pollState = POLL_WANT; +	    timePtr = NULL; +	} else { +	    waitForFiles = (tsdPtr->numFdBits > 0); +	    tsdPtr->pollState = 0; +	} -    if (waitForFiles) { -	/* -	 * Add the ThreadSpecificData structure of this thread to the list of -	 * ThreadSpecificData structures of all threads that are waiting on -	 * file events. -	 */ +	if (waitForFiles) { +	    /* +	     * Add the ThreadSpecificData structure of this thread to the list +	     * of ThreadSpecificData structures of all threads that are +	     * waiting on file events. +	     */ -	tsdPtr->nextPtr = waitingListPtr; -	if (waitingListPtr) { -	    waitingListPtr->prevPtr = tsdPtr; +	    tsdPtr->nextPtr = waitingListPtr; +	    if (waitingListPtr) { +		waitingListPtr->prevPtr = tsdPtr; +	    } +	    tsdPtr->prevPtr = 0; +	    waitingListPtr = tsdPtr; +	    tsdPtr->onList = 1; + +	    if ((write(triggerPipe, "", 1) == -1) && (errno != EAGAIN)) { +		Tcl_Panic("Tcl_WaitForEvent: %s", +			"unable to write to triggerPipe"); +	    }  	} -	tsdPtr->prevPtr = 0; -	waitingListPtr = tsdPtr; -	tsdPtr->onList = 1; -	write(triggerPipe, "", 1); -    } +	FD_ZERO(&tsdPtr->readyMasks.readable); +	FD_ZERO(&tsdPtr->readyMasks.writable); +	FD_ZERO(&tsdPtr->readyMasks.exception); -    FD_ZERO(&(tsdPtr->readyMasks.readable)); -    FD_ZERO(&(tsdPtr->readyMasks.writable)); -    FD_ZERO(&(tsdPtr->readyMasks.exceptional)); +	if (!tsdPtr->eventReady) { +#ifdef __CYGWIN__ +	    if (!PeekMessageW(&msg, NULL, 0, 0, 0)) { +		DWORD timeout; -    if (!tsdPtr->eventReady) { -	Tcl_ConditionWait(&tsdPtr->waitCV, ¬ifierMutex, myTimePtr); -    } -    tsdPtr->eventReady = 0; +		if (timePtr) { +		    timeout = timePtr->sec * 1000 + timePtr->usec / 1000; +		} else { +		    timeout = 0xFFFFFFFF; +		} +		Tcl_MutexUnlock(¬ifierMutex); +		MsgWaitForMultipleObjects(1, &tsdPtr->event, 0, timeout, 1279); +		Tcl_MutexLock(¬ifierMutex); +	    } +#else +	    Tcl_ConditionWait(&tsdPtr->waitCV, ¬ifierMutex, timePtr); +#endif /* __CYGWIN__ */ +	} +	tsdPtr->eventReady = 0; -    if (waitForFiles && tsdPtr->onList) { -	/* -	 * Remove the ThreadSpecificData structure of this thread from the -	 * waiting list. Alert the notifier thread to recompute its select -	 * masks - skipping this caused a hang when trying to close a pipe -	 * which the notifier thread was still doing a select on. -	 */ +#ifdef __CYGWIN__ +	while (PeekMessageW(&msg, NULL, 0, 0, 0)) { +	    /* +	     * Retrieve and dispatch the message. +	     */ -	if (tsdPtr->prevPtr) { -	    tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr; -	} else { -	    waitingListPtr = tsdPtr->nextPtr; +	    DWORD result = GetMessageW(&msg, NULL, 0, 0); + +	    if (result == 0) { +		PostQuitMessage(msg.wParam); +		/* What to do here? */ +	    } else if (result != (DWORD) -1) { +		TranslateMessage(&msg); +		DispatchMessageW(&msg); +	    }  	} -	if (tsdPtr->nextPtr) { -	    tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr; +	ResetEvent(tsdPtr->event); +#endif /* __CYGWIN__ */ + +	if (waitForFiles && tsdPtr->onList) { +	    /* +	     * Remove the ThreadSpecificData structure of this thread from the +	     * waiting list. Alert the notifier thread to recompute its select +	     * masks - skipping this caused a hang when trying to close a pipe +	     * which the notifier thread was still doing a select on. +	     */ + +	    if (tsdPtr->prevPtr) { +		tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr; +	    } else { +		waitingListPtr = tsdPtr->nextPtr; +	    } +	    if (tsdPtr->nextPtr) { +		tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr; +	    } +	    tsdPtr->nextPtr = tsdPtr->prevPtr = NULL; +	    tsdPtr->onList = 0; +	    if ((write(triggerPipe, "", 1) == -1) && (errno != EAGAIN)) { +		Tcl_Panic("Tcl_WaitForEvent: %s", +			"unable to write to triggerPipe"); +	    }  	} -	tsdPtr->nextPtr = tsdPtr->prevPtr = NULL; -	tsdPtr->onList = 0; -	write(triggerPipe, "", 1); -    }  #else -    tsdPtr->readyMasks = tsdPtr->checkMasks; -    numFound = select(tsdPtr->numFdBits, &(tsdPtr->readyMasks.readable), -	    &(tsdPtr->readyMasks.writable), &(tsdPtr->readyMasks.exceptional), -	    timeoutPtr); +	tsdPtr->readyMasks = tsdPtr->checkMasks; +	numFound = select(tsdPtr->numFdBits, &tsdPtr->readyMasks.readable, +		&tsdPtr->readyMasks.writable, &tsdPtr->readyMasks.exception, +		timeoutPtr); -    /* -     * Some systems don't clear the masks after an error, so we have to do it -     * here. -     */ +	/* +	 * Some systems don't clear the masks after an error, so we have to do +	 * it here. +	 */ -    if (numFound == -1) { -	FD_ZERO(&(tsdPtr->readyMasks.readable)); -	FD_ZERO(&(tsdPtr->readyMasks.writable)); -	FD_ZERO(&(tsdPtr->readyMasks.exceptional)); -    } +	if (numFound == -1) { +	    FD_ZERO(&tsdPtr->readyMasks.readable); +	    FD_ZERO(&tsdPtr->readyMasks.writable); +	    FD_ZERO(&tsdPtr->readyMasks.exception); +	}  #endif /* TCL_THREADS */ -    /* -     * Queue all detected file events before returning. -     */ +	/* +	 * Queue all detected file events before returning. +	 */ -    for (filePtr = tsdPtr->firstFileHandlerPtr; (filePtr != NULL); -	    filePtr = filePtr->nextPtr) { +	for (filePtr = tsdPtr->firstFileHandlerPtr; (filePtr != NULL); +		filePtr = filePtr->nextPtr) { +	    mask = 0; +	    if (FD_ISSET(filePtr->fd, &tsdPtr->readyMasks.readable)) { +		mask |= TCL_READABLE; +	    } +	    if (FD_ISSET(filePtr->fd, &tsdPtr->readyMasks.writable)) { +		mask |= TCL_WRITABLE; +	    } +	    if (FD_ISSET(filePtr->fd, &tsdPtr->readyMasks.exception)) { +		mask |= TCL_EXCEPTION; +	    } -	mask = 0; -	if (FD_ISSET(filePtr->fd, &(tsdPtr->readyMasks.readable))) { -	    mask |= TCL_READABLE; -	} -	if (FD_ISSET(filePtr->fd, &(tsdPtr->readyMasks.writable))) { -	    mask |= TCL_WRITABLE; -	} -	if (FD_ISSET(filePtr->fd, &(tsdPtr->readyMasks.exceptional))) { -	    mask |= TCL_EXCEPTION; -	} +	    if (!mask) { +		continue; +	    } -	if (!mask) { -	    continue; -	} +	    /* +	     * Don't bother to queue an event if the mask was previously +	     * non-zero since an event must still be on the queue. +	     */ -	/* -	 * Don't bother to queue an event if the mask was previously non-zero -	 * since an event must still be on the queue. -	 */ +	    if (filePtr->readyMask == 0) { +		FileHandlerEvent *fileEvPtr = +			ckalloc(sizeof(FileHandlerEvent)); -	if (filePtr->readyMask == 0) { -	    fileEvPtr = (FileHandlerEvent *) ckalloc(sizeof(FileHandlerEvent)); -	    fileEvPtr->header.proc = FileHandlerEventProc; -	    fileEvPtr->fd = filePtr->fd; -	    Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL); +		fileEvPtr->header.proc = FileHandlerEventProc; +		fileEvPtr->fd = filePtr->fd; +		Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL); +	    } +	    filePtr->readyMask = mask;  	} -	filePtr->readyMask = mask; -    }  #ifdef TCL_THREADS -    Tcl_MutexUnlock(¬ifierMutex); +	Tcl_MutexUnlock(¬ifierMutex);  #endif /* TCL_THREADS */ -    return 0; +	return 0; +    }  }  #ifdef TCL_THREADS @@ -905,38 +1116,35 @@ NotifierThreadProc(      ThreadSpecificData *tsdPtr;      fd_set readableMask;      fd_set writableMask; -    fd_set exceptionalMask; +    fd_set exceptionMask;      int fds[2]; -    int i, status, numFdBits = 0, receivePipe; +    int i, numFdBits = 0, receivePipe;      long found;      struct timeval poll = {0., 0.}, *timePtr;      char buf[2];      if (pipe(fds) != 0) { -	Tcl_Panic("NotifierThreadProc: could not create trigger pipe"); +	Tcl_Panic("NotifierThreadProc: %s", "could not create trigger pipe");      }      receivePipe = fds[0]; -#ifndef USE_FIONBIO -    status = fcntl(receivePipe, F_GETFL); -    status |= O_NONBLOCK; -    if (fcntl(receivePipe, F_SETFL, status) < 0) { -	Tcl_Panic("NotifierThreadProc: could not make receive pipe non blocking"); +    if (TclUnixSetBlockingMode(receivePipe, TCL_MODE_NONBLOCKING) < 0) { +	Tcl_Panic("NotifierThreadProc: %s", +		"could not make receive pipe non blocking");      } -    status = fcntl(fds[1], F_GETFL); -    status |= O_NONBLOCK; -    if (fcntl(fds[1], F_SETFL, status) < 0) { -	Tcl_Panic("NotifierThreadProc: could not make trigger pipe non blocking"); +    if (TclUnixSetBlockingMode(fds[1], TCL_MODE_NONBLOCKING) < 0) { +	Tcl_Panic("NotifierThreadProc: %s", +		"could not make trigger pipe non blocking");      } -#else -    if (ioctl(receivePipe, (int) FIONBIO, &status) < 0) { -	Tcl_Panic("NotifierThreadProc: could not make receive pipe non blocking"); +    if (fcntl(receivePipe, F_SETFD, FD_CLOEXEC) < 0) { +	Tcl_Panic("NotifierThreadProc: %s", +		"could not make receive pipe close-on-exec");      } -    if (ioctl(fds[1], (int) FIONBIO, &status) < 0) { -	Tcl_Panic("NotifierThreadProc: could not make trigger pipe non blocking"); +    if (fcntl(fds[1], F_SETFD, FD_CLOEXEC) < 0) { +	Tcl_Panic("NotifierThreadProc: %s", +		"could not make trigger pipe close-on-exec");      } -#endif /* FIONBIO */      /*       * Install the write end of the pipe into the global variable. @@ -959,7 +1167,7 @@ NotifierThreadProc(      while (1) {  	FD_ZERO(&readableMask);  	FD_ZERO(&writableMask); -	FD_ZERO(&exceptionalMask); +	FD_ZERO(&exceptionMask);  	/*  	 * Compute the logical OR of the select masks from all the waiting @@ -970,14 +1178,14 @@ NotifierThreadProc(  	timePtr = NULL;  	for (tsdPtr = waitingListPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) {  	    for (i = tsdPtr->numFdBits-1; i >= 0; --i) { -		if (FD_ISSET(i, &(tsdPtr->checkMasks.readable))) { +		if (FD_ISSET(i, &tsdPtr->checkMasks.readable)) {  		    FD_SET(i, &readableMask);  		} -		if (FD_ISSET(i, &(tsdPtr->checkMasks.writable))) { +		if (FD_ISSET(i, &tsdPtr->checkMasks.writable)) {  		    FD_SET(i, &writableMask);  		} -		if (FD_ISSET(i, &(tsdPtr->checkMasks.exceptional))) { -		    FD_SET(i, &exceptionalMask); +		if (FD_ISSET(i, &tsdPtr->checkMasks.exception)) { +		    FD_SET(i, &exceptionMask);  		}  	    }  	    if (tsdPtr->numFdBits > numFdBits) { @@ -1004,7 +1212,7 @@ NotifierThreadProc(  	}  	FD_SET(receivePipe, &readableMask); -	if (select(numFdBits, &readableMask, &writableMask, &exceptionalMask, +	if (select(numFdBits, &readableMask, &writableMask, &exceptionMask,  		timePtr) == -1) {  	    /*  	     * Try again immediately on an error. @@ -1022,19 +1230,19 @@ NotifierThreadProc(  	    found = 0;  	    for (i = tsdPtr->numFdBits-1; i >= 0; --i) { -		if (FD_ISSET(i, &(tsdPtr->checkMasks.readable)) +		if (FD_ISSET(i, &tsdPtr->checkMasks.readable)  			&& FD_ISSET(i, &readableMask)) { -		    FD_SET(i, &(tsdPtr->readyMasks.readable)); +		    FD_SET(i, &tsdPtr->readyMasks.readable);  		    found = 1;  		} -		if (FD_ISSET(i, &(tsdPtr->checkMasks.writable)) +		if (FD_ISSET(i, &tsdPtr->checkMasks.writable)  			&& FD_ISSET(i, &writableMask)) { -		    FD_SET(i, &(tsdPtr->readyMasks.writable)); +		    FD_SET(i, &tsdPtr->readyMasks.writable);  		    found = 1;  		} -		if (FD_ISSET(i, &(tsdPtr->checkMasks.exceptional)) -			&& FD_ISSET(i, &exceptionalMask)) { -		    FD_SET(i, &(tsdPtr->readyMasks.exceptional)); +		if (FD_ISSET(i, &tsdPtr->checkMasks.exception) +			&& FD_ISSET(i, &exceptionMask)) { +		    FD_SET(i, &tsdPtr->readyMasks.exception);  		    found = 1;  		}  	    } @@ -1061,7 +1269,11 @@ NotifierThreadProc(  		    tsdPtr->onList = 0;  		    tsdPtr->pollState = 0;  		} +#ifdef __CYGWIN__ +		PostMessageW(tsdPtr->hwnd, 1024, 0, 0); +#else  		Tcl_ConditionNotify(&tsdPtr->waitCV); +#endif /* __CYGWIN__ */  	    }  	}  	Tcl_MutexUnlock(¬ifierMutex); @@ -1098,11 +1310,80 @@ NotifierThreadProc(      Tcl_ConditionNotify(¬ifierCV);      Tcl_MutexUnlock(¬ifierMutex); -    TclpThreadExit (0); +    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: | 
