title: NSTask Race Condition With ReadabilityHandler Block
body: <h1>Basic Setup</h1>
  <p>I use <code>NSTask</code> to run a process that optimizes images. This process writes output data to <code>stdout</code>. I use the <code>readabilityHandler</code> property of <code>NSTask</code> to capture that data. Here is the abbreviated setup:</p>
  <pre><code>NSTask *task = [[NSTask alloc] init];
  [task setArguments:arguments];  // arguments defined above
                     
  NSPipe *errorPipe = [NSPipe pipe];
  [task setStandardError:errorPipe];
  NSFileHandle *errorFileHandle = [errorPipe fileHandleForReading];
                     
  NSPipe *outputPipe = [NSPipe pipe];
  [task setStandardOutput:outputPipe];
  NSFileHandle *outputFileHandle = [outputPipe fileHandleForReading];
                     
  NSMutableData *outputData = [[NSMutableData alloc] init];
  NSMutableData *errorOutputData = [[NSMutableData alloc] init];
                     
  outputFileHandle.readabilityHandler = ^void(NSFileHandle *handle) { 
        NSLog(@&quot;Appending data for %@&quot;, inputPath.lastPathComponent); 
        [outputData appendData:handle.availableData]; 
  };
  
  errorFileHandle.readabilityHandler = ^void(NSFileHandle *handle) { 
         [errorOutputData appendData:handle.availableData]; 
  };
  </code></pre>
  <p>I then call NSTask like this:</p>
  <pre><code>[task setLaunchPath:_pathToJPEGOptim];
  [task launch];
  [task waitUntilExit];
  </code></pre>
  <p>(This is all done on a background dispatch queue). Next I examine the return values of NSTask:</p>
  <pre><code>if ([task terminationStatus] == 0)
  {
      newSize = outputData.length;
                             
      if (newSize &lt;= 0)
      {
          NSString *errorString = [[NSString alloc] initWithData:errorOutputData encoding:NSUTF8StringEncoding];
          NSLog(@&quot;ERROR string: %@&quot;, errorString);
      }
  
      // Truncated for brevity...
  }
  </code></pre>
  <hr />
  <h1>The Problem</h1>
  <p>Approximately 98% of the time, this works perfectly. However, it appears that <code>-waitUntilExit</code> CAN fire before the readabilityHandler block is run. Here is a screenshot showing that the readability handler is running AFTER the task has exited:</p>
  <p><a href="https://i.stack.imgur.com/CEb1Z.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/CEb1Z.png" alt="enter image description here" /></a></p>
  <p>So this is clearly a race condition between the dispatch queue running the readabilityHandler and the dispatch queue where I've fired off my NSTask. My question is: how the hell can I determine that the readabilityHandler is done? How do I beat this race condition if, when NSTask tells me it's done, it may not be done?</p>
  <hr />
  <h1>NOTE:</h1>
  <p>I am aware that NSTask has an optional <code>completionHandler</code> block. But the docs state that this block is not guaranteed to run before <code>-waitUntilExit</code> returns, which implies that it CAN begin running even SOONER than <code>-waitUntilExit</code>. This would make the race condition even more likely.</p>