On Sep 14, 2009, at 6:29 PM, Jerry Krinock wrote:

The reason why I asked this is because I need:
    -[NSTask waitUntilExitOrTimeout:(NSTimeInterval)].
But there is no such method. So, I wrote a wrapper around NSTask that does this.

On 2009 Sep 14, at 07:41, Adam R. Maxwell wrote:

Ordinarily, I'd guess that it's supposed to mean that you can't share instances between threads. However, guesswork when dealing with the threading docs drives me crazy.

For NSTask in particular, I recommend that you interpret that more conservatively...

Also, I presume it means that you should not invoke the same instance from more than one thread. I'm not sure how much more conservative you could get, other than to run it on the main thread.

I wrote an implementation of NSTask from scratch to work around the problem.

Whew, Adam. Since I'm 10.5+, and don't think I'll ever be running more than one NSTask at a time, I won't be quite that conservative. In my NSTask wrapper, I detach a new thread. In this new thread, I set up the task, then run the run loop once...

[task launch] ;
if ([task isRunning]) {
   // Write data to stdin file/pipe
   // ...
   // ...
}

NSDate* limitTime = [NSDate dateWithTimeIntervalSinceNow:timeout] ;
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
                        beforeDate:limitTime] ;
// The above will block and be run to here either due to
// the posting of an NSTaskDidTerminateNotification, or the
// passing of limitTime, whichever occurs first.

That is not a valid assumption (described in the comments and apparent from the rest of the code).

Just do the loop, checking the time and isRunning. Running the run loop once is not necessarily sufficient.

Keep in mind that isRunning can start returning NO before (or after) the notification gets delivered. There's no necessary correlation between the two. If you actually care that the notification has been posted, you should register an observer and change some shared state in the handler method, which the looping can check for.


if (![task isRunning]) {
   taskResult = [task terminationStatus] ;

   // Read stdout and stderr
   // ...
   // ...
}
else {
   taskResult = SSYShellTaskerErrorTimedOut ;

   // Clean up
   kill([task processIdentifier], SIGKILL) ;

   // Set NSError indicating timeout
   // ...
   // ...
}

Actually, I originally was doing this on the main thread, except that the -[NSRunLoop runMode:beforeDate] was in a while() loop. When the loop ran, I would check and see if there was a timeout, task was finished, or neither (presumably some other input source triggered the run). But then I found an obscure bug: If I had disabled undo registration in order to do some behind-the-scenes changes, it would re-enable undo registration. Uh, yeah, it took me a while to track that one down. Apparently, running a run loop that is already running is not a good idea. I suppose I could also have done it with a timer.


If I interpret your explanation of the previous code correctly and it was similar to the code above, the problem here, in a sense, is more your desire to block until the task is finished or a timeout expires. Go back to the main thread. Setup a oneshot NSTimer for the timeout period. Setup a notification handler to listen for the NSTaskDidTerminateNotification. If the timer fires first, kill the task, unregister the notification handler, etc. If the notification happens first, invalidate the timer, unregister the notification handler, etc. Don't run the run loop yourself. Let your code be event-driven.

A run loop can be run re-entrantly, but as Jens said, usually it is done in a private mode, to prevent (say) the default mode stuff from happening at times which are surprising to that stuff (breaking assumptions or requirements it has), and possibly surprising to your app (breaking assumptions or requirements it has). However in this case, the task death notification, if you need that, requires the default run loop mode to be run to get delivered.


Chris Kane
Cocoa Frameworks, Apple




_______________________________________________

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

Reply via email to