Yet another problem with NSTask and NSPipe

Dragan Milić milke at mac.com
Sun Apr 22 05:17:11 PDT 2007


Hello all,

I know this topic has been beaten do death during past seven years,  
yet after reading from archives all that I could find about it and  
after struggling for couple of days, I'm not able to solve it, hence  
need some help.

The situation is quite standard. I run hdiutil inside NSTask. The  
goal is to get information on an image and use it afterwards. For  
that reason I use -plist option of the hdiutil, so the NSTask should do:

hdiutil imageinfo -plist image_name

Not to spend too many words, here is the part of code that I use:

	NSPipe *stdInPipe = [NSPipe pipe];
	NSPipe *stdOutPipe = [NSPipe pipe];
	NSFileHandle *stdOutHandle = [stdOutPipe fileHandleForReading];
	[[NSNotificationCenter defaultCenter]
			addObserver:self
			selector:@selector(outPipeDataAvailable:)
             name:NSFileHandleReadCompletionNotification
             object:stdOutHandle];
	NSTask *task = [[[NSTask alloc] init] autorelease];
	[task setStandardInput:stdInPipe];
	[task setStandardOutput:stdOutPipe];
	[task setArguments:[NSArray arrayWithObjects:@"hdiutil",  
@"imageinfo", @"-plist", imageName, nil]];
	[task setLaunchPath:@"/usr/bin/env"];
	[stdOutHandle readInBackgroundAndNotify];
	[self setStdOutData:[NSMutableData data]];
	[task launch];
	[task waitUntilExit];
	int terminationStatus = [task terminationStatus];
	[task release];
	if (terminationStatus != 0) {
		// error handling
	}
	[[NSNotificationCenter defaultCenter]
			removeObserver:self
			name:NSFileHandleReadCompletionNotification
			object:nil];
	NSDictionary *properties = [[[[NSString alloc] initWithData:stdOutData
			encoding:NSASCIIStringEncoding] autorelease] propertyList];
	...
	...

The notification method:

- (void)outPipeDataAvailable:(NSNotification *)notification
{
     NSData *data = [[notification userInfo]  
objectForKey:NSFileHandleNotificationDataItem];
     if ([data length] > 0) {
		[stdOutData appendData:data];
		[[notification object] readInBackgroundAndNotify];
     }
}

When I run this code on 900Mhz G3 iBook, it runs just fine. I can  
notice that the whole plist dictionary is read and stuffed into  
stdOutData in a few readings, meaning that the notification method  
gets called several times.

But, if I run this code on much faster computer, e.g. 2.0GHz Intel  
Core 2 Duo MacBook, I notice that the notification method gets called  
only once, and then the task finishes its execution, leaving me with  
incomplete output that cannot be parsed into -[NSString propertyList]  
method. Like the task has been executed so quickly that the output  
pipe hasn't had a chance to be read to the end.

I've tried various approaches and everything that I could find on  
mailing lists on NSTask and NSPipe topics and that I could think of,  
including pooling file descriptor of stdOutHandle with select() and  
setting it to O_NONBLOCK, reading in separate thread, putting delays  
in the main method, postponing removing of object from notification  
center until later, postponing releasing of NSTask object until  
later,... None of it helped.

Can anyone point to me the correct way of doing this, which will  
discard all possible timing issues? Any help is much appreciated.

Cheers,
Milke


More information about the MacOSX-dev mailing list