On Mar 19, 2011, at 1:27 PM, Ken Thomases wrote:

> On Mar 18, 2011, at 5:32 PM, Jason Harris wrote:
> 
>> On Mar 18, 2011, at 11:07 PM, Ken Thomases wrote:
>> 
>>> The exception is saying that the file descriptor backing the 
>>> NSPipe/NSFileHandle has been closed.  Retrying after some time can only 
>>> result in the frameworks co-opting a new, unrelated file object that 
>>> happened to get the same file descriptor.  That would explain why your 
>>> reads sometimes never get any data -- you're reading from a completely 
>>> different file object.
>> 
>> I am sorry, but can you explain the paragraph in more detail?
> 
> Well, are you familiar with file descriptors?  When you open a file, pipe, 
> socket, or whatever, you get a file descriptor by which to refer to it.  A 
> file descriptor is just a number, like 5.  When you use Cocoa's NSPipe or 
> NSFileHandle, these details are hidden behind the scenes.
> 
> Anyway, something may be closing the file descriptor.  When that happens, 
> _for a while_ any code which tries to use that file descriptor (in this case, 
> 5) with the system calls for reading from the file will get an error, EBADF, 
> indicating that the file descriptor doesn't refer to an open file.  However, 
> and this is very important, it is very likely that future requests to open 
> files, pipes, sockets, etc. will reuse the file descriptor.  That is, some 
> other part of the code will open a file and receive the file descriptor 5 for 
> its open file.  Once that happens, the original code which had been use file 
> descriptor 5 can't tell that this new file descriptor 5 is not the one it 
> used to own.  It will keep trying to use it, and that may seem to succeed, 
> but it's accessing a completely different file than it should be.  It may be 
> trying to read from a file which will never receive any data, which would 
> explain why you don't receive the output from your task.

I was sort of aware of this but its very nice to have it explained in the 
context of my problem. Thank you!


>> (Just to be clear sometimes I never get the exception and yet it still drops 
>> data... So the ugly bit of the @catch is never hit but it still was dropping 
>> the data...)
> 
> Note that there's no guarantee that your attempt to read from the file 
> descriptor will happen during the window when the file descriptor is invalid. 
>  The file descriptor may be closed and then nearly-immediately reused for a 
> different file, and your first attempt to read from it will access a 
> different, unrelated file.  That would be bad and wrong, but would not cause 
> the exception resulting from an EBADF error.
> 
> 
>>> I'm not sure why the file descriptor is being closed.  It may be a 
>>> framework bug having to do with garbage collection.
>>> 
>>> I'd try creating the file descriptors manually with the pipe(2) system 
>>> call.  Then, construct NSFileHandles from them with 
>>> -initWithFileDescriptor:theReadFD closeOnDealloc:NO.
>> 
>> Sorry just so I don't stuff this up can I ask you to give me the exact code 
>> here... or point me to an example of this?
> 
> Well, this is sketched in mail.  By the way, I reviewed the docs and the 
> closeOnDealloc:NO part is not necessary since that's the default:
> 
>       int out_pipe_fds[2];
>       // Creates a pipe consisting of two file descriptors.  The [0] element 
> gets the read end of the
>       // pipe; the [1] element gets the write end.
>       if (pipe(out_pipe_fds) != 0)
>               /* handle error */;
>       NSFileHandle* outReadHandle = [[NSFileHandle alloc] 
> initWithFileDescriptor:out_pipe_fds[0]];
>       NSFileHandle* outWriteHandle = [[NSFileHandle alloc] 
> initWithFileDescriptor:out_pipe_fds[1]];
>       [task setStandardOutput:outWriteHandle];
> 
>       // Initiate reading on the read handle, including observing the 
> appropriate notification
> 
>       // Repeat for stderr
> 
>       [task launch];
>       [outWriteHandle release];
>       close(out_pipe_fds[1]);
>       // Repeat for stderr
> 
>       // ...
> 
>       // After you've received the end-of-file indication:
>       [outReadHandle release];
>       close(out_pipe_fds[0]);

Thanks!

> 
>>> I see no evidence that it has "vanished".  It seems to me that 
>>> -waitTillFinished is still executing, presumably blocked in the run loop.  
>>> Note that, not only is the DebugLog at the end of -waitTillFinished not 
>>> hit, but neither is the one immediately after the call to it in 
>>> +execute:withArgs:onTask:.  In other words, it just hasn't returned.
>> 
>> Ahh so you are saying that the loop is still executing? Well if I put a 
>> break point in the loop in waitTillFinished then it doesn't break on this 
>> breakpoint...
> 
> The run loop is still executing in the sense of flow of execution has not 
> returned back to your code.  You are still blocked in the -runMode:untilDate: 
> call.  So, your breakpoint won't be hit because your breakpoint is outside of 
> that method in the caller, but that's never reached.
> 
> Keep in mind that -runMode:untilDate: won't necessarily return for 
> _everything_ that happens within it.  In particular, see this note in the 
> documentation:
> 
>> Note: A timer is not considered an input source and may fire multiple times 
>> while waiting for this method to return
> 
> I believe that 'a timer' includes those -performSelector... methods with a 
> delay, like you had been using in -setPendingTermination.  So, your selector 
> might be performed but that would not necessarily give -waitTillFinished the 
> opportunity to notice it.

ok that very likely explains that as well...

> 
>> Maybe this is an XCode bug or something? Also I would imagine though if this 
>> were the case then stepping up through the call stack would have been 
>> normal. Ie would have got back to the loop. Instead it was like I hit 
>> continue when I reached __CFRunLoopRun...
> 
> Xcode does have its share of bugs with stepping through code.  However, I 
> suspect this was just behaving normally.  You had suggested that this whole 
> task execution process was happening on a background thread.  So, I suspect 
> that background thread was blocked in the run loop and was not reaching your 
> breakpoint (nor the implicit breakpoint involved in stepping over code).  
> However, it appeared that you had just hit continue because the other threads 
> of your program were running just fine.  The debugger would have stopped the 
> next time a real run loop source were to fire, but that never happened 
> because there were no active sources scheduled.

Ok again that very nicely likely explains what is going on.

I am going back to have a play with the code. I have a much better idea now 
what is likely going wrong, but previously I tried a gizzillion different 
combinations and none of them worked. In any case given the extremely simple 
use of [task_ waitUntilExit] (once the pipes and notifications are set up) is 
now working it will likely be the approach I go with.

Thank you again for taking the time to give this detailed response!
Cheers,
  Jason_______________________________________________

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