Re: NSTask oddity with getting stdout
Re: NSTask oddity with getting stdout
- Subject: Re: NSTask oddity with getting stdout
- From: "Stephen J. Butler" <email@hidden>
- Date: Mon, 25 Jul 2011 00:01:19 -0500
On Sun, Jul 24, 2011 at 10:17 PM, Scott Ribe
<email@hidden> 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 (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