Using NSTask and NSPipe causes 100% CPU usage
Asked Answered
H

2

7

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

Hepta answered 6/12, 2012 at 15:44 Comment(0)
H
11

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.

Hyperthermia answered 6/12, 2012 at 17:9 Comment(1)
This is great, thanks! Can you also explain why the CPU reacts like that to left open file handles?Galactic
I
2

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];
}
Inhibition answered 4/11, 2015 at 2:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.