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

> I'm trying to execute a task in the background and parsing the output from 
> the task along the way. However I get the NSTaskDidTerminateNotification 
> before all the output from the task has been delivered by 
> NSFileHandleReadCompletionNotification

This is completely ordinary.  There are two independent inter-process 
communication mechanisms at work, and there's no guarantee that all of the 
output data will arrive at your process and be delivered in a notification 
before the task termination notification is delivered.

> - and I am not able to squeeze much more (but in some cases a little, but 
> never all the way to the end) out of the filehandle after the task exits.

From what you say below, I'm not sure that's accurate.

> - (id) init {
>       [super init];

You should be assigning the result from [super init] to self.  You should also 
be checking if it's nil.

>       [[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.

>       return self;
> }
> 
> -(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.

> }
> 
> -(void)threadPipeReader:(NSNotification *)notification {
>       [... snipped ...]
> }

That looks reasonable, to the extent that you showed.

> -(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).

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.  The 
background read has probably obtained the "missing" data that you're never 
seeing from the foreground read.  You will never see it if you don't allow the 
run loop to fire -- for example, if you terminate the thread after getting the 
task termination notification.  Even if you did see it, you'd get it out of 
order with respect to the synchronous foreground read you're doing.  There's no 
telling which read operation would get any particular chunk of data.

Abandon the foreground reading and the assumption that all data will have 
arrived by the time you get the task termination notification.  Use only 
background reading and keep running the run loop until you get both end-of-file 
and task-terminated indicators.

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

Regards,
Ken

_______________________________________________

Cocoa-dev mailing list ([email protected])

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 [email protected]

Reply via email to