Re: Running an NSTask Within an NSThread
Re: Running an NSTask Within an NSThread
- Subject: Re: Running an NSTask Within an NSThread
- From: James Bucanek <email@hidden>
- Date: Sun, 27 Jan 2008 17:35:02 -0700
Jonathan Dann <mailto:email@hidden> wrote (Sunday, January
27, 2008 12:13 PM -0000):
You were half-right with the over-releasing thing. All my code for
running the task is in a TaskController object, which was being
autoreleased after the task was run, but before the NSTaskDidTerminate
was sent. I'd registered the object as an observer for this
notification but not removed is as the observer in the dealloc method
for the class! It suddenly occurred to me that a notification was
being sent to a dealloc'd object.
Now I have the problem of my taskController being dealloc'd before the
task has finished (whether its in a new thread or not) so the
NSTaskDidTerminateNotification is not being received by the object. I
*could* call [self autorelease] in the -taskDidTerminate: method, but
that will violate the memory management conventions. I currently have
a class method that instantiates my TaskController object, this is
called from my NSDocument subclass. So NSDocument subclass calls:
<clipped>
Jon,
See three possible solutions to this. You can be the judge of
which one you like.
The real problem here is that your TaskController is associated
with the running NSTask, but really isn't owned by any other
object. This is the kind of situation where the typical Obj-C
memory management rules break down.
----
1) I don't see any reason why the TaskController can't retain
itself until it receives the taskDidTerminate: message. The
TaskController is really "owned" by the NSTask, but you can't
make the NSTask retain your TaskController until its done. So
the TaskController can act as a proxy owner for itself until the
NSTask terminates.
2) Take ownership of the TaskController in your NSDocument.
Instead of creating an autoreleased TaskController and throwing
it to the wind, the document could retain the TaskController
until the TaskController was finished (which it could
communicate back to the document object using a variety of
means). Personally, I find this awkward, but it's "clean."
3) The "elegant" and "non polling" solution would be to spawn a
new thread running [TaskController performTask]. In
TaskController, create an NSConditionalLock object (let's call
it taskDoneLock) with an initial condition of NO. The thread
would start the NSTask then perform [taskDoneLock lockWhenCondition:YES];
In the taskDidTerminate: method, perform [taskDoneLock lock];
[taskDoneLock unlockWithCondition:YES];
The performTask basicaly goes to sleep waiting for
taskDidTerminate: to "wake it up". performTask then continues
executing. It now knows that the task has completed and can
perform whatever post-processing it needs to before returning,
which terminates the thread.
The "elegant" part of this solution is that [NSTask
detachNewThreadSelector:toTarget:withObject:] retains the
TaskController object until the thread terminates. By using a
semaphore lock to signal when the NSTask is done, the thread
exists (suspended) until the NSTask completes.
----
As for you timeout problem, you can start an NSTimer to send a
message to another handler if you haven't received the
taskDidTerminate: message in a reasonable amount of time. Your
taskDidTerminate: would invalidate the timer so if the task ends
before the timeout, the timer will never fire.
If you adopt solution #3, you have the added option of using
[taskDoneLock lockWhenCondition:YES beforeDate:<NSDate*>]. This
will cause performTask to wait until the lock is release OR the
time-out time has arrived. You can then decide if you want to
keep waiting for the task to finish or do something else.
Good luck,
James
--
James Bucanek
_______________________________________________
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