Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSFileHandleDataAvailableNotification files repeatedly with no new data (resulting in very high CPU usage)

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];
}
like image 928
Marco Aurélio Avatar asked Apr 08 '13 19:04

Marco Aurélio


1 Answers

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];
    }
}
like image 89
Marco Aurélio Avatar answered Nov 14 '22 15:11

Marco Aurélio