Re: Need -[NSTask waitUntilExitOrTimeout:] (was NSTask "Thread Unsafe"...)
Re: Need -[NSTask waitUntilExitOrTimeout:] (was NSTask "Thread Unsafe"...)
- Subject: Re: Need -[NSTask waitUntilExitOrTimeout:] (was NSTask "Thread Unsafe"...)
- From: Chris Kane <email@hidden>
- Date: Wed, 16 Sep 2009 14:07:27 -0700
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 (email@hidden)
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:
This email sent to email@hidden