On Sun, Jul 24, 2011 at 10:17 PM, Scott Ribe <scott_r...@elevated-dev.com> wrote: > I'm using NSTask to run a background process, monitor its output, and > ultimately present a message to the user. All has been fine with this 10.2 > through 10.6, as far as I know. Now under 10.7 I saw a case where my code did > not receive quite all the stdout output from the background process. I saw > this once; I haven't tried running it a huge number of times, but it is > definitely not the common case. (And I've never seen it in dev mode running > under the debugger, only on a machine without dev tools. Also BTW, the output > of the bg process is plain ASCII, no worries about multi-byte sequences.) > Does anybody see anything wrong with this code (severely stripped for > legibility):
Interesting. THIS IS JUST ME SPECULATING OUT LOUD. I wonder if they've changed how the readInBackgroundAndNotify works. In particular, my guess here would be that: 1) Two "threads" are created to handle the stdout and stderr (may be dispatch queues, but let's say threads for this thought experiment). 2) Your task generates output in stdout, and most of it gets consumed. 3) Your task generates its last data and closes its stderr then stdout. 4) The stdout thread reads the last data and readies a notification, but doesn't get a chance to post it before being swapped. 5) The stderr thread sees a closed socket and posts a notification before being swapped. 6) The stdout thread wakes up, and posts its notification. Now you are in a situation where there are two notifications being delivered, and their order is wrong for your code. Namely, when the stderr notification arrives first you will do this: a) see that stderr is closed. b) try to read all the remaining data from stdout. There is none, because it has been queued already. c) remove yourself as a listener for stdout. d) try to read all the remaining data from stderr. e) remove yourself as a listener for stderr. The problem is step (c)! There might be data in the notification queue that you will never receive. Here's how I'd write your methods: > - (void) doSomeTask: (NSTask *) task Same. > - (void) processStdOut: (NSNotification *) ntc > { > NSFileHandle *f = [ntc object]; > NSData *d = [[ntc userInfo] objectForKey: > NSFileHandleNotificationDataItem]; > > if( [d length] == 0 ) { [[NSNotificationCenter defaultCenter] removeObserver: self name: NSFileHandleReadCompletionNotification object: curStdOut]; curStdOut = nil; } > else > { > [f readInBackgroundAndNotify]; > [curStdOutStr appendString: > [[[NSString alloc] initWithData: d encoding: > NSASCIIStringEncoding] autorelease]]; > } > } > > > - (void) processStdErr: (NSNotification *) ntc > { > NSFileHandle *f = [ntc object]; > NSData *d = [[ntc userInfo] objectForKey: > NSFileHandleNotificationDataItem]; > > if( [d length] == 0 ) { [[NSNotificationCenter defaultCenter] removeObserver: self name: NSFileHandleReadCompletionNotification object: curStdErr]; curStdErr = nil; } > else > { > [f readInBackgroundAndNotify]; > [curStdErrStr appendString: > [[[NSString alloc] initWithData: d encoding: > NSASCIIStringEncoding] autorelease]]; > } > } That is, abandon processPipeClose and let each pipe handle its own close in the notification when you're sure all of its read notifications have actually been delivered. As you've presented it, processPipeClose isn't needed. _______________________________________________ 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