8

I'm attempting to read data from the Standard Error of a NSTask in Cocoa using waitForDataInBackgroundAndNotify. The following code does read the stream, so it's already working partially.

The problem I have is that sometimes the NSFileHandleDataAvailableNotification starts firing repeatedly (thousands of times per second) with no new data at all ([data length] returns 0). My process then starts using a lot of CPU, slowing the machine to a halt. Has any of you guys hit something like this before in the past? Thanks in advance.

/**
 * Start reading from STDERR
 *
 * @private
 */

- (void)startReadingStandardError {
    NSFileHandle *fileHandle = [_task.standardError fileHandleForReading];
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(errorData:)
                                                 name:NSFileHandleDataAvailableNotification
                                               object:fileHandle];
    [fileHandle waitForDataInBackgroundAndNotify];
}

/**
 * Fired whenever new data becomes available on STDERR
 *
 * @private
 */

-(void) errorData: (NSNotification *) notification
{
    NSFileHandle *fileHandle = (NSFileHandle*) [notification object];
    NSData *data = [fileHandle availableData];

    if ([data length]) {
       // consume data
    }

   [fileHandle waitForDataInBackgroundAndNotify];
}
Marco Aurélio
  • 2,463
  • 1
  • 21
  • 21

1 Answers1

10

So, ended up figuring it out by myself. According to the NSFileHandle Class Reference, if the NSData object returned by availableData has a length of 0, it means that the end-of-file has been reached. I was not handling this case correctly. This fixed it for me:

/**
 * Fired whenever new data becomes available on STDERR
 *
 * @private
 */

-(void) errorData: (NSNotification *) notification
{
    NSFileHandle *fileHandle = (NSFileHandle*) [notification object];
    NSData *data = [fileHandle availableData];

    if ([data length]) {
        // consume data
        // ...

        [fileHandle waitForDataInBackgroundAndNotify];
    } else {
        // EOF was hit, remove observer
        [[NSNotificationCenter defaultCenter] removeObserver:self name:NSFileHandleDataAvailableNotification object:fileHandle];
    }
}
Marco Aurélio
  • 2,463
  • 1
  • 21
  • 21