summaryrefslogtreecommitdiffstats
path: root/macosx/tclMacOSXNotify.c
diff options
context:
space:
mode:
authordas <das>2009-08-24 00:27:53 (GMT)
committerdas <das>2009-08-24 00:27:53 (GMT)
commit7dee6f186f755d493008c9ee00265f70599e9806 (patch)
tree53b1dc8e00149bc1d7877f8c523539c2bdc63272 /macosx/tclMacOSXNotify.c
parent7f6a947768d59a315f49e28ec93a8122e6eec886 (diff)
downloadtcl-7dee6f186f755d493008c9ee00265f70599e9806.zip
tcl-7dee6f186f755d493008c9ee00265f70599e9806.tar.gz
tcl-7dee6f186f755d493008c9ee00265f70599e9806.tar.bz2
* macosx/tclMacOSXNotify.c: fix multiple issues with nested event loops
when CoreFoundation notifier is running in embedded mode. (fixes problems in TkAqua Cocoa reported by Youness Alaoui on tcl-mac)
Diffstat (limited to 'macosx/tclMacOSXNotify.c')
-rw-r--r--macosx/tclMacOSXNotify.c79
1 files changed, 52 insertions, 27 deletions
diff --git a/macosx/tclMacOSXNotify.c b/macosx/tclMacOSXNotify.c
index f56bb9a..8016b32 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.18.2.4 2009/04/14 00:55:34 das Exp $
+ * RCS: @(#) $Id: tclMacOSXNotify.c,v 1.18.2.5 2009/08/24 00:27:53 das Exp $
*/
#include "tclInt.h"
@@ -246,6 +246,8 @@ typedef struct ThreadSpecificData {
* performed. */
int runLoopRunning; /* True if this thread's Tcl runLoop is running */
int runLoopNestingLevel; /* Level of nested runLoop invocations */
+ int runLoopServicingEvents; /* True if this thread's runLoop is servicing
+ * tcl events */
/* Must hold the notifierLock before accessing the following fields: */
/* Start notifierLock section */
int onList; /* True if this thread is on the waitingList */
@@ -277,7 +279,7 @@ typedef struct ThreadSpecificData {
/* Any other thread alerts a notifier that an
* event is ready to be processed by signaling
* this CFRunLoopSource. */
- CFRunLoopObserverRef runLoopObserver;
+ CFRunLoopObserverRef runLoopObserver, runLoopObserverTcl;
/* Adds/removes this thread from waitingList
* when the CFRunLoop starts/stops. */
CFRunLoopTimerRef runLoopTimer;
@@ -453,7 +455,7 @@ Tcl_InitNotifier(void)
CFRunLoopSourceRef runLoopSource;
CFRunLoopSourceContext runLoopSourceContext;
CFRunLoopObserverContext runLoopObserverContext;
- CFRunLoopObserverRef runLoopObserver;
+ CFRunLoopObserverRef runLoopObserver, runLoopObserverTcl;
bzero(&runLoopSourceContext, sizeof(CFRunLoopSourceContext));
runLoopSourceContext.info = tsdPtr;
@@ -477,12 +479,30 @@ Tcl_InitNotifier(void)
"CFRunLoopObserver");
}
CFRunLoopAddObserver(runLoop, runLoopObserver, kCFRunLoopCommonModes);
- CFRunLoopAddObserver(runLoop, runLoopObserver,
+
+ /*
+ * Create a second CFRunLoopObserver with the same callback as above
+ * for the tclEventsOnlyRunLoopMode to ensure that the callback can be
+ * re-entered via Tcl_ServiceAll() in the kCFRunLoopBeforeWaiting case
+ * (CFRunLoop prevents observer callback re-entry of a given observer
+ * instance).
+ */
+
+ runLoopObserverTcl = CFRunLoopObserverCreate(NULL,
+ kCFRunLoopEntry|kCFRunLoopExit|kCFRunLoopBeforeWaiting, TRUE,
+ LONG_MIN, UpdateWaitingListAndServiceEvents,
+ &runLoopObserverContext);
+ if (!runLoopObserverTcl) {
+ Tcl_Panic("Tcl_InitNotifier: could not create "
+ "CFRunLoopObserver");
+ }
+ CFRunLoopAddObserver(runLoop, runLoopObserverTcl,
tclEventsOnlyRunLoopMode);
tsdPtr->runLoop = runLoop;
tsdPtr->runLoopSource = runLoopSource;
tsdPtr->runLoopObserver = runLoopObserver;
+ tsdPtr->runLoopObserverTcl = runLoopObserverTcl;
tsdPtr->runLoopTimer = NULL;
tsdPtr->waitTime = CF_TIMEINTERVAL_FOREVER;
tsdPtr->tsdLock = SPINLOCK_INIT;
@@ -710,6 +730,9 @@ Tcl_FinalizeNotifier(
CFRunLoopObserverInvalidate(tsdPtr->runLoopObserver);
CFRelease(tsdPtr->runLoopObserver);
tsdPtr->runLoopObserver = NULL;
+ CFRunLoopObserverInvalidate(tsdPtr->runLoopObserverTcl);
+ CFRelease(tsdPtr->runLoopObserverTcl);
+ tsdPtr->runLoopObserverTcl = NULL;
if (tsdPtr->runLoopTimer) {
CFRunLoopTimerInvalidate(tsdPtr->runLoopTimer);
CFRelease(tsdPtr->runLoopTimer);
@@ -1150,9 +1173,8 @@ int
Tcl_WaitForEvent(
Tcl_Time *timePtr) /* Maximum block time, or NULL. */
{
- int result, polling;
+ int result, polling, runLoopRunning;
CFTimeInterval waitTime;
- CFStringRef runLoopMode;
SInt32 runLoopStatus;
ThreadSpecificData *tsdPtr;
@@ -1200,22 +1222,20 @@ Tcl_WaitForEvent(
tsdPtr->runLoopSourcePerformed = 0;
/*
- * If the Tcl 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 the Tcl runloop is already running (e.g. if Tcl_WaitForEvent was
+ * called recursively) or is servicing events via the runloop observer,
+ * re-run it in a custom runloop mode containing only the source for the
+ * notifier thread, otherwise wakeups from other sources added to the
+ * common runloop modes might get lost or 3rd party event handlers might
+ * get called when they do not expect to be.
*/
- if (tsdPtr->runLoopRunning) {
- runLoopMode = tclEventsOnlyRunLoopMode;
- } else {
- runLoopMode = kCFRunLoopDefaultMode;
- tsdPtr->runLoopRunning = 1;
- }
- runLoopStatus = CFRunLoopRunInMode(runLoopMode, waitTime, TRUE);
- if (runLoopMode == kCFRunLoopDefaultMode) {
- tsdPtr->runLoopRunning = 0;
- }
+ runLoopRunning = tsdPtr->runLoopRunning;
+ tsdPtr->runLoopRunning = 1;
+ runLoopStatus = CFRunLoopRunInMode(tsdPtr->runLoopServicingEvents ||
+ runLoopRunning ? tclEventsOnlyRunLoopMode : kCFRunLoopDefaultMode,
+ waitTime, TRUE);
+ tsdPtr->runLoopRunning = runLoopRunning;
LOCK_NOTIFIER_TSD;
tsdPtr->polling = 0;
@@ -1333,19 +1353,22 @@ UpdateWaitingListAndServiceEvents(
{
ThreadSpecificData *tsdPtr = (ThreadSpecificData*) info;
+ if (tsdPtr->sleeping) {
+ return;
+ }
switch (activity) {
case kCFRunLoopEntry:
tsdPtr->runLoopNestingLevel++;
- if (tsdPtr->runLoopNestingLevel == 1 && !tsdPtr->sleeping &&
- (tsdPtr->numFdBits > 0 || tsdPtr->polling)) {
+ if (tsdPtr->numFdBits > 0 || tsdPtr->polling) {
LOCK_NOTIFIER;
- OnOffWaitingList(tsdPtr, 1, 1);
+ if (!OnOffWaitingList(tsdPtr, 1, 1) && tsdPtr->polling) {
+ write(triggerPipe, "", 1);
+ }
UNLOCK_NOTIFIER;
}
break;
case kCFRunLoopExit:
- if (tsdPtr->runLoopNestingLevel == 1 && !tsdPtr->sleeping &&
- (tsdPtr->numFdBits > 0 || tsdPtr->polling)) {
+ if (tsdPtr->runLoopNestingLevel == 1) {
LOCK_NOTIFIER;
OnOffWaitingList(tsdPtr, 0, 1);
UNLOCK_NOTIFIER;
@@ -1353,9 +1376,11 @@ UpdateWaitingListAndServiceEvents(
tsdPtr->runLoopNestingLevel--;
break;
case kCFRunLoopBeforeWaiting:
- if (!tsdPtr->sleeping && tsdPtr->runLoopTimer &&
+ if (tsdPtr->runLoopTimer && !tsdPtr->runLoopServicingEvents &&
(tsdPtr->runLoopNestingLevel > 1 || !tsdPtr->runLoopRunning)) {
+ tsdPtr->runLoopServicingEvents = 1;
while (Tcl_ServiceAll() && tsdPtr->waitTime == 0) {}
+ tsdPtr->runLoopServicingEvents = 0;
}
break;
default:
@@ -1641,7 +1666,7 @@ TclUnixWaitForFile(
if (FD_ISSET(fd, &writableMask)) {
SET_BITS(result, TCL_WRITABLE);
}
- if (FD_ISSET(fd, &exceptionalMask)) {
+ if (FD_ISSET(fd, &exceptionalMask)) {
SET_BITS(result, TCL_EXCEPTION);
}
result &= mask;