Index: macosx/tkMacOSXNotify.c =================================================================== RCS file: /cvsroot/tktoolkit/tk/macosx/tkMacOSXNotify.c,v retrieving revision 1.8 diff -u -p -r1.8 tkMacOSXNotify.c --- macosx/tkMacOSXNotify.c 24 Feb 2005 00:41:04 -0000 1.8 +++ macosx/tkMacOSXNotify.c 17 Apr 2005 04:37:39 -0000 @@ -2,7 +2,7 @@ * tkMacOSXNotify.c -- * * This file contains the implementation of a merged - * Carbon/select-based notifier, which is the lowest-level part + * CFRunLoop/select-based notifier, which is the lowest-level part * of the Tcl event loop. This file works together with * ../generic/tclNotify.c. * @@ -17,11 +17,23 @@ #include "tclInt.h" #include "tkInt.h" +#include +#include #include "tkMacOSXEvent.h" -#include - -extern TclStubs tclStubs; +/* low overhead spinlock SPI from libSystem */ +typedef uint32_t SpinLock_t; +extern void _spin_lock(SpinLock_t *lockp); +extern void _spin_unlock(SpinLock_t *lockp); + +/* spinlocks locking access to global notifier state */ +static SpinLock_t notifierInitLock = 0; +static SpinLock_t notifierLock = 0; + +#define LOCK_NOTIFIER_INIT _spin_lock(¬ifierInitLock) +#define UNLOCK_NOTIFIER_INIT _spin_unlock(¬ifierInitLock) +#define LOCK_NOTIFIER _spin_lock(¬ifierLock) +#define UNLOCK_NOTIFIER _spin_unlock(¬ifierLock) /* * This structure is used to keep track of the notifier info for a @@ -57,6 +69,18 @@ typedef struct FileHandlerEvent { } FileHandlerEvent; /* + * + * The following structure contains a set of select() masks to track + * readable, writable, and exceptional conditions. + */ + +typedef struct SelectMasks { + fd_set readable; + fd_set writable; + fd_set exceptional; +} SelectMasks; + +/* * The following static structure contains the state information for the * select based implementation of the Tcl notifier. One of these structures * is created for each thread that is using the notifier. @@ -65,13 +89,12 @@ typedef struct FileHandlerEvent { typedef struct ThreadSpecificData { FileHandler *firstFileHandlerPtr; /* Pointer to head of file handler list. */ - fd_mask checkMasks[3*MASK_SIZE]; - /* This array is used to build up the masks + + SelectMasks checkMasks; /* This structure is used to build up the masks * to be used in the next call to select. * Bits are set in response to calls to * Tcl_CreateFileHandler. */ - fd_mask readyMasks[3*MASK_SIZE]; - /* This array reflects the readable/writable + SelectMasks readyMasks; /* This array reflects the readable/writable * conditions that were found to exist by the * last call to select. */ int numFdBits; /* Number of valid bits in checkMasks @@ -79,7 +102,6 @@ typedef struct ThreadSpecificData { * Tcl_WatchFile has been called). */ int isMainLoop; /* Is this the main Carbon Loop (in which case * we will call RNE in the actual wait... */ -#ifdef TCL_THREADS int onList; /* True if it is in this list */ unsigned int pollState; /* pollState is used to implement a polling * handshake between each thread and the @@ -89,25 +111,20 @@ typedef struct ThreadSpecificData { * an event have their ThreadSpecificData * structure on a doubly-linked listed formed * from these pointers. You must hold the - * notifierMutex lock before accessing these + * notifierLock before accessing these * fields. */ - Tcl_Condition waitCV; /* Any other thread alerts a notifier - * that an event is ready to be processed - * by signaling this condition variable. */ - int eventReady; /* True if an event is ready to be processed. - * Used as condition flag together with - * waitCV above. */ -#endif + int eventReady; /* True if an event is ready to be processed. */ + CFRunLoopSourceRef runLoopSource; + CFRunLoopRef runLoop; } ThreadSpecificData; static Tcl_ThreadDataKey dataKey; -#ifdef TCL_THREADS /* * The following static indicates the number of threads that have * initialized notifiers. * - * You must hold the notifierMutex lock before accessing this variable. + * You must hold the notifierInitLock before accessing this variable. */ static int notifierCount = 0; @@ -117,7 +134,7 @@ static int notifierCount = 0; * of ThreadSpecificData structures for all threads that are currently * waiting on an event. * - * You must hold the notifierMutex lock before accessing this list. + * You must hold the notifierLock before accessing this list. */ static ThreadSpecificData *waitingListPtr = NULL; @@ -134,24 +151,11 @@ static ThreadSpecificData *waitingListPt * 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 notifierLock lock before writing to the pipe. */ static int triggerPipe = -1; - -/* - * The notifierMutex locks access to all of the global notifier state. - */ - -TCL_DECLARE_MUTEX(notifierMutex) - -/* - * The notifier thread signals the notifierCV when it has finished - * initializing the triggerPipe and right before the notifier - * thread terminates. - */ - -static Tcl_Condition notifierCV; +static int receivePipe = -1; /* * The pollState bits @@ -168,17 +172,13 @@ static Tcl_Condition notifierCV; /* * This is the thread ID of the notifier thread that does select. */ -static Tcl_ThreadId notifierThread; - -#endif +static pthread_t notifierThread; /* * Static routines defined in this file. */ -#ifdef TCL_THREADS static void NotifierThreadProc _ANSI_ARGS_((ClientData clientData)); -#endif static int FileHandlerEventProc _ANSI_ARGS_((Tcl_Event *evPtr, int flags)); @@ -190,11 +190,11 @@ void TkMacOSXAlertNotifier(ClientData cl ClientData TkMacOSXInitNotifier(); void TkMacOSXFinalizeNotifier(ClientData clientData); void TkMacOSXServiceModeHook(int mode); -EventRef TkMacOSXCreateFakeEvent (); + /* *---------------------------------------------------------------------- * - * TkMacOSXSetupTkNotifier -- + * Tk_MacOSXSetupTkNotifier -- * * Replaces the Tcl notifier (from tclUnixNotfy.c) with * the Mac notifier that melds the Unix select based notifer @@ -272,22 +272,51 @@ ClientData TkMacOSXInitNotifier() { ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + CFRunLoopRef runLoop; + CFRunLoopSourceRef runLoopSource; + CFRunLoopSourceContext runLoopSourceContext; -#ifdef TCL_THREADS tsdPtr->eventReady = 0; /* * Start the Notifier thread if necessary. */ - Tcl_MutexLock(¬ifierMutex); + LOCK_NOTIFIER_INIT; if (notifierCount == 0) { - if (Tcl_CreateThread(¬ifierThread, NotifierThreadProc, NULL, - TCL_THREAD_STACK_DEFAULT, TCL_THREAD_NOFLAGS) != TCL_OK) { + int fds[2], status, result; + pthread_attr_t attr; + + 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."); + } + + receivePipe = fds[0]; + triggerPipe = fds[1]; + + 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) { Tcl_Panic("Tcl_InitNotifier: unable to start notifier thread"); } } notifierCount++; + UNLOCK_NOTIFIER_INIT; if (GetCurrentEventLoop() == GetMainEventLoop()) { tsdPtr->isMainLoop = 1; @@ -295,16 +324,18 @@ TkMacOSXInitNotifier() tsdPtr->isMainLoop = 0; } - /* - * Wait for the notifier pipe to be created. - */ - - while (triggerPipe < 0) { - Tcl_ConditionWait(¬ifierCV, ¬ifierMutex, NULL); - } + runLoop = CFRunLoopGetCurrent(); + 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); + tsdPtr->runLoopSource = runLoopSource; + tsdPtr->runLoop = runLoop; + CFRelease(runLoopSource); - Tcl_MutexUnlock(¬ifierMutex); -#endif return (ClientData) tsdPtr; } @@ -330,10 +361,9 @@ void TkMacOSXFinalizeNotifier(clientData) ClientData clientData; /* Not used. */ { -#ifdef TCL_THREADS ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); - Tcl_MutexLock(¬ifierMutex); + LOCK_NOTIFIER_INIT; notifierCount--; /* @@ -342,6 +372,8 @@ TkMacOSXFinalizeNotifier(clientData) */ if (notifierCount == 0) { + int result; + if (triggerPipe < 0) { Tcl_Panic("Tcl_FinalizeNotifier: notifier pipe not initialized"); } @@ -359,17 +391,18 @@ TkMacOSXFinalizeNotifier(clientData) write(triggerPipe, "q", 1); close(triggerPipe); - Tcl_ConditionWait(¬ifierCV, ¬ifierMutex, NULL); + result = pthread_join(notifierThread, NULL); + if (result) { + Tcl_Panic("Tcl_FinalizeNotifier: unable to join notifier thread"); + } + + close(receivePipe); + triggerPipe = -1; } - - /* - * Clean up any synchronization objects in the thread local storage. - */ - - Tcl_ConditionFinalize(&(tsdPtr->waitCV)); - - Tcl_MutexUnlock(¬ifierMutex); -#endif + UNLOCK_NOTIFIER_INIT; + + /* Removes runLoopSource from all runLoops and release it */ + CFRunLoopSourceInvalidate(tsdPtr->runLoopSource); } /* @@ -397,13 +430,12 @@ void TkMacOSXAlertNotifier(clientData) ClientData clientData; { -#ifdef TCL_THREADS ThreadSpecificData *tsdPtr = (ThreadSpecificData *) clientData; - Tcl_MutexLock(¬ifierMutex); + LOCK_NOTIFIER; tsdPtr->eventReady = 1; - Tcl_ConditionNotify(&tsdPtr->waitCV); - Tcl_MutexUnlock(¬ifierMutex); -#endif + UNLOCK_NOTIFIER; + CFRunLoopSourceSignal(tsdPtr->runLoopSource); + CFRunLoopWakeUp(tsdPtr->runLoop); } /* @@ -488,7 +520,6 @@ TkMacOSXCreateFileHandler(fd, mask, proc { ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); FileHandler *filePtr; - int index, bit; for (filePtr = tsdPtr->firstFileHandlerPtr; filePtr != NULL; filePtr = filePtr->nextPtr) { @@ -511,22 +542,20 @@ TkMacOSXCreateFileHandler(fd, mask, proc * Update the check masks for this file. */ - index = fd/(NBBY*sizeof(fd_mask)); - bit = 1 << (fd%(NBBY*sizeof(fd_mask))); - if (mask & TCL_READABLE) { - tsdPtr->checkMasks[index] |= bit; + if ( mask & TCL_READABLE ) { + FD_SET( fd, &(tsdPtr->checkMasks.readable) ); } else { - tsdPtr->checkMasks[index] &= ~bit; - } - if (mask & TCL_WRITABLE) { - (tsdPtr->checkMasks+MASK_SIZE)[index] |= bit; + FD_CLR( fd, &(tsdPtr->checkMasks.readable) ); + } + if ( mask & TCL_WRITABLE ) { + FD_SET( fd, &(tsdPtr->checkMasks.writable) ); } else { - (tsdPtr->checkMasks+MASK_SIZE)[index] &= ~bit; + FD_CLR( fd, &(tsdPtr->checkMasks.writable) ); } - if (mask & TCL_EXCEPTION) { - (tsdPtr->checkMasks+2*(MASK_SIZE))[index] |= bit; + if ( mask & TCL_EXCEPTION ) { + FD_SET( fd, &(tsdPtr->checkMasks.exceptional) ); } else { - (tsdPtr->checkMasks+2*(MASK_SIZE))[index] &= ~bit; + FD_CLR( fd, &(tsdPtr->checkMasks.exceptional) ); } if (tsdPtr->numFdBits <= fd) { tsdPtr->numFdBits = fd+1; @@ -555,8 +584,7 @@ TkMacOSXDeleteFileHandler(fd) int fd; /* Stream id for which to remove callback procedure. */ { FileHandler *filePtr, *prevPtr; - int index, bit, i; - unsigned long flags; + int i; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); /* @@ -577,17 +605,14 @@ TkMacOSXDeleteFileHandler(fd) * Update the check masks for this file. */ - index = fd/(NBBY*sizeof(fd_mask)); - bit = 1 << (fd%(NBBY*sizeof(fd_mask))); - if (filePtr->mask & TCL_READABLE) { - tsdPtr->checkMasks[index] &= ~bit; + FD_CLR( fd, &(tsdPtr->checkMasks.readable) ); } if (filePtr->mask & TCL_WRITABLE) { - (tsdPtr->checkMasks+MASK_SIZE)[index] &= ~bit; + FD_CLR( fd, &(tsdPtr->checkMasks.writable) ); } if (filePtr->mask & TCL_EXCEPTION) { - (tsdPtr->checkMasks+2*(MASK_SIZE))[index] &= ~bit; + FD_CLR( fd, &(tsdPtr->checkMasks.exceptional) ); } /* @@ -595,17 +620,12 @@ TkMacOSXDeleteFileHandler(fd) */ if (fd+1 == tsdPtr->numFdBits) { - for (tsdPtr->numFdBits = 0; index >= 0; index--) { - flags = tsdPtr->checkMasks[index] - | (tsdPtr->checkMasks+MASK_SIZE)[index] - | (tsdPtr->checkMasks+2*(MASK_SIZE))[index]; - if (flags) { - for (i = (NBBY*sizeof(fd_mask)); i > 0; i--) { - if (flags & (((unsigned long)1) << (i-1))) { - break; - } - } - tsdPtr->numFdBits = index * (NBBY*sizeof(fd_mask)) + i; + 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; } } @@ -696,31 +716,6 @@ FileHandlerEventProc(evPtr, flags) return 1; } -void -DoActualWait(timePtr) - Tcl_Time *timePtr; /* Maximum block time, or NULL. */ -{ - ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); - OSErr err; - - if (!tsdPtr->isMainLoop) { - Tcl_ConditionWait(&tsdPtr->waitCV, ¬ifierMutex, timePtr); - } else { - EventRef eventRef; - EventTime waitTime; - Tcl_MutexUnlock(¬ifierMutex); - - if (timePtr == NULL) { - waitTime = kEventDurationForever; - } else { - waitTime = timePtr->sec * kEventDurationSecond - + timePtr->usec * kEventDurationMicrosecond; - } - err = ReceiveNextEvent(0, NULL, waitTime, false, &eventRef); - Tcl_MutexLock(¬ifierMutex); - } -} - /* *---------------------------------------------------------------------- * @@ -746,14 +741,10 @@ TkMacOSXWaitForEvent(timePtr) { FileHandler *filePtr; FileHandlerEvent *fileEvPtr; - struct timeval timeout, *timeoutPtr; - int bit, index, mask; -#ifdef TCL_THREADS + int mask; + Tcl_Time myTime; int waitForFiles; -#else - int numFound; -#endif - + Tcl_Time *myTimePtr; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); /* @@ -763,35 +754,35 @@ TkMacOSXWaitForEvent(timePtr) */ if (timePtr) { - timeout.tv_sec = timePtr->sec; - timeout.tv_usec = timePtr->usec; - timeoutPtr = &timeout; -#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. - */ + /* 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; + + if (myTime.sec != 0 || myTime.usec != 0) { + Tcl_ScaleTimeProc* tclScaleTimeProcPtr; + ClientData tclTimeClientData; + Tcl_QueryTimeProc(NULL, &tclScaleTimeProcPtr, &tclTimeClientData); + + (*tclScaleTimeProcPtr) (&myTime, tclTimeClientData); + } - return -1; -#endif + myTimePtr = &myTime; } else { - timeoutPtr = NULL; + myTimePtr = NULL; } -#ifdef TCL_THREADS /* * Place this thread on the list of interested threads, signal the * notifier thread, and wait for a response or a timeout. */ - Tcl_MutexLock(¬ifierMutex); + LOCK_NOTIFIER; waitForFiles = (tsdPtr->numFdBits > 0); - if (timePtr != NULL && timePtr->sec == 0 && timePtr->usec == 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 @@ -802,7 +793,7 @@ TkMacOSXWaitForEvent(timePtr) waitForFiles = 1; tsdPtr->pollState = POLL_WANT; - timePtr = NULL; + myTimePtr = NULL; } else { tsdPtr->pollState = 0; } @@ -826,10 +817,21 @@ TkMacOSXWaitForEvent(timePtr) write(triggerPipe, "", 1); } - memset((VOID *) tsdPtr->readyMasks, 0, 3*MASK_SIZE*sizeof(fd_mask)); + FD_ZERO( &(tsdPtr->readyMasks.readable) ); + FD_ZERO( &(tsdPtr->readyMasks.writable) ); + FD_ZERO( &(tsdPtr->readyMasks.exceptional) ); if (!tsdPtr->eventReady) { - DoActualWait(timePtr); + CFTimeInterval waitTime; + + if (myTimePtr == NULL) { + waitTime = 1.0e10; + } else { + waitTime = myTimePtr->sec + 1.0e-6 * myTimePtr->usec; + } + UNLOCK_NOTIFIER; + CFRunLoopRunInMode(kCFRunLoopDefaultMode, waitTime, TRUE); + LOCK_NOTIFIER; } tsdPtr->eventReady = 0; @@ -855,41 +857,21 @@ TkMacOSXWaitForEvent(timePtr) } -#else - memcpy((VOID *) tsdPtr->readyMasks, (VOID *) tsdPtr->checkMasks, - 3*MASK_SIZE*sizeof(fd_mask)); - numFound = select(tsdPtr->numFdBits, - (SELECT_MASK *) &tsdPtr->readyMasks[0], - (SELECT_MASK *) &tsdPtr->readyMasks[MASK_SIZE], - (SELECT_MASK *) &tsdPtr->readyMasks[2*MASK_SIZE], timeoutPtr); - - /* - * Some systems don't clear the masks after an error, so - * we have to do it here. - */ - - if (numFound == -1) { - memset((VOID *) tsdPtr->readyMasks, 0, 3*MASK_SIZE*sizeof(fd_mask)); - } -#endif - /* * Queue all detected file events before returning. */ for (filePtr = tsdPtr->firstFileHandlerPtr; (filePtr != NULL); filePtr = filePtr->nextPtr) { - index = filePtr->fd / (NBBY*sizeof(fd_mask)); - bit = 1 << (filePtr->fd % (NBBY*sizeof(fd_mask))); - mask = 0; - if (tsdPtr->readyMasks[index] & bit) { + mask = 0; + if ( FD_ISSET( filePtr->fd, &(tsdPtr->readyMasks.readable) ) ) { mask |= TCL_READABLE; } - if ((tsdPtr->readyMasks+MASK_SIZE)[index] & bit) { + if ( FD_ISSET( filePtr->fd, &(tsdPtr->readyMasks.writable) ) ) { mask |= TCL_WRITABLE; } - if ((tsdPtr->readyMasks+2*(MASK_SIZE))[index] & bit) { + if ( FD_ISSET( filePtr->fd, &(tsdPtr->readyMasks.exceptional) ) ) { mask |= TCL_EXCEPTION; } @@ -911,15 +893,11 @@ TkMacOSXWaitForEvent(timePtr) } filePtr->readyMask = mask; } - + UNLOCK_NOTIFIER; + /* * Also queue the Mac Events found... */ - -#ifdef TCL_THREADS - Tcl_MutexUnlock(¬ifierMutex); -#endif - if (tsdPtr->isMainLoop) { TkMacOSXCountAndProcessMacEvents(); } @@ -927,7 +905,6 @@ TkMacOSXWaitForEvent(timePtr) return 0; } -#ifdef TCL_THREADS /* *---------------------------------------------------------------------- * @@ -958,82 +935,46 @@ NotifierThreadProc(clientData) ClientData clientData; /* Not used. */ { ThreadSpecificData *tsdPtr; - fd_mask masks[3*MASK_SIZE]; - long *maskPtr = (long *)masks; /* masks[] cast to type long[] */ - int fds[2]; - int i, status, index, bit, numFdBits, found, receivePipe, word; + fd_set readableMask; + fd_set writableMask; + fd_set exceptionalMask; + int i, numFdBits = 0; + long found; struct timeval poll = {0., 0.}, *timePtr; - int maskSize = 3 * ((MASK_SIZE) / sizeof(long)) * sizeof(fd_mask); char buf[2]; - if (pipe(fds) != 0) { - Tcl_Panic("NotifierThreadProc: 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."); - } - 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."); - } -#else - if (ioctl(receivePipe, (int) FIONBIO, &status) < 0) { - Tcl_Panic("NotifierThreadProc: could not make receive pipe non blocking."); - } - if (ioctl(fds[1], (int) FIONBIO, &status) < 0) { - Tcl_Panic("NotifierThreadProc: could not make trigger pipe non blocking."); - } -#endif - - /* - * Install the write end of the pipe into the global variable. - */ - - Tcl_MutexLock(¬ifierMutex); - triggerPipe = fds[1]; - - /* - * Signal any threads that are waiting. - */ - - Tcl_ConditionNotify(¬ifierCV); - Tcl_MutexUnlock(¬ifierMutex); - /* * Look for file events and report them to interested threads. */ while (1) { - /* - * Set up the select mask to include the receive pipe. - */ - memset((VOID *)masks, 0, 3*MASK_SIZE*sizeof(fd_mask)); - numFdBits = receivePipe + 1; - index = receivePipe / (NBBY*sizeof(fd_mask)); - bit = 1 << (receivePipe % (NBBY*sizeof(fd_mask))); - masks[index] |= bit; + FD_ZERO( &readableMask ); + FD_ZERO( &writableMask ); + FD_ZERO( &exceptionalMask ); /* - * Add in the check masks from all of the waiting notifiers. + * Compute the logical OR of the select masks from all the + * waiting notifiers. */ - - Tcl_MutexLock(¬ifierMutex); + + LOCK_NOTIFIER; timePtr = NULL; for (tsdPtr = waitingListPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) { - for (i = 0; i < maskSize; i++) { - maskPtr[i] |= ((long*)tsdPtr->checkMasks)[i]; - } - if (tsdPtr->numFdBits > numFdBits) { - numFdBits = tsdPtr->numFdBits; - } + for ( i = tsdPtr->numFdBits-1; i >= 0; --i ) { + if ( FD_ISSET( i, &(tsdPtr->checkMasks.readable) ) ) { + FD_SET( i, &readableMask ); + } + if ( FD_ISSET( i, &(tsdPtr->checkMasks.writable) ) ) { + FD_SET( i, &writableMask ); + } + if ( FD_ISSET( i, &(tsdPtr->checkMasks.exceptional) ) ) { + FD_SET( i, &exceptionalMask ); + } + } + if ( tsdPtr->numFdBits > numFdBits ) { + numFdBits = tsdPtr->numFdBits; + } if (tsdPtr->pollState & POLL_WANT) { /* * Here we make sure we go through select() with the same @@ -1043,14 +984,20 @@ NotifierThreadProc(clientData) tsdPtr->pollState |= POLL_DONE; timePtr = &poll; } - } - Tcl_MutexUnlock(¬ifierMutex); + } + UNLOCK_NOTIFIER; - maskSize = 3 * ((MASK_SIZE) / sizeof(long)) * sizeof(fd_mask); + /* + * Set up the select mask to include the receive pipe. + */ + + if ( receivePipe >= numFdBits ) { + numFdBits = receivePipe + 1; + } + FD_SET( receivePipe, &readableMask ); - if (select(numFdBits, (SELECT_MASK *) &masks[0], - (SELECT_MASK *) &masks[MASK_SIZE], - (SELECT_MASK *) &masks[2*MASK_SIZE], timePtr) == -1) { + if ( select( numFdBits, &readableMask, &writableMask, + &exceptionalMask, timePtr) == -1 ) { /* * Try again immediately on an error. */ @@ -1062,53 +1009,55 @@ NotifierThreadProc(clientData) * Alert any threads that are waiting on a ready file descriptor. */ - Tcl_MutexLock(¬ifierMutex); + LOCK_NOTIFIER; for (tsdPtr = waitingListPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) { found = 0; - for (i = 0; i < maskSize; i++) { - word = maskPtr[i] & ((long*)tsdPtr->checkMasks)[i]; - found |= word; - (((long*)(tsdPtr->readyMasks))[i]) = word; + for ( i = tsdPtr->numFdBits-1; i >= 0; --i ) { + if ( FD_ISSET( i, &(tsdPtr->checkMasks.readable) ) + && FD_ISSET( i, &readableMask ) ) { + FD_SET( i, &(tsdPtr->readyMasks.readable) ); + found = 1; + } + if ( FD_ISSET( i, &(tsdPtr->checkMasks.writable) ) + && FD_ISSET( i, &writableMask ) ) { + 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) ); + found = 1; + } } + if (found || (tsdPtr->pollState & POLL_DONE)) { - 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. - */ - - 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; - tsdPtr->pollState = 0; - } tsdPtr->eventReady = 1; - if (tsdPtr->isMainLoop) { - OSErr err; - - /* We need to wake up the main loop, and let it have the event. */ - EventRef fakeEvent = TkMacOSXCreateFakeEvent(); - EventQueueRef mainEventQueue = GetMainEventQueue(); - - err = PostEventToQueue(mainEventQueue, fakeEvent, - kEventPriorityHigh); - ReleaseEvent(fakeEvent); - } else { - Tcl_ConditionNotify(&tsdPtr->waitCV); - } + 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. + */ + + 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; + tsdPtr->pollState = 0; + } + CFRunLoopSourceSignal(tsdPtr->runLoopSource); + CFRunLoopWakeUp(tsdPtr->runLoop); } } - Tcl_MutexUnlock(¬ifierMutex); + UNLOCK_NOTIFIER; /* * Consume the next byte from the notifier pipe if the pipe was @@ -1116,8 +1065,9 @@ NotifierThreadProc(clientData) * to avoid a race condition we only read one at a time. */ - if (masks[index] & bit) { + if ( FD_ISSET( receivePipe, &readableMask ) ) { i = read(receivePipe, buf, 1); + if ((i == 0) || ((i == 1) && (buf[0] == 'q'))) { /* * Someone closed the write end of the pipe or sent us a @@ -1129,35 +1079,5 @@ NotifierThreadProc(clientData) } } } - - /* - * Clean up the read end of the pipe and signal any threads waiting on - * termination of the notifier thread. - */ - - close(receivePipe); - Tcl_MutexLock(¬ifierMutex); - triggerPipe = -1; - Tcl_ConditionNotify(¬ifierCV); - Tcl_MutexUnlock(¬ifierMutex); -} -#endif - -EventRef -TkMacOSXCreateFakeEvent () -{ - EventKind eKind; - EventClass eClass; - EventTime eTime; - EventRef eventRef; - EventAttributes flags; - eClass = kEventClassWish; - eKind = 0xffff; - eTime = GetLastUserEventTime() + 0.001; - flags = kEventAttributeUserEvent; - if (CreateEvent(NULL,eClass,eKind,eTime,flags,&eventRef) != noErr) { - fprintf(stderr,"CreateEvent failed\n"); - return NULL; - } - return eventRef; + pthread_exit (0); } Index: macosx/Wish.pbproj/project.pbxproj =================================================================== RCS file: /cvsroot/tktoolkit/tk/macosx/Wish.pbproj/project.pbxproj,v retrieving revision 1.38 diff -u -p -r1.38 project.pbxproj --- macosx/Wish.pbproj/project.pbxproj 9 Apr 2005 11:19:00 -0000 1.38 +++ macosx/Wish.pbproj/project.pbxproj 17 Apr 2005 04:37:39 -0000 @@ -126,12 +126,6 @@ settings = { }; }; - F51D903E0181474301DC9062 = { - fileRef = F5875C7B016FEF1D01DC9062; - isa = PBXBuildFile; - settings = { - }; - }; F51D903F018149BD01DC9062 = { buildActionMask = 2147483647; dstPath = "Versions/$(FRAMEWORK_VERSION)/Headers/X11"; @@ -1906,6 +1900,7 @@ F53755DF016C38D201DC9062 = { buildPhases = ( F5877FB7031F97ED016F146B, + F92CCC75080CEBA800E72D64, F53755E0016C38D201DC9062, F53755E1016C38D301DC9062, F53755E2016C38D301DC9062, @@ -1929,10 +1924,10 @@ GLOBAL_CFLAGS = "`source \"${TCL_FRAMEWORK_DIR}/Tcl.framework/tclConfig.sh\"; echo $${}{TCL_EXTRA_CFLAGS} $${}{TCL_DEFS} | sed -e 's|\\\\\\\\\\\\\\\"|\\\"|g' -e 's|\\\\\\\\\\\\ |_|g'`"; HEADER_SEARCH_PATHS = "\"$(TCL_FRAMEWORK_DIR)/Tcl.framework/Headers\" \"$(TCL_FRAMEWORK_DIR)/Tcl.framework/PrivateHeaders\" . ../bitmaps ../generic ../xlib"; INSTALL_PATH = "${DYLIB_INSTALL_PATH}"; - LIBRARY_SEARCH_PATHS = ""; + LIBRARY_SEARCH_PATHS = "\"$(TCL_FRAMEWORK_DIR)/Tcl.framework\""; OPTIMIZATION_CFLAGS = "-O0"; - OTHER_CFLAGS = "-DMAC_OSX_TK -DTCL_WIDE_INT_TYPE=\"long long\""; - OTHER_LDFLAGS = "-seg1addr 0xb000000"; + OTHER_CFLAGS = "-DMAC_OSX_TK -DUSE_TCL_STUBS -DTCL_WIDE_INT_TYPE=\"long long\""; + OTHER_LDFLAGS = "-ltclstub${FRAMEWORK_VERSION} -seg1addr 0xb000000 -Wl,-search_paths_first -unexported_symbols_list \"${TEMP_DIR}/tclstub.exp\""; OTHER_LIBTOOL_FLAGS = ""; OTHER_REZFLAGS = "-i \"$(TCL_FRAMEWORK_DIR)/Tcl.framework/Headers\" -i \"../generic\""; PRECOMPILE_PREFIX_HEADER = YES; @@ -2172,7 +2167,6 @@ MacOS X Port by Jim Ingham <jingham@a F53755E3016C38D301DC9062 = { buildActionMask = 2147483647; files = ( - F51D903E0181474301DC9062, F537567E016C3ADB01DC9062, F50D96130196176E01DC9062, ); @@ -4284,6 +4278,19 @@ MacOS X Port by Jim Ingham <jingham@a //F92 //F93 //F94 + F92CCC75080CEBA800E72D64 = { + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + isa = PBXShellScriptBuildPhase; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "# extract global symbols in libtclstub so that we can unexport them from Tk.framework\nnm -gjp \"${TCL_FRAMEWORK_DIR}/Tcl.framework/libtclstub${FRAMEWORK_VERSION}.a\" | tail +3 > \"${TEMP_DIR}/tclstub.exp\""; + }; F92ED9910403D0F0006F146B = { fileEncoding = 5; isa = PBXFileReference;