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