Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using NSTask and NSPipe causes 100% CPU usage

I'm trying to run a simple bash script using NSTask and direct the output to a text view. Once the task is executed, the CPU usage of my app is 100%, even though it's a simple echo (for now).

I created a completely fresh project to isolate the issue:

@interface AppDelegate ()
@property (nonatomic) NSTask *task;
@property (nonatomic) NSPipe *pipe;
@end

@implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    self.pipe = [NSPipe pipe];
    self.pipe.fileHandleForReading.readabilityHandler = ^(NSFileHandle *h) {
        NSLog(@"Read: %@", [h readDataToEndOfFile]);
    };

    self.task = [[NSTask alloc] init];
    self.task.launchPath = @"/bin/bash";
    self.task.arguments = @[@"-c", @"echo test"];
    self.task.standardOutput = self.pipe;
    [self.task launch];
}
@end

It is correctly executed and the output (as an NSData) is logged with NSLog:

PipeTest[3933:2623] Read: <74657374 0a>

However the CPU usage stays at 100% until I terminate my app.

EDIT:

A Time Profiler test returns the list below, but I'm not sure how to interpret this.

enter image description here

like image 754
DrummerB Avatar asked Dec 06 '12 15:12

DrummerB


2 Answers

File handle left open?

@interface AppDelegate ()
@property (nonatomic) NSTask *task;
@property (nonatomic) NSPipe *pipe;
@end

@implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    self.pipe = [NSPipe pipe];
    self.pipe.fileHandleForReading.readabilityHandler = ^(NSFileHandle *h) {
        NSLog(@"Read: %@", [h readDataToEndOfFile]);
        [h closeFile];
    };

    self.task = [[NSTask alloc] init];
    self.task.launchPath = @"/bin/bash";
    self.task.arguments = @[@"-c", @"echo test"];
    self.task.standardOutput = self.pipe;
    [self.task launch];
}

Closing the file on the NSFileHandle h seems to return your CPU usage to normal.

like image 169
FluffulousChimp Avatar answered Nov 09 '22 00:11

FluffulousChimp


The suggested code would not work if the app writes more than the NSFileHandle's implementation buffer (4K in my observation on El Capitan). [h readDataToEndOfFile] tends to read 4K at a time, so this example may close the buffer prematurely. A more robust and equally undocumented approach for your handler is this one:

NSData *data = [h readDataToEndOfFile];
if (data.length) {
  NSLog(@"Read: %@", data);
} else {
  [h closeFile];
}
like image 2
Boris Vidolov Avatar answered Nov 09 '22 00:11

Boris Vidolov