NSTask: why program is blocking when read from NSPipe?

551 Views Asked by At

I use the NSTask to run shell command and output the data via NSPipe. At first, I using bellow method to read output data, it is no any problem.

- (void)outputAvailable:(NSNotification *)aNotification {
  NSString *newOutput;
  NSMutableData *allData = [[NSMutableData alloc] init];
  NSData *taskData = nil;        
  if((taskData = [readHandle availableData]) && [taskData length])
    newOutput = [[NSString alloc] initWithData:allData encoding:NSASCIIStringEncoding];
  NSLog(@"%@", newOutput);
  [readHandle readInBackgroundAndNotify];    
}

The problem about the method is that it only output 4096 bytes data. So I using while loop to get more data, modify the method like this:

- (void)outputAvailable:(NSNotification *)aNotification {
  NSString *newOutput;
  NSMutableData *allData; //Added.
  NSData *taskData = nil;
  while ((taskData = [readHandle availableData]) && [taskData length]) {
    [allData appendData:taskData];
  }    
  newOutput = [[NSString alloc] initWithData:allData encoding:NSASCIIStringEncoding];
  NSLog(@"%@", newOutput);
  [readHandle readInBackgroundAndNotify];
}

Then problem occurs: the program is blocking in the while loop and can not perform the following statements. I ensure that allData is what I wanted, but after appending the last data chunk, it is blocking. Could you give me some solutions? Thanks.

1

There are 1 best solutions below

5
bbum On

Your while() loop effectively blocks further notifications, causing the whole program to block waiting for something to flush the buffer.

You should readInBackgroundAndNotify, then pull off availableBytes on each notification, appending it to your NSMutableData (which is likely held in an instance variable). When you handle the notification, don't attempt to wait for more data or do any kind of a while loop. The system will notify you when more data is available.

I.e. the system pushes data to you, you do not pull data from the system.


Ahh... OK. You should still only pull data when there is data available. Your while() loop is doing that. Not enough coffee. My bad.

The final block is most likely because your external process is not closing the pipe; no EOF is received and, thus, the program is waiting forever for more data that never arrives.

Either:

  • make sure the background task exits

  • detect when you've received enough data and terminate the process

If you are doing some kind of conversion program (say, tr) where you write data on the processes standard input, then you might need to close the standard input pipe.