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]