views:

591

answers:

8

Has anyone else seen or heard of any issues with NSTask in 10.6?

This code worked fine yesterday, and is not working today.

NSTask *task = [converter task];
[task waitUntilExit];
NSLog(@"Task did complete");

The task does what it's supposed to do (I checked the output and it's fine), but the program will wait indefinitely at the waitUntilExit method. I have a few unit tests that use similar code, they all had passed previously, but as of yesterday, they are no longer working.

A: 

Check ps, top, or Activity Monitor. Make sure your process exited.

Peter Hosey
It shows up in Activity Monitor and exits fine. The files that it's converting are converted and not corrupted. It appears that the task is completing, but somehow not informing anyone that it has completed.
kubi
+1  A: 

(1) Do you process the output from the task in any way? If so, the buffering of the output can block if you don't read & process the data along the way.

(2) Does your task wait for EOL on stdin before terminating? If so, you'll want to grab stdin and explicitly close it.

As Peter said, check ps to see if your task is still running. If it is, use sample to sample it to figure out what it is up to.

bbum
+1  A: 

Possibly- I noticed a problem with some of my code on 10.6 that sounds suspiciously like your problem.

I needed to get some network info (mostly concerning BGP and AS stuff) in a totally asynchronous manner. The ideal solution for me is to send special DNS TXT record requests to a publicly accessible DNS server, but Cocoa/Core Foundation doesn't provide an API for doing these kinds of odd ball DNS requests. Another way was to use shell% whois -h IP 'SPECIAL REQUEST' and just parse out the relevant info from the output. This was something that I could get up and running in a matter of hours and come back later and put in a real solution. Very hacky, but an awful lot faster than 1) finding a suitable asynchronous DNS library and 2) getting up to speed on its API and possibly writing a wrapper for it.

So, I created a class that forks off a background thread, and then NSTask to kick off the whois request. The background thread sits in a NSRunLoop loop to process the results, but checks every ~1/10 of a second to see if the NSTask died for some reason, or if [NSThread isCanceled], etc etc. It also registers for NSApplicationWillTerminateNotification notifications so it can do proper cleanup if the app is quitting.

Well, as of 10.6... I could no longer quit 'quickly'. On 10.5, everything is glass smooth, and the app quits instantly, at least perceptually. Under 10.6, quitting causes the app to 'hang'. Debugging showed that everything was getting hung up trying to clean up lingering whois NSTasks. Since none of the information I get this way is critical to the apps functionality (it's extra, nice to know kind of info), I just sort of punted and stopped trying to get the info as a stop gap solution.

The quick passes at debugging the problem showed that the main app was blocking on trying to acquire the instances NSLock. What's interesting is that if I just leave it alone, the application does eventually exit normally- anywhere from 10-20 seconds to minutes.. something changed between 10.5 and 10.6 that is causing code bracketed inside a [NSLock lock] ... [NSLock unlock] block to 'take a long time'. I have yet to track down where that's happening, though (not yet a priority).... but one of those things is terminating the background NSTask if it's still running and 'waiting for it to finish up' before it can safely get rid of stuff like its NSPipe.

DUP: This might be a dupe answer... my first one seems to have vanished in to the either?

johne
That sounds a lot like my issue. I'm not touching NSLock, the new process is controlled entirely through NSTask, so my problem lies somewhere in there. Sucks that you don't (yet) have a solution, but i'll keep you updated if I figure anything out.
kubi
A: 

What happens if you substitute a simple script that just pulls an exit 0; for the one you're using now?

Azeem.Butt
A: 

You man need to launch the task first.

Steve D
+1  A: 

You need to add the following magic line:

[task setStandardInput:[NSPipe pipe]];
jrtc27
A: 

Similar NSTask deadlock described here:

http://dev.notoptimal.net/2007/04/nstasks-nspipes-and-deadlocks-when.html

gulli
A: 

Not sure if that helps, but you can try doing the wait on a separate thread:

[NSThread detachNewThreadSelector: @selector(foo) toTarget: self withObject: task];

...

- (void) foo: (NSTask *) task {
  [task launch];
  [task waitUntilExit];
  ...

I had a problem where I didn't get the NSNotification when the task finished, and this fixed it. Apparently there are some bugs in the NSTask implementation under 10.6...

Psionides