Re: Problem with reading an NSPipe->NSFileHandle to end
Re: Problem with reading an NSPipe->NSFileHandle to end
- Subject: Re: Problem with reading an NSPipe->NSFileHandle to end
- From: Ken Thomases <email@hidden>
- Date: Sun, 11 Apr 2010 14:17:39 -0500
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@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