Re: Problem with reading an NSPipe->NSFileHandle to end
Re: Problem with reading an NSPipe->NSFileHandle to end
- Subject: Re: Problem with reading an NSPipe->NSFileHandle to end
- From: Ken Thomases <email@hidden>
- Date: Sun, 11 Apr 2010 18:59:57 -0500
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 (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