diff options
Diffstat (limited to 'macosx/tclMacOSXNotify.c')
-rw-r--r-- | macosx/tclMacOSXNotify.c | 791 |
1 files changed, 397 insertions, 394 deletions
diff --git a/macosx/tclMacOSXNotify.c b/macosx/tclMacOSXNotify.c index b67ef3e..8edbda2 100644 --- a/macosx/tclMacOSXNotify.c +++ b/macosx/tclMacOSXNotify.c @@ -12,7 +12,7 @@ * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclMacOSXNotify.c,v 1.21 2008/10/26 18:50:07 dkf Exp $ + * RCS: @(#) $Id: tclMacOSXNotify.c,v 1.22 2009/01/09 11:21:46 dkf Exp $ */ #include "tclInt.h" @@ -239,7 +239,7 @@ static OSSpinLock notifierLock = SPINLOCK_INIT; * Debug version of SpinLockLock that logs the time spent waiting for the lock */ -#define SpinLockLockDbg(p) if(!SpinLockTry(p)) { \ +#define SpinLockLockDbg(p) if (!SpinLockTry(p)) { \ Tcl_WideInt s = TclpGetWideClicks(), e; \ SpinLockLock(p); e = TclpGetWideClicks(); \ fprintf(notifierLog, "tclMacOSXNotify.c:" \ @@ -313,8 +313,8 @@ static void AtForkChild(void); #if defined(HAVE_WEAK_IMPORT) && MAC_OS_X_VERSION_MIN_REQUIRED < 1040 /* Support for weakly importing pthread_atfork. */ #define WEAK_IMPORT_PTHREAD_ATFORK -extern int pthread_atfork(void (*prepare)(void), void (*parent)(void), - void (*child)(void)) WEAK_IMPORT_ATTRIBUTE; +extern int pthread_atfork(void (*prepare)(void), void (*parent)(void), + void (*child)(void)) WEAK_IMPORT_ATTRIBUTE; #endif /* HAVE_WEAK_IMPORT */ /* * On Darwin 9 and later, it is not possible to call CoreFoundation after @@ -348,113 +348,112 @@ MODULE_SCOPE long tclMacOSXDarwinRelease; ClientData Tcl_InitNotifier(void) { + ThreadSpecificData *tsdPtr; + if (tclNotifierHooks.initNotifierProc) { return tclNotifierHooks.initNotifierProc(); - } else { - ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + } - tsdPtr->eventReady = 0; + tsdPtr = TCL_TSD_INIT(&dataKey); + tsdPtr->eventReady = 0; #ifdef WEAK_IMPORT_SPINLOCKLOCK - /* - * Initialize support for weakly imported spinlock API. - */ - if (pthread_once(&spinLockLockInitControl, SpinLockLockInit)) { - Tcl_Panic("Tcl_InitNotifier: pthread_once failed"); - } + /* + * Initialize support for weakly imported spinlock API. + */ + + if (pthread_once(&spinLockLockInitControl, SpinLockLockInit)) { + Tcl_Panic("Tcl_InitNotifier: pthread_once failed"); + } #endif #ifndef __CONSTANT_CFSTRINGS__ - if (!tclEventsOnlyRunLoopMode) { - tclEventsOnlyRunLoopMode = CFSTR(TCL_EVENTS_ONLY_RUN_LOOP_MODE); - } + if (!tclEventsOnlyRunLoopMode) { + tclEventsOnlyRunLoopMode = CFSTR(TCL_EVENTS_ONLY_RUN_LOOP_MODE); + } #endif - /* - * Initialize CFRunLoopSource and add it to CFRunLoop of this thread. - */ + /* + * Initialize CFRunLoopSource and add it to CFRunLoop of this thread. + */ - if (!tsdPtr->runLoop) { - CFRunLoopRef runLoop = CFRunLoopGetCurrent(); - CFRunLoopSourceRef runLoopSource; - CFRunLoopSourceContext runLoopSourceContext; - - bzero(&runLoopSourceContext, sizeof(CFRunLoopSourceContext)); - runLoopSourceContext.info = tsdPtr; - runLoopSource = CFRunLoopSourceCreate(NULL, 0, - &runLoopSourceContext); - if (!runLoopSource) { - Tcl_Panic("Tcl_InitNotifier: could not create CFRunLoopSource"); - } - CFRunLoopAddSource(runLoop, runLoopSource, kCFRunLoopCommonModes); - CFRunLoopAddSource(runLoop, runLoopSource, - tclEventsOnlyRunLoopMode); - tsdPtr->runLoopSource = runLoopSource; - tsdPtr->runLoop = runLoop; + if (!tsdPtr->runLoop) { + CFRunLoopRef runLoop = CFRunLoopGetCurrent(); + CFRunLoopSourceRef runLoopSource; + CFRunLoopSourceContext runLoopSourceContext; + + bzero(&runLoopSourceContext, sizeof(CFRunLoopSourceContext)); + runLoopSourceContext.info = tsdPtr; + runLoopSource = CFRunLoopSourceCreate(NULL, 0, &runLoopSourceContext); + if (!runLoopSource) { + Tcl_Panic("Tcl_InitNotifier: could not create CFRunLoopSource"); } + CFRunLoopAddSource(runLoop, runLoopSource, kCFRunLoopCommonModes); + CFRunLoopAddSource(runLoop, runLoopSource, tclEventsOnlyRunLoopMode); + tsdPtr->runLoopSource = runLoopSource; + tsdPtr->runLoop = runLoop; + } - LOCK_NOTIFIER_INIT; + LOCK_NOTIFIER_INIT; #ifdef HAVE_PTHREAD_ATFORK - /* - * Install pthread_atfork handlers to reinitialize the notifier in the - * child of a fork. - */ + /* + * Install pthread_atfork handlers to reinitialize the notifier in the + * child of a fork. + */ - if ( + if ( #ifdef WEAK_IMPORT_PTHREAD_ATFORK - pthread_atfork != NULL && + pthread_atfork != NULL && #endif - !atForkInit) { - int result = pthread_atfork(AtForkPrepare, AtForkParent, - AtForkChild); + !atForkInit) { + int result = pthread_atfork(AtForkPrepare, AtForkParent, AtForkChild); - if (result) { - Tcl_Panic("Tcl_InitNotifier: pthread_atfork failed"); - } - atForkInit = 1; + if (result) { + Tcl_Panic("Tcl_InitNotifier: pthread_atfork failed"); } + atForkInit = 1; + } #endif - if (notifierCount == 0) { - int fds[2], status; + if (notifierCount == 0) { + int fds[2], status; - /* - * Initialize trigger pipe. - */ + /* + * Initialize trigger pipe. + */ - if (pipe(fds) != 0) { - Tcl_Panic("Tcl_InitNotifier: could not create trigger pipe"); - } + if (pipe(fds) != 0) { + Tcl_Panic("Tcl_InitNotifier: could not create trigger pipe"); + } - status = fcntl(fds[0], F_GETFL); - status |= O_NONBLOCK; - if (fcntl(fds[0], F_SETFL, status) < 0) { - Tcl_Panic("Tcl_InitNotifier: 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("Tcl_InitNotifier: could not make trigger pipe non blocking"); - } + status = fcntl(fds[0], F_GETFL); + status |= O_NONBLOCK; + if (fcntl(fds[0], F_SETFL, status) < 0) { + Tcl_Panic("Tcl_InitNotifier: 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("Tcl_InitNotifier: could not make trigger pipe non blocking"); + } - receivePipe = fds[0]; - triggerPipe = fds[1]; + receivePipe = fds[0]; + triggerPipe = fds[1]; - /* - * Create notifier thread lazily in Tcl_WaitForEvent() to avoid - * interfering with fork() followed immediately by execve() - * (cannot execve() when more than one thread is present). - */ + /* + * Create notifier thread lazily in Tcl_WaitForEvent() to avoid + * interfering with fork() followed immediately by execve() (we cannot + * execve() when more than one thread is present). + */ - notifierThread = 0; + notifierThread = 0; #ifdef TCL_MAC_DEBUG_NOTIFIER - OPEN_NOTIFIER_LOG; + OPEN_NOTIFIER_LOG; #endif - } - notifierCount++; - UNLOCK_NOTIFIER_INIT; - - return (ClientData) tsdPtr; } + notifierCount++; + UNLOCK_NOTIFIER_INIT; + + return (ClientData) tsdPtr; } /* @@ -479,71 +478,72 @@ void Tcl_FinalizeNotifier( ClientData clientData) /* Not used. */ { + ThreadSpecificData *tsdPtr; + if (tclNotifierHooks.finalizeNotifierProc) { tclNotifierHooks.finalizeNotifierProc(clientData); return; - } else { - ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + } - LOCK_NOTIFIER_INIT; - notifierCount--; + tsdPtr = TCL_TSD_INIT(&dataKey); + LOCK_NOTIFIER_INIT; + 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: 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); + write(triggerPipe, "q", 1); + close(triggerPipe); - if (notifierThread) { - result = pthread_join(notifierThread, NULL); - if (result) { - Tcl_Panic("Tcl_FinalizeNotifier: unable to join notifier thread"); - } - notifierThread = 0; + if (notifierThread) { + result = pthread_join(notifierThread, NULL); + if (result) { + Tcl_Panic("Tcl_FinalizeNotifier: unable to join notifier thread"); } + notifierThread = 0; + } - close(receivePipe); - triggerPipe = -1; + close(receivePipe); + triggerPipe = -1; #ifdef TCL_MAC_DEBUG_NOTIFIER - CLOSE_NOTIFIER_LOG; + CLOSE_NOTIFIER_LOG; #endif - } - UNLOCK_NOTIFIER_INIT; + } + UNLOCK_NOTIFIER_INIT; - LOCK_NOTIFIER; /* for concurrency with Tcl_AlertNotifier */ - if (tsdPtr->runLoop) { - tsdPtr->runLoop = NULL; + LOCK_NOTIFIER; /* For concurrency with Tcl_AlertNotifier */ + if (tsdPtr->runLoop) { + tsdPtr->runLoop = NULL; - /* - * Remove runLoopSource from all CFRunLoops and release it. - */ + /* + * Remove runLoopSource from all CFRunLoops and release it. + */ - CFRunLoopSourceInvalidate(tsdPtr->runLoopSource); - CFRelease(tsdPtr->runLoopSource); - tsdPtr->runLoopSource = NULL; - } - UNLOCK_NOTIFIER; + CFRunLoopSourceInvalidate(tsdPtr->runLoopSource); + CFRelease(tsdPtr->runLoopSource); + tsdPtr->runLoopSource = NULL; } + UNLOCK_NOTIFIER; } /* @@ -569,20 +569,20 @@ void Tcl_AlertNotifier( ClientData clientData) { + ThreadSpecificData *tsdPtr = clientData; + if (tclNotifierHooks.alertNotifierProc) { tclNotifierHooks.alertNotifierProc(clientData); return; - } else { - ThreadSpecificData *tsdPtr = (ThreadSpecificData *) clientData; + } - LOCK_NOTIFIER; - if (tsdPtr->runLoop) { - tsdPtr->eventReady = 1; - CFRunLoopSourceSignal(tsdPtr->runLoopSource); - CFRunLoopWakeUp(tsdPtr->runLoop); - } - UNLOCK_NOTIFIER; + LOCK_NOTIFIER; + if (tsdPtr->runLoop) { + tsdPtr->eventReady = 1; + CFRunLoopSourceSignal(tsdPtr->runLoopSource); + CFRunLoopWakeUp(tsdPtr->runLoop); } + UNLOCK_NOTIFIER; } /* @@ -610,13 +610,13 @@ Tcl_SetTimer( 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. - */ } + + /* + * 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. + */ } /* @@ -675,52 +675,54 @@ Tcl_CreateFileHandler( * event. */ ClientData clientData) /* Arbitrary data to pass to proc. */ { + ThreadSpecificData *tsdPtr; + FileHandler *filePtr; + 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; - } - } - if (filePtr == NULL) { - filePtr = (FileHandler*) ckalloc(sizeof(FileHandler)); - filePtr->fd = fd; - filePtr->readyMask = 0; - filePtr->nextPtr = tsdPtr->firstFileHandlerPtr; - tsdPtr->firstFileHandlerPtr = filePtr; + tsdPtr = TCL_TSD_INIT(&dataKey); + + for (filePtr = tsdPtr->firstFileHandlerPtr; filePtr != NULL; + filePtr = filePtr->nextPtr) { + if (filePtr->fd == fd) { + break; } - filePtr->proc = proc; - filePtr->clientData = clientData; - filePtr->mask = mask; + } + 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; - /* - * 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.exceptional)); + } else { + FD_CLR(fd, &(tsdPtr->checkMasks.exceptional)); + } + if (tsdPtr->numFdBits <= fd) { + tsdPtr->numFdBits = fd+1; } } @@ -745,69 +747,71 @@ Tcl_DeleteFileHandler( int fd) /* Stream id for which to remove callback * function. */ { + FileHandler *filePtr, *prevPtr; + int i; + ThreadSpecificData *tsdPtr; + 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). - */ + } - for (prevPtr = NULL, filePtr = tsdPtr->firstFileHandlerPtr; ; - prevPtr = filePtr, filePtr = filePtr->nextPtr) { - if (filePtr == NULL) { - return; - } - if (filePtr->fd == fd) { - break; - } - } + tsdPtr = TCL_TSD_INIT(&dataKey); - /* - * Update the check masks for this file. - */ + /* + * Find the entry for the given file (and return if there isn't one). + */ - if (filePtr->mask & TCL_READABLE) { - FD_CLR(fd, &(tsdPtr->checkMasks.readable)); - } - if (filePtr->mask & TCL_WRITABLE) { - FD_CLR(fd, &(tsdPtr->checkMasks.writable)); + for (prevPtr = NULL, filePtr = tsdPtr->firstFileHandlerPtr; ; + prevPtr = filePtr, filePtr = filePtr->nextPtr) { + if (filePtr == NULL) { + return; } - if (filePtr->mask & TCL_EXCEPTION) { - FD_CLR(fd, &(tsdPtr->checkMasks.exceptional)); + if (filePtr->fd == fd) { + break; } + } - /* - * Find current max fd. - */ + /* + * Update the check masks for this file. + */ - 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 (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)); + } + + /* + * 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; } } + } - /* - * 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; - } - ckfree((char *) filePtr); + if (prevPtr == NULL) { + tsdPtr->firstFileHandlerPtr = filePtr->nextPtr; + } else { + prevPtr->nextPtr = filePtr->nextPtr; } + ckfree((char *) filePtr); } /* @@ -905,203 +909,202 @@ int Tcl_WaitForEvent( const Tcl_Time *timePtr) /* Maximum block time, or NULL. */ { + FileHandler *filePtr; + FileHandlerEvent *fileEvPtr; + int mask, waitForFiles; + Tcl_Time myTime, *myTimePtr; + ThreadSpecificData *tsdPtr; + if (tclNotifierHooks.waitForEventProc) { return tclNotifierHooks.waitForEventProc(timePtr); - } else { - FileHandler *filePtr; - FileHandlerEvent *fileEvPtr; - int mask; - Tcl_Time myTime; - int waitForFiles; - Tcl_Time *myTimePtr; - ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + } + + tsdPtr = TCL_TSD_INIT(&dataKey); + /* + * 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. + */ + + if (timePtr != NULL) { /* - * 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. + * 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 (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. - */ + myTime.sec = timePtr->sec; + myTime.usec = timePtr->usec; - myTime.sec = timePtr->sec; - myTime.usec = timePtr->usec; + if (myTime.sec != 0 || myTime.usec != 0) { + tclScaleTimeProcPtr(&myTime, tclTimeClientData); + } - if (myTime.sec != 0 || myTime.usec != 0) { - tclScaleTimeProcPtr(&myTime, tclTimeClientData); - } + myTimePtr = &myTime; + } else { + myTimePtr = NULL; + } - myTimePtr = &myTime; - } else { - myTimePtr = NULL; + /* + * Start notifier thread if necessary. + */ + + LOCK_NOTIFIER_INIT; + if (!notifierCount) { + Tcl_Panic("Tcl_WaitForEvent: notifier not initialized"); + } + if (!notifierThread) { + int result; + pthread_attr_t attr; + + pthread_attr_init(&attr); + pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + pthread_attr_setstacksize(&attr, 60 * 1024); + result = pthread_create(¬ifierThread, &attr, + (void * (*)(void *))NotifierThreadProc, NULL); + pthread_attr_destroy(&attr); + if (result || !notifierThread) { + Tcl_Panic("Tcl_WaitForEvent: unable to start notifier thread"); } + } + UNLOCK_NOTIFIER_INIT; + + /* + * Place this thread on the list of interested threads, signal the + * notifier thread, and wait for a response or a timeout. + */ + LOCK_NOTIFIER; + if (!tsdPtr->runLoop) { + Tcl_Panic("Tcl_WaitForEvent: CFRunLoop not initialized"); + } + waitForFiles = (tsdPtr->numFdBits > 0); + if (myTimePtr != NULL && myTimePtr->sec == 0 && myTimePtr->usec == 0) { /* - * Start notifier thread if necessary. + * 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. */ - LOCK_NOTIFIER_INIT; - if (!notifierCount) { - Tcl_Panic("Tcl_WaitForEvent: notifier not initialized"); - } - if (!notifierThread) { - int result; - pthread_attr_t attr; - - pthread_attr_init(&attr); - pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); - pthread_attr_setstacksize(&attr, 60 * 1024); - result = pthread_create(¬ifierThread, &attr, - (void * (*)(void *))NotifierThreadProc, NULL); - pthread_attr_destroy(&attr); - if (result || !notifierThread) { - Tcl_Panic("Tcl_WaitForEvent: unable to start notifier thread"); - } - } - UNLOCK_NOTIFIER_INIT; + waitForFiles = 1; + tsdPtr->pollState = POLL_WANT; + myTimePtr = NULL; + } else { + tsdPtr->pollState = 0; + } + if (waitForFiles) { /* - * Place this thread on the list of interested threads, signal the - * notifier thread, and wait for a response or a timeout. + * Add the ThreadSpecificData structure of this thread to the list of + * ThreadSpecificData structures of all threads that are waiting on + * file events. */ - LOCK_NOTIFIER; - if (!tsdPtr->runLoop) { - Tcl_Panic("Tcl_WaitForEvent: CFRunLoop not initialized"); - } - waitForFiles = (tsdPtr->numFdBits > 0); - if (myTimePtr != NULL && myTimePtr->sec == 0 && myTimePtr->usec == 0) { - /* - * 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; + tsdPtr->nextPtr = waitingListPtr; + if (waitingListPtr) { + waitingListPtr->prevPtr = tsdPtr; } + tsdPtr->prevPtr = 0; + waitingListPtr = tsdPtr; + tsdPtr->onList = 1; - 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->prevPtr = 0; - waitingListPtr = tsdPtr; - tsdPtr->onList = 1; - - write(triggerPipe, "", 1); - } + write(triggerPipe, "", 1); + } - FD_ZERO(&(tsdPtr->readyMasks.readable)); - FD_ZERO(&(tsdPtr->readyMasks.writable)); - FD_ZERO(&(tsdPtr->readyMasks.exceptional)); + FD_ZERO(&(tsdPtr->readyMasks.readable)); + FD_ZERO(&(tsdPtr->readyMasks.writable)); + FD_ZERO(&(tsdPtr->readyMasks.exceptional)); - if (!tsdPtr->eventReady) { - CFTimeInterval waitTime; - CFStringRef runLoopMode; + if (!tsdPtr->eventReady) { + CFTimeInterval waitTime; + CFStringRef runLoopMode; - if (myTimePtr == NULL) { - waitTime = 1.0e10; /* Wait forever, as per CFRunLoop.c */ - } else { - waitTime = myTimePtr->sec + 1.0e-6 * myTimePtr->usec; - } - /* - * If the run loop is already running (e.g. if Tcl_WaitForEvent was - * called recursively), re-run it in a custom run loop mode - * containing only the source for the notifier thread, otherwise - * wakeups from other sources added to the common run loop modes - * might get lost. - */ - if ((runLoopMode = CFRunLoopCopyCurrentMode(tsdPtr->runLoop))) { - CFRelease(runLoopMode); - runLoopMode = tclEventsOnlyRunLoopMode; - } else { - runLoopMode = kCFRunLoopDefaultMode; - } - UNLOCK_NOTIFIER; - CFRunLoopRunInMode(runLoopMode, waitTime, TRUE); - LOCK_NOTIFIER; + if (myTimePtr == NULL) { + waitTime = 1.0e10; /* Wait forever, as per CFRunLoop.c */ + } else { + waitTime = myTimePtr->sec + 1.0e-6 * myTimePtr->usec; } - 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. - */ + /* + * If the run loop is already running (e.g. if Tcl_WaitForEvent was + * called recursively), re-run it in a custom run loop mode containing + * only the source for the notifier thread, otherwise wakeups from + * other sources added to the common run loop modes might get lost. + */ - 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; - write(triggerPipe, "", 1); + if ((runLoopMode = CFRunLoopCopyCurrentMode(tsdPtr->runLoop))) { + CFRelease(runLoopMode); + runLoopMode = tclEventsOnlyRunLoopMode; + } else { + runLoopMode = kCFRunLoopDefaultMode; } + UNLOCK_NOTIFIER; + CFRunLoopRunInMode(runLoopMode, waitTime, TRUE); + LOCK_NOTIFIER; + } + tsdPtr->eventReady = 0; + if (waitForFiles && tsdPtr->onList) { /* - * Queue all detected file events before returning. + * 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. */ - for (filePtr = tsdPtr->firstFileHandlerPtr; (filePtr != NULL); - filePtr = filePtr->nextPtr) { + 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; + write(triggerPipe, "", 1); + } - 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; - } + /* + * Queue all detected file events before returning. + */ - if (!mask) { - continue; - } + 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.exceptional))) { + mask |= TCL_EXCEPTION; + } - /* - * Don't bother to queue an event if the mask was previously - * non-zero since an event must still be on the queue. - */ + if (!mask) { + continue; + } - 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); - } - filePtr->readyMask = mask; + /* + * 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) { + fileEvPtr = (FileHandlerEvent *) + ckalloc(sizeof(FileHandlerEvent)); + fileEvPtr->header.proc = FileHandlerEventProc; + fileEvPtr->fd = filePtr->fd; + Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL); } - UNLOCK_NOTIFIER; - return 0; + filePtr->readyMask = mask; } + UNLOCK_NOTIFIER; + return 0; } /* @@ -1233,9 +1236,9 @@ NotifierThreadProc( if (tsdPtr->onList) { /* * Remove the ThreadSpecificData structure of this thread - * from the waiting list. This prevents us from - * continuously spining on select until the other threads - * runs and services the file event. + * from the waiting list. This prevents us from spinning + * continuously on select until the other threads runs and + * services the file event. */ if (tsdPtr->prevPtr) { |