Reading all data before the NSTask terminates, synchronizing notifications

  • Hello!

    I noticed that under some circumstances  NSTaskDidTerminateNotification
    is sent before all  NSFileHandleReadCompletionNotification are
    consumed. I suppose this is possible in all circumstances, isn't it?
    However, I only experience this in certain cases, and to show it with
    brutality I use sleep() to reproduce It. But in reality, sleep() is
    replaced with a processData-message, that in debug build uses NSLog()
    to print debug-messages and that is how I noticed it.

    To remmedy this synchronization issue, I have to run the run-loop until
    a condition is set. And I'm not very comfortable with run-loops. Is
    there a smarter method to synchronize the notifications? And why do I
    need to do this? I suppose It can occure even if I send
    readInBackgroundAndNotify before I process the data (here represented
    by a sleep()), don't you think so?

    Am I supposed to temporary turn off some kind of signals when using
    data-available notifications from a NSTasks? (Now, this is only
    something I have thought of but maybee it is crazyness.)

    Thanks for any help
    Roger P.

    -(void)commandGetData:(NSNotification *)obj {
    NSData *incommingData = [[obj userInfo]
    objectForKey:NSFileHandleNotificationDataItem];
    if([incommingData length])
    {
      // uncommented [[obj object] readInBackgroundAndNotify]; // Is this
    correct?
      fprintf(stderr, "Received %u bytes: Sleeping for 1 seconds...",
    [incommingData length]);
      sleep(1);
      fprintf(stderr, "done!\n");
      [dataBuffer appendData:incommingData];
      [[obj object] readInBackgroundAndNotify]; //??? This is wrong
    }
    else
    {
      fprintf(stderr, "Completed!\n");
      readCompleted = YES;
    }
    }

    -(void)commandTerminated:(NSNotification *)obj {
    // Synchronize notifications
    int i = 1;
    while(readCompleted == NO)
    {
      fprintf(stderr, "Synchronizing#%3d> ", i++);
      [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
    beforeDate:[NSDate distantFuture]];
    }

    fprintf(stderr, "%s Shell terminated %p, %u bytes received\n",
    __func__, [obj object], [dataBuffer length]);

        // ...
    }

    This is the output (readInBackgroundAndNotify AFTER sleep()):

    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Synchronizing#  1> Received 510 bytes: Sleeping for 1 seconds...done!
    Synchronizing#  2> Received 510 bytes: Sleeping for 1 seconds...done!
    Synchronizing#  3> Received 510 bytes: Sleeping for 1 seconds...done!
    Synchronizing#  4> Received 510 bytes: Sleeping for 1 seconds...done!
    Synchronizing#  5> Received 510 bytes: Sleeping for 1 seconds...done!
    Synchronizing#  6> Received 510 bytes: Sleeping for 1 seconds...done!
    Synchronizing#  7> Received 510 bytes: Sleeping for 1 seconds...done!
    Synchronizing#  8> Received 510 bytes: Sleeping for 1 seconds...done!
    Synchronizing#  9> Received 510 bytes: Sleeping for 1 seconds...done!
    Synchronizing# 10> Received 510 bytes: Sleeping for 1 seconds...done!
    Synchronizing# 11> Received 510 bytes: Sleeping for 1 seconds...done!
    Synchronizing# 12> Received 510 bytes: Sleeping for 1 seconds...done!
    Synchronizing# 13> Received 510 bytes: Sleeping for 1 seconds...done!
    Synchronizing# 14> Received 510 bytes: Sleeping for 1 seconds...done!
    Synchronizing# 15> Received 510 bytes: Sleeping for 1 seconds...done!
    Synchronizing# 16> Received 423 bytes: Sleeping for 1 seconds...done!
    Synchronizing# 17> Completed!
    -[MnWindow commandTerminated:] Shell terminated 0x32d040, 24903 bytes
    received

    If I do it the other way (readInBackgroundAndNotify BEFORE sleep()):

    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 510 bytes: Sleeping for 1 seconds...done!
    Received 423 bytes: Sleeping for 1 seconds...done!
    Completed!
    -[MnWindow commandTerminated:] Shell terminated 0x3f0ad0, 24903 bytes
    received
  • Here is how I did it just recently. The taskStates enum defines one
    bit for each condition (my original code has a stdErr bit as well).
    The taskDone method then does final processing not until all
    conditions are set. No need for an embedded run loop. Also it makes
    no difference exactly where readInBackgroundAndNotify is sent in your
    read notification method.

    @interface MyTask : NSObject
    {
        NSTask * task ;
        unsigned int taskStatus;
        NSMutableData * buffer;
    }
    @end

    @implementation MyTask

    enum {
        taskReadDone  = 1<<0,
        taskTerminated = 1<<1,
        taskAllDone    = (taskReadDone | taskTerminated)
    } taskStates;

    -(void)readData:(NSNotification *)noty
    {
        NSData *data = [[noty userInfo]
            objectForKey:NSFileHandleNotificationDataItem];
        if ([data length])
        {
            [buffer appendData:data];
            [[noty object] readInBackgroundAndNotify];
        }
        else
            [self taskDone:taskReadDone];
    }

    -(void)taskTerminated:(NSNotification *)noty
    {
        [self taskDone:taskTerminated];
    }

    -(void)taskDone:(unsigned int)whichBit
    {
        taskStatus |= whichBit;
        if (taskStatus == taskAllDone)
        {
            int returnCode = [[self task] terminationStatus];
            // clean up task, remove observers etc.
            // process data
        }
    }

    // rest of implementation...

    On Oct 2, 2005, at 00:57, Frode wrote:

    > Hello!
    >
    > I noticed that under some circumstances
    > NSTaskDidTerminateNotification
    > is sent before all  NSFileHandleReadCompletionNotification are
    > consumed. I suppose this is possible in all circumstances, isn't it?
    > However, I only experience this in certain cases, and to show it with
    > brutality I use sleep() to reproduce It. But in reality, sleep() is
    > replaced with a processData-message, that in debug build uses NSLog()
    > to print debug-messages and that is how I noticed it.
    >
    > To remmedy this synchronization issue, I have to run the run-loop
    > until
    > a condition is set. And I'm not very comfortable with run-loops. Is
    > there a smarter method to synchronize the notifications? And why do I
    > need to do this? I suppose It can occure even if I send
    > readInBackgroundAndNotify before I process the data (here represented
    > by a sleep()), don't you think so?
    >
    > Am I supposed to temporary turn off some kind of signals when using
    > data-available notifications from a NSTasks? (Now, this is only
    > something I have thought of but maybee it is crazyness.)
    >
    > Thanks for any help
    > Roger P.
    >
    > -(void)commandGetData:(NSNotification *)obj {
    > NSData *incommingData = [[obj userInfo]
    > objectForKey:NSFileHandleNotificationDataItem];
    > if([incommingData length])
    > {
    > // uncommented [[obj object] readInBackgroundAndNotify]; //
    > Is this
    > correct?
    > fprintf(stderr, "Received %u bytes: Sleeping for 1
    > seconds...",
    > [incommingData length]);
    > sleep(1);
    > fprintf(stderr, "done!\n");
    > [dataBuffer appendData:incommingData];
    > [[obj object] readInBackgroundAndNotify]; //??? This is wrong
    > }
    > else
    > {
    > fprintf(stderr, "Completed!\n");
    > readCompleted = YES;
    > }
    > }
    >
    > -(void)commandTerminated:(NSNotification *)obj {
    > // Synchronize notifications
    > int    i = 1;
    > while(readCompleted == NO)
    > {
    > fprintf(stderr, "Synchronizing#%3d> ", i++);
    > [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
    > beforeDate:[NSDate distantFuture]];
    > }
    >
    > fprintf(stderr, "%s Shell terminated %p, %u bytes received\n",
    > __func__, [obj object], [dataBuffer length]);
    >
    > // ...
    > }
    >
previous month october 2005 next month
MTWTFSS
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31            
Go to today
MindNode
MindNode offered a free license !