0

My Cocoa App has multi threads (up to 8 threads) running a python script using NSTask which takes about 1200 seconds. Almost in every execution of the 8 threads running NSTask, the app just waits almost forever and never returns. I paused the execution and noticed that the app is stuck in waitUntilExit. My code snippet is as below. Please help to let me know how I can solve this issue.

  dispatch_semaphore_t MySemaphore;

...

MySemaphore = dispatch_semaphore_create(8);

...

//Code portion where NSTask is used to execute a python script.
...
NSTask* task = [NSTask new];

    [task setLaunchPath:launchPath];
    [task setArguments:[NSArray arrayWithObjects:param1,
                                param2, param3,
                                param4, param5,
                                param6, param7, nil]];
    NSData *outData;
    NSData *errorData;
    int retVal;


         dispatch_semaphore_wait(MySemaphore, DISPATCH_TIME_FOREVER);

        retVal = [self _runTask:task
                             inData:nil
                            outData:&outData
                          errorData:&errorData];
        dispatch_semaphore_signal(MySemaphore);

    NSMutableString *retStr = [[[NSMutableString alloc] initWithData:outData
                                                                 encoding:NSUTF8StringEncoding] autorelease];
    NSMutableString *retStrErr = [[[NSMutableString alloc] initWithData:errorData
                                                                       encoding:NSUTF8StringEncoding] autorelease];

        if([retStrErr length] != 0)
        {
            [retStr appendString:@"\nstandard Error output:\n"];
            [retStr appendString:retStrErr];
        }

        if( retVal == 0 )
        {
            [retStr appendString:@"\nOK\n"];

        }
        else
        {
            [retStr appendString:@"\nERROR\n"];
        }

        NSLog(@"Response of _DRAMRetention for UUT:%d, %@", nUut+1, retStr);

        [task release];

...

- (int) _runTask:(NSTask*)task inData:(NSData*)inData outData:(NSData**)outData errorData:(NSData**)errorData
{
    NSPipe*             inPipe = nil;
    NSPipe*             outPipe = nil;
    NSPipe*             errorPipe = nil;
    NSFileHandle*       fileHandle;

    /* Reset output variables */
    if(outData)
    {
        *outData = nil;
    }

    if(errorData)
    {
        *errorData = nil;
    }
    /* Safe check */
    if(task == nil)
    {
        return -1;
    }
    /* Create standard input pipe */
    if([inData length])
    {  
        if((inPipe = [NSPipe new]))
        {
            [task setStandardInput:inPipe];
            [inPipe release];
        }
        else
        {
            task = nil;
            goto Exit;
        }
    }

    /* Create standard output pipe */
    if(outData)
    {
        if((outPipe = [NSPipe new]))
        {
            [task setStandardOutput:outPipe];
            [outPipe release];
        }
        else
        {
            task = nil;
            goto Exit;
        }
    }

    /* Create standard error pipe */
    if(errorData)
    {
        if((errorPipe = [NSPipe new]))
        {
            [task setStandardError:errorPipe];
            [errorPipe release];
        }
        else
        {
            task = nil;
            goto Exit;
        }
    }

    /* Launch task */
    NS_DURING
    [task launch];
    NS_HANDLER
    task = nil;
    NS_ENDHANDLER
    if(task == nil)
    {
        goto Exit;
    }
    /* Write data to standard input pipe */
    if((fileHandle = [inPipe fileHandleForWriting]))
    {
        NS_DURING
        [fileHandle writeData:inData];
        [fileHandle closeFile];
        NS_HANDLER
        [task terminate];
        [task interrupt];
        task = nil;
        NS_ENDHANDLER
    }
    if(task == nil)
    {
        goto Exit;
    }
    /* Wait for task to complete and read data from standard output and standard error pipes in background */
    if((fileHandle = [outPipe fileHandleForReading]))
    {
        *outData = [NSMutableData data];
        [[NSNotificationCenter defaultCenter] addObserver:*outData selector:@selector(_CommandLineToolFileHandleDataAvailable:) name:NSFileHandleDataAvailableNotification object:fileHandle];
        [fileHandle waitForDataInBackgroundAndNotify];
    }
    if((fileHandle = [errorPipe fileHandleForReading]))
    {
        *errorData = [NSMutableData data];
        [[NSNotificationCenter defaultCenter] addObserver:*errorData selector:@selector(_CommandLineToolFileHandleDataAvailable:) name:NSFileHandleDataAvailableNotification object:fileHandle];
        [fileHandle waitForDataInBackgroundAndNotify];
    }

    //My app is stuck here
    [task waitUntilExit];

    if((fileHandle = [errorPipe fileHandleForReading]))
    {
        [[NSNotificationCenter defaultCenter] removeObserver:*errorData name:NSFileHandleDataAvailableNotification object:fileHandle];
        [(NSMutableData*)*errorData appendData:[fileHandle readDataToEndOfFile]];
    }
    if((fileHandle = [outPipe fileHandleForReading]))
    {
        [[NSNotificationCenter defaultCenter] removeObserver:*outData name:NSFileHandleDataAvailableNotification object:fileHandle];
        [(NSMutableData*)*outData appendData:[fileHandle readDataToEndOfFile]];
    }

Exit:
    [[inPipe fileHandleForReading] closeFile];
    [[inPipe fileHandleForWriting] closeFile];
    [[outPipe fileHandleForReading] closeFile];
    [[outPipe fileHandleForWriting] closeFile];
    [[errorPipe fileHandleForReading] closeFile];
    [[errorPipe fileHandleForWriting] closeFile];

    return (task ? [task terminationStatus] : -1);
}

@end

@implementation NSMutableData (CommandLineTool)

/* Extend the NSMutableData class to add a method called by   NSFileHandleDataAvailableNotification to automatically append the new data */
- (void) _CommandLineToolFileHandleDataAvailable:(NSNotification*)notification
{
    NSFileHandle*           fileHandle = [notification object];

    [self appendData:[fileHandle availableData]];

    [fileHandle waitForDataInBackgroundAndNotify];
}
albertleng
  • 61
  • 7

1 Answers1

0

First, you don't need to repeatedly call [fileHandle waitForDataInBackgroundAndNotify];. Probably not the source of your problem...

Secondly, in cases like these, it is almost always because of buffering issues. But you've addressed that. The second most common cause is that the task isn't terminating.

Are you sure the python scripts are actually terminating?

bbum
  • 162,346
  • 23
  • 271
  • 359
  • Yes I am sure the Python scripts are terminating. What else do you suggest me to do to solve this "waiting forever" at nstask waituntilexit issue? – albertleng May 31 '15 at 01:13
  • @user2370312 Beyond making sure all the data is read from the output pipes, I'm not sure. I'm trying to think of what else would block termination and coming up blank. – bbum May 31 '15 at 16:01