Re: Running run loops in 10.6 (was: Need -[NSTask waitUntilExitOrTimeout:])
Trying to find a way to tickle a run loop into returning in Mac OS 10.6, I wrote another little tool which, instead of spawning a short- duration NSTask, sends a message to a mach port which has been added to the current run loop. To my surprise, result is the same. Running in Mac OS 10.5, when the message is received, the run loop returns from runMode:beforeDate:. Running in Mac OS 10.6, it never returns. Now I understand that there is this new thing called Grand Central Dispatch with dispatch queues. But the documentation on run loops in the Threading Programming Guide still says that ports in particular are input sources to run loops, and the documentation for - runMode:beforeDate: says that it returns after ... the first input source is processed. Why does -runMode:beforeDate: not return after processing input from a port in Mac OS 10.6? Can anyone provide a little code which will cause the following line to unblock in Mac OS 10.6? [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDatedistantFuture]] ; Sincerely, Jerry Krinock ** New Demo Tool: Mach Ports #import Cocoa/Cocoa.h @interface MessageSender : NSObject { } + (void)sendMessage:(NSTimer*)timer ; @end @implementation MessageSender + (void)sendMessage:(NSTimer*)timer { NSPort* receivePort = [timer userInfo] ; NSPort* sendPort = [NSMachPort port] ; NSData* data = [@Hello dataUsingEncoding:NSUTF8StringEncoding] ; NSPortMessage* message = [[NSPortMessage alloc] initWithSendPort:sendPort receivePort:receivePort components: [NSArray arrayWithObject:data]] ; BOOL didSend = [message sendBeforeDate:[NSDate dateWithTimeIntervalSinceNow:0.2]] ; NSLog(@Sent message; %...@., didSend ? @succeeded : @failed) ; [message release] ; } @end int main(int argc, const char *argv[]) { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init] ; NSPort* receivePort = [NSMachPort port] ; [[NSRunLoop currentRunLoop] addPort:receivePort forMode:NSDefaultRunLoopMode] ; [NSTimer scheduledTimerWithTimeInterval:1.0 target:[MessageSenderclass] selector:@selector(sendMessage:) userInfo:receivePort repeats:YES] ; NSLog(@Will run run loop) ; NSInteger i = 0 ; NSInteger msgLimit = 3 ; while (YES) { NSLog(@ top of loop) ; BOOL moreInputSources = [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate: [NSDatedistantFuture]] ; NSLog(@Run loop has returned from running) ; NSLog(@ moreInputSources = %d, moreInputSources) ; if (!moreInputSources) { NSLog(@Breaking because no more input sources.) ; break ; } i++ ; if (i 2) { NSLog(@Breaking because we've sent %d messages., msgLimit) ; break ; } } NSLog(@Run loop has quit. Exitting.) ; [pool release] ; return 0 ; } *** Log output running in Mac OS 10.5: TestTool 23:11:22.831 TestTool[236:10b] Will run run loop TestTool 23:11:22.836 TestTool[236:10b]top of loop TestTool 23:11:23.832 TestTool[236:10b] Sent message; succeeded. TestTool 23:11:23.839 TestTool[236:10b] Run loop has returned from running TestTool 23:11:23.841 TestTool[236:10b]moreInputSources = 1 TestTool 23:11:23.841 TestTool[236:10b]top of loop TestTool 23:11:24.832 TestTool[236:10b] Sent message; succeeded. TestTool 23:11:24.832 TestTool[236:10b] Run loop has returned from running TestTool 23:11:24.833 TestTool[236:10b]moreInputSources = 1 TestTool 23:11:24.833 TestTool[236:10b]top of loop TestTool 23:11:25.832 TestTool[236:10b] Sent message; succeeded. TestTool 23:11:25.834 TestTool[236:10b] Run loop has returned from running TestTool 23:11:25.834 TestTool[236:10b]moreInputSources = 1 TestTool 23:11:25.835 TestTool[236:10b] Breaking because we've sent 3 messages. TestTool 23:11:25.835 TestTool[236:10b] Run loop has quit. Exitting. ** Log output running in Mac OS 10.6: Snow-Leopards-Mac-Pro:~ sl$ /Applications/TestTool TestTool 22:50:06.485 TestTool[154:903] Will run run loop TestTool 22:50:06.489 TestTool[154:903]top of loop TestTool 22:50:07.485 TestTool[154:903] Sent message; succeeded. TestTool 22:50:08.485 TestTool[154:903] Sent message; succeeded. TestTool 22:50:09.485 TestTool[154:903] Sent message; succeeded. TestTool 22:50:10.485 TestTool[154:903] Sent message; succeeded. TestTool 22:50:11.485 TestTool[154:903] Sent message; succeeded. ...continues forever like this, never exits.
Re: Running run loops in 10.6 (was: Need -[NSTask waitUntilExitOrTimeout:])
On Sep 20, 2009, at 1:38 AM, Jerry Krinock wrote: Trying to find a way to tickle a run loop into returning in Mac OS 10.6, I wrote another little tool which, instead of spawning a short- duration NSTask, sends a message to a mach port which has been added to the current run loop. To my surprise, result is the same. Running in Mac OS 10.5, when the message is received, the run loop returns from runMode:beforeDate:. Running in Mac OS 10.6, it never returns. + (void)sendMessage:(NSTimer*)timer { NSPort* receivePort = [timer userInfo] ; NSPort* sendPort = [NSMachPort port] ; NSData* data = [@Hello dataUsingEncoding:NSUTF8StringEncoding] ; NSPortMessage* message = [[NSPortMessage alloc] initWithSendPort:sendPort receivePort:receivePort components: [NSArray arrayWithObject:data]] ; You've got the send port and receive port mixed up here. The send port is the one you're sending to. The receive port is the one on which you'd receive any reply, if there is one. Since the timer user info contains the port which was added to the run loop, you want to send to that one. BOOL didSend = [message sendBeforeDate:[NSDate dateWithTimeIntervalSinceNow:0.2]] ; NSLog(@Sent message; %...@., didSend ? @succeeded : @failed) ; [message release] ; } Regards, Ken ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) 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: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Running run loops in 10.6 (was: Need -[NSTask waitUntilExitOrTimeout:])
On 2009 Sep 20, at 02:50, Ken Thomases wrote: You've got the send port and receive port mixed up here. The send port is the one you're sending to. The receive port is the one on which you'd receive any reply, if there is one. Since the timer user info contains the port which was added to the run loop, you want to send to that one. Thanks, Ken. Indeed, simply reversing makes it work the same in 10.5 and 10.6. Now I have to go think about what this means. ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) 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: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Running run loops in 10.6 (was: Need -[NSTask waitUntilExitOrTimeout:])
On 2009 Sep 16, at 14:07, Chris Kane wrote: On Sep 14, 2009, at 6:29 PM, I wrote: [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:limitTime] ; // The above will block and be run to here either due to // the posting of an NSTaskDidTerminateNotification, ... That is not a valid assumption (described in the comments and apparent from the rest of the code). Yup. It works in 10.5 but not 10.6. Something is tickling this run loop in 10.5, within a few milliseconds of the NSTaskDidTerminateNotification, so I thought it was the notification itself. Re-reading about Run Loops in Threading Programming Guide [1], I learn that notifications are in fact *not* input sources. The difference in 10.6 is probably explained in the 10.6 Cocoa Foundation Release Notes [2], which I would paraphrase as run loops which were tickled into running by mysterious input sources in 10.5 may no longer get those tickles in 10.6. Someone please correct me if I misunderstood. So, I'd like to implement Chris Kane's preferred solution Go back to the main thread. Setup a oneshot NSTimer for the timeout period. Setup a notification handler to listen for the NSTaskDidTerminateNotification. If the timer fires first, kill the task, unregister the notification handler, etc. If the notification happens first, invalidate the timer, unregister the notification handler, etc. Don't run the run loop yourself. Let your code be event-driven. That would certainly work, but I sometimes need to run this show in a background worker tool, or in secondary thread -- not just in the main thread of an app. How can I detect notifications and use them for controlling program flow in a non-event-driven environment? To illustrate the problem, I've appended a demo program [3]. Like my original code, the demo works in 10.5 -- the timeout and NSTaskDidTerminateNotification are received *and* the run loop runs... 21:35:56.969 TestTool[366:10b] ver 1.2 21:35:56.982 TestTool[366:10b] Will run run loop 21:35:56.985 TestTool[366:10b]top of loop 21:35:57.122 TestTool[366:10b] Succeeded : Task with timeout: 1.00 cmd: /bin/sleep 0.10 21:35:57.124 TestTool[366:10b]moreInputSources = 1 21:35:57.164 TestTool[366:10b]nTasks = 1 21:35:57.171 TestTool[366:10b]top of loop 21:35:57.481 TestTool[366:10b] Timed out : Task with timeout: 0.50 cmd: /bin/sleep 0.80 21:35:57.795 TestTool[366:10b]moreInputSources = 1 21:35:57.797 TestTool[366:10b]nTasks = 0 21:35:57.798 TestTool[366:10b] All tasks are done. Exitting. Running same executable in 10.6, the run loop never runs, and thus the program never exits... 22:05:14.137 TestTool[141:903] ver 1.2 22:05:14.152 TestTool[141:903] Will run run loop 22:05:14.154 TestTool[141:903]top of loop 22:05:14.254 TestTool[141:903] Succeeded : Task with timeout: 1.00 cmd: /bin/sleep 0.10 22:05:14.643 TestTool[141:903] Timed out : Task with timeout: 0.50 cmd: /bin/sleep 0.80 P.S. Interesting how run loops work. As you can see from the code, after logging 22:05:14 top of loop, the next statement invokes - runMode:beforeDate: which apparently blocks forever because the NSLog in the following line never logs. But, while it's blocked there, it still receives and processes the NSTaskDidTerminateNotification and timer firing. Thanks very much, Jerry Krinock 1. http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html#/ /apple_ref/doc/uid/1057i-CH16-SW19 2. http://developer.apple.com/mac/library/releasenotes/Cocoa/Foundation.html See the end of the second paragraph of the section named Concurrency, GCD, and Run Loop Cautions (New since November seed) 3. Demo Program /* This program creates two TaskWrapper objects. Each TaskWrapper runs an NSTask launching /bin/sleep. The first one succeeds because its sleep argument of 0.1 seconds is less than its timeout of 1.0 seconds. The second one times out because its sleep argument of 0.8 seconds is greater than its timeout time of 0.5 seconds. */ #import Cocoa/Cocoa.h static NSMutableArray* activeTaskWrappers ; @interface TaskWrapper : NSObject { NSTask* m_task ; NSTimer* m_timeoutTimer ; NSString* m_signature ; NSString* m_status ; } @property (retain) NSTask* task ; @property (retain) NSTimer* timeoutTimer ; @property (copy) NSString* signature ; @property (copy) NSString* status ; @end @implementation TaskWrapper @synthesize task = m_task ; @synthesize timeoutTimer = m_timeoutTimer ; @synthesize signature = m_signature ; @synthesize status = m_status ; - (void)dealloc { [m_task release] ; [m_timeoutTimer release] ; [m_signature release] ; [super dealloc] ; } - (void)doShellTaskCommand:(NSString*)command arguments:(NSArray*)arguments timeout:(NSTimeInterval)timeout {