NSRunLoop's -runMode:beforeDate: not exiting after an NSTask finishes
NSRunLoop's -runMode:beforeDate: not exiting after an NSTask finishes
- Subject: NSRunLoop's -runMode:beforeDate: not exiting after an NSTask finishes
- From: Jens Alfke <email@hidden>
- Date: Thu, 5 Aug 2010 10:10:50 -0700
I’ve got a place in my code where I need to block waiting for an otherwise-asynchronous action to complete, so I use a fairly standard technique of running a nested runloop. But sometimes the runloop just keeps waiting forever even after the action’s completed, so my app locks up.
The wait code looks like:
while (_busy) {
if (![[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode beforeDate: [NSDate distantFuture]])
break;
}
where _busy is an instance variable that will be set to NO by a notification observer method when the async action notifies me that it’s complete.
What I see happening when the bug strikes is that the underlying async code completes (it gets a notification from an NSTask it started), but the runloop keeps going forever, without returning from my above -runMode: call. This doesn’t jibe with my understanding of -runMode: — the docs say it returns after an input source is processed.
Here [fig. 1] is the backtrace from gdb at the moment the NSTask notification is received. The runloop is inside a function “__CFRunLoopDoBlocks”, which in turn calls a block belonging to NSConcreteTask. Presumably this is some implementation detail of NSTask, that it uses a block to delay the actual launch.
What I’m guessing is that running a delayed block does not count as an “input source”. That’s kind of frustrating, because it makes the runloop’s behavior highly dependent on internal details of framework classes — in this case, how was I to know that NSTask used a block and not an input source to handle this? And presumably that means the behavior has changed in 10.6, which would explain some weird NSTask related problems I’ve seen over the past year.
Anyway. How the heck do I work around this? It seems that I need some kind of call that will give the runloop a swift kick and get it to exit the runMode: method. But I don’t see any explicit API for that. Do I need to kludge something together by adding an input source? (A delayed-perform probably won’t work, because I think those are implemented using timers, which are documented as not causing -runMode: to exit.)
—Jens
PS: Oh yes, this is 10.6.4, on a 2009-model iMac.
[fig. 1: the backtrace:]
#0 -[KSDownloadAction taskExited:] (self=0x30f440, _cmd=0x29032, notification=0x403040) at KSDownloadAction.m:323
#1 0x929201c3 in _nsnote_callback ()
#2 0x97fcb3c3 in __CFXNotificationPost ()
#3 0x97fcadca in _CFXNotificationPostNotification ()
#4 0x92915090 in -[NSNotificationCenter postNotificationName:object:userInfo:] ()
#5 0x929aebf5 in __-[NSConcreteTask launchWithDictionary:]_block_invoke_2 ()
#6 0x97ffc861 in __CFRunLoopDoBlocks ()
#7 0x97fad613 in __CFRunLoopRun ()
#8 0x97fac094 in CFRunLoopRunSpecific ()
#9 0x97fabec1 in CFRunLoopRunInMode ()
#10 0x9295a378 in -[NSRunLoop(NSRunLoop) runMode:beforeDate:] ()
#11 0x00011887 in -[KSRegistration updateSynchronously] (self=0x207ac0, _cmd=0x28b9d) at KSRegistration.m:153
#12 0x00010f40 in main (argc=4, argv=0xbffff66c) at main.m:288
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden