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