summaryrefslogtreecommitdiffstats
path: root/macosx/tclMacOSXNotify.c
diff options
context:
space:
mode:
authordas <das>2009-08-24 00:27:47 (GMT)
committerdas <das>2009-08-24 00:27:47 (GMT)
commit2e2467e1e0a386b1bd53818a449cccc41f27dc87 (patch)
treee90652f7ace1ad92961b82c3f4dbeb74188f5205 /macosx/tclMacOSXNotify.c
parent7389349d58690d0116266237df61841a87b4763c (diff)
downloadtcl-2e2467e1e0a386b1bd53818a449cccc41f27dc87.zip
tcl-2e2467e1e0a386b1bd53818a449cccc41f27dc87.tar.gz
tcl-2e2467e1e0a386b1bd53818a449cccc41f27dc87.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.c77
1 files changed, 51 insertions, 26 deletions
diff --git a/macosx/tclMacOSXNotify.c b/macosx/tclMacOSXNotify.c
index ee4335a..2fae4b6 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.27 2009/07/22 12:00:42 nijtmans Exp $
+ * RCS: @(#) $Id: tclMacOSXNotify.c,v 1.28 2009/08/24 00:27:47 das Exp $
*/
#include "tclInt.h"
@@ -243,6 +243,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 */
@@ -274,7 +276,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;
@@ -454,7 +456,7 @@ Tcl_InitNotifier(void)
CFRunLoopSourceRef runLoopSource;
CFRunLoopSourceContext runLoopSourceContext;
CFRunLoopObserverContext runLoopObserverContext;
- CFRunLoopObserverRef runLoopObserver;
+ CFRunLoopObserverRef runLoopObserver, runLoopObserverTcl;
bzero(&runLoopSourceContext, sizeof(CFRunLoopSourceContext));
runLoopSourceContext.info = tsdPtr;
@@ -478,12 +480,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;
@@ -716,6 +736,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);
@@ -1164,9 +1187,8 @@ int
Tcl_WaitForEvent(
const Tcl_Time *timePtr) /* Maximum block time, or NULL. */
{
- int result, polling;
+ int result, polling, runLoopRunning;
CFTimeInterval waitTime;
- CFStringRef runLoopMode;
SInt32 runLoopStatus;
ThreadSpecificData *tsdPtr;
@@ -1214,22 +1236,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;
@@ -1347,19 +1367,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;
@@ -1367,9 +1390,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: