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 ([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]