On Apr 11, 2010, at 3:18 PM, Rasmus Skaarup wrote:

> On 11/04/2010, at 21.17, Ken Thomases wrote:
> 
>> On Apr 8, 2010, at 9:57 AM, Rasmus Skaarup wrote:
> 
>>>     [[NSNotificationCenter defaultCenter] addObserver:self 
>>>                                                                     
>>> selector:@selector(threadPipeReader:) 
>>>                                                                     
>>> name:NSFileHandleReadCompletionNotification 
>>>                                                                     
>>> object:nil]; 
>>> 
>>>     [[NSNotificationCenter defaultCenter] addObserver:self
>>>                                                                             
>>>          selector:@selector(threadTaskStopped:)
>>>                                                                             
>>>          name:NSTaskDidTerminateNotification
>>>                                                                             
>>>          object:nil];
>> 
>> The above lines register to for those notifications on _all_ tasks and file 
>> handles in the whole process.  This is probably not what you want.  You 
>> should register for those notifications after you've created the pipe (and 
>> its file handle) and the task, and you should register on those objects 
>> specifically.
> 
> I launch multiple processes, and I do a check to see which one is the one I'm 
> getting notified for by doing this in threadPipeReader:
> 
> if ( [notification object] == myFileHandle ) 
> 
> (and of course I have more NSTasks and NSFileHandles than myTask and 
> myFileHandle than my example shows).

OK, that's fairly reasonable.  Given that you're checking the notification 
object is one of the ones you're interested in, it's safe.

> So unless you strongly discourage this method, it works out pretty well and I 
> am able to distinguish which process is notifying me. The method you suggests 
> forces me to create seperate sub-routines for each invokation.

That's actually not true.  You can observe specific objects while using the 
same selector for all of the observations.

The distinction between this and what you are apparently doing is that the 
framework could theoretically be using NSFileHandles or (less likely) NSTask 
for its own purposes.  If you observe indiscriminately, then your method may 
get called for file handles or tasks that you didn't explicitly create.  You 
then check the notification objects, which filters out the ones you didn't 
create.  If you observe only the specific objects you create, then your method 
is only invoked for them and not for any framework-created objects.  Either way 
is workable, although I personally prefer to be specific.

>> What you need to do is just mark some internal state so you know the task 
>> has exited in threadTaskStopped:.  Then, return to the run loop so that you 
>> can continue to receive the notifications from the background reads.  
>> Eventually, you'll receive the end-of-file marker (a zero-length data).  
>> After you've received both the end-of-file and the task termination 
>> notification, then you can proceed to make final use of the data and clean 
>> up the task (and your registrations with the notification center).
> 
> This sounds like the thing I need - however I need a more detailed 
> explanation. I don't know what "return to the run loop" means. Can you give a 
> code example?

Um, not really.  Returning to the run loop means returning from your methods 
back to the whatever run loop is running.  For example, the main event loop (if 
the task launch and file handle readInBackgroundAndNotify are issued on the 
main thread).

Your code is launching the task and initiating the background read from the 
file handle, and then returning.  Then, when something happens, a notification 
is posted and your registered observer methods are invoked.  In between those 
events, what is your program doing?  Generally speaking, it's running the run 
loop, which actually means it's waiting for external events or data to arrive 
(or timers to fire).

If you don't understand this, you may need to read up about run loops:
http://developer.apple.com/mac/library/documentation/cocoa/conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html


> -(id) init {
> [... snipped ...]
>       NSThread *bgThread = [[NSThread alloc] initWithTarget:self 
> selector:@selector(myThread:) object:nil];
>       [bgThread start];
> [... snipped ...]
> }
> 
> - (void)myThread:(id)param {
>       [self performSelectorOnMainThread:@selector(startMyTask:) 
> withObject:nil waitUntilDone:YES]; 
> }

The above makes no sense.  You are launching a background thread, but the only 
thing the background thread is doing is passing some work to the foreground 
thread.

You have added nothing but complexity and cost by involving a thread.

There is no need to do anything with threads to get asynchronous launching and 
monitoring of tasks.  The APIs involved (at least, the ones you are using) are 
inherently asynchronous.

I suspect we're still not seeing the real code.  I further expect that your 
problem is a result of confused use of threads.

When you invoke -readInBackgroundAndNotify, there is a run loop source 
installed on the run loop associated with the current thread.  That is, if you 
invoke -readInBackgroundAndNotify on a background thread, the run loop source 
is installed on the background thread's run loop.  If you invoke it on the main 
thread, the source is installed on the main thread's run loop.

This is important because, unless you take specific steps to run a background 
thread's run loop, it isn't run for you.  (The main thread's run loop is run as 
part of a GUI application's main event loop.  If you're writing a command-line 
tool or daemon instead of an application, then you must run it manually if you 
want it to be run.)

If the run loop containing the file handle's background read run loop source is 
not run in a relevant mode, then you won't get the notification about data that 
was read.

Try simplifying your app by eliminating the use of background threads.  Or, if 
you feel you must use background threads for long-running computation, use them 
only for handling the data after receiving it.  Do everything involving 
launching the task and initiating background reads of its output from the main 
thread (without that bizarre bit about launching a background thread just to 
have it shunt some work back to the main thread).

Regards,
Ken

_______________________________________________

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