Re: Running run loops in 10.6 (was: Need -[NSTask waitUntilExitOrTimeout:])

2009-09-20 Thread Jerry Krinock
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:])

2009-09-20 Thread Ken Thomases

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:])

2009-09-20 Thread Jerry Krinock


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:])

2009-09-18 Thread Jerry Krinock


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 {