0

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.

Gate Luma
  • 195
  • 1
  • 2
  • 10

1 Answers1

0

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.

bbum
  • 162,346
  • 23
  • 271
  • 359
  • I really do not understand, sorry. Could you provide any codes to demonstrate it? Thank you. – Gate Luma Sep 16 '13 at 15:57
  • You say that `while` loop blokcs further notifications. But the `while` loop receives all the data chunk by chunk, that is to say, the data is completely received by `while` loop. The blocking only occurs after the last chunk data (<4096bytes) receiving. – Gate Luma Sep 16 '13 at 16:06
  • @bbbum, Thanks for your help. Now I know why. Unfortunately, I can not exit the background task because my app want to interact with the shell (e.g. launch a sh, and input command via pipe). As for the second way, I do not konw when and how to detect the data volume. I find an [example](http://dev.notoptimal.net/2007/04/nstasks-nspipes-and-deadlocks-when.html) which talked about the solution for the problem, but it is not valid to me. I need your further help. Thanks again. – Gate Luma Sep 16 '13 at 16:57
  • @GateLuma What format is the data being read? Typically, within any given protocol/format, there'll be some way of knowing when all the data has been received. Barring that, you'll have to have some kind of signaling mechanism which would require modifying the code of whatever external tool you are running. – bbum Sep 16 '13 at 18:07
  • The data is the output of shell command performed, such as "ls" or "system_profiler". Are there any simple solutions for this problem? – Gate Luma Sep 16 '13 at 23:35