On 11/04/2010, at 21.17, Ken Thomases wrote:

> On Apr 8, 2010, at 9:57 AM, Rasmus Skaarup wrote:

>>      [[NSNotificationCenter defaultCenter] addObserver:self 
>>                                                                      
>> selector:@selector(threadPipeReader:) 
>>                                                                      
>> name:NSFileHandleReadCompletionNotification 
>>                                                                      
>> object:nil]; 
>> 
>>      [[NSNotificationCenter defaultCenter] addObserver:self
>>                                                                              
>>          selector:@selector(threadTaskStopped:)
>>                                                                              
>>          name:NSTaskDidTerminateNotification
>>                                                                              
>>          object:nil];
> 
> The above lines register to for those notifications on _all_ tasks and file 
> handles in the whole process.  This is probably not what you want.  You 
> should register for those notifications after you've created the pipe (and 
> its file handle) and the task, and you should register on those objects 
> specifically.

I launch multiple processes, and I do a check to see which one is the one I'm 
getting notified for by doing this in threadPipeReader:

if ( [notification object] == myFileHandle ) 

(and of course I have more NSTasks and NSFileHandles than myTask and 
myFileHandle than my example shows).

So unless you strongly discourage this method, it works out pretty well and I 
am able to distinguish which process is notifying me. The method you suggests 
forces me to create seperate sub-routines for each invokation.

My programs sole purpose is to launch processes and look at their output.

>> -(void)startMyTask {
>> 
>>      NSPipe *pipe = [[NSPipe alloc] init];
>> 
>>      myFileHandle = [pipe fileHandleForReading];
>> 
>>      [myFileHandle readInBackgroundAndNotify];
>> 
>>      myTask = [[NSTask alloc] init];
>>      [myTask setLaunchPath:launchPath];
>>      [myTask setCurrentDirectoryPath:homeDirectory];
>>      [myTask setStandardOutput: pipe];
>>      [myTask setStandardError: pipe];
>>      [myTask setArguments:arguments];
>>      
>>      [myTask launch];
> 
> Are you using garbage collection?  If not, then the above code leaks the pipe.

I'm doing garbage collection.

>> -(void)threadPipeReader:(NSNotification *)notification {
>>      [... snipped ...]
>> }
> 
> That looks reasonable, to the extent that you showed.

Only issue here is that threadPipeReader: does not get called after 
threadTaskStopped: has been called. Even though output is clearly missing.

>> -(void)threadTaskStopped:(NSNotification *)notification {
>> 
>>              NSData *data = [myFileHandle availableData];    
>>                              
>>              while ([data length] > 0) {
>>                      NSLog(@"got some more: %@", [[NSString alloc] 
>> initWithData:data encoding:NSUTF8StringEncoding]);
>>                      NSLog(@"output size: %d", [data length]);       
>>                      data = [myFileHandle availableData];
>>              }
>> 
>> }
>> <code end>
>> 
>> I never got the full output in threadPipeReader, but then I tried to fetch 
>> the data in threadTaskStopped - but that only gives some output. Not all the 
>> way to the end either.
> 
> What you need to do is just mark some internal state so you know the task has 
> exited in threadTaskStopped:.  Then, return to the run loop so that you can 
> continue to receive the notifications from the background reads.  Eventually, 
> you'll receive the end-of-file marker (a zero-length data).  After you've 
> received both the end-of-file and the task termination notification, then you 
> can proceed to make final use of the data and clean up the task (and your 
> registrations with the notification center).

This sounds like the thing I need - however I need a more detailed explanation. 
I don't know what "return to the run loop" means. Can you give a code example?

But threadTaskStopped: does not need to examine data for the process that 
exited. In fact I will prefer to have threadPipeReader: continue to get the 
data fed, even though the process exited. It's not important that the process 
exited.

> The issue you're encountering is probably because there's both a background 
> read in progress and your attempt to synchronously read in the foreground.  

If you think of the availableData calls from threadTaskStopped:, I only put 
them there because threadPipeReader: didn't get called after threadTaskStopped: 
did.

> The background read has probably obtained the "missing" data that you're 
> never seeing from the foreground read.  

Hmmm, I suspect some more code is needed. The part of the program that executes 
these NSTasks is a seperate thread:

App.h: 

-(id) init {

  self = [super init];
// now assigning super init result to self :-)

  if (self) {

        [[NSNotificationCenter defaultCenter] addObserver:self
                                                                                
         selector:@selector(threadPipeReader:)
                                                                                
                 name:NSFileHandleReadCompletionNotification
                                                                                
           object:nil];
        // prepare task termination notification
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                                                
         selector:@selector(threadTaskStopped:)
                                                                                
                 name:NSTaskDidTerminateNotification
                                                                                
           object:nil];

        NSThread *bgThread = [[NSThread alloc] initWithTarget:self 
selector:@selector(myThread:) object:nil];
        [bgThread start];

  }

}


- (void)myThread:(id)param {

        [self performSelectorOnMainThread:@selector(startMyTask:) 
withObject:nil waitUntilDone:YES]; 

}

> If you still aren't getting all of the output you expect, then your task is 
> probably exiting early, perhaps crashing.

I'm stone sure the program is not crashing and I am missing some output. If run 
by hand it works everytime and output is satisfactory.

Thanks for your time Ken. I much appreciate it - bear with me as I have not yet 
earned the Master of Cocoa badge.

Br
Rasmus Skaarup_______________________________________________

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

Reply via email to