> Possibly unrelated to your issue with the dispatch threads (although possibly 
> related), the above use of NSTask is somewhat broken.  You've made a common 
> mistake.  It is not OK to block waiting for the task to exit when you haven't 
> established an ongoing asynchronous read of its output (when you're capturing 
> the output rather than letting it go to file, /dev/console, or /dev/null, 
> etc.).
> 
> The problem is that pipes have a fixed buffer in the kernel.  If a task 
> writes more than that amount to the pipe when no reader is draining the pipe, 
> the writer blocks.  You don't read from the pipe until after the task has 
> exited, but the task may be prevented from exiting because you're not reading 
> from the pipe.  Classic deadlock.
> 
> Put another way, have you confirmed that your tasks are really completing?  
> Maybe the dispatch threads are still alive because of this deadlock I'm 
> describing.  (I guess it depends on whether your "ls -l -a -t" command is 
> producing more output than the size of a pipe's buffer, which in turn depends 
> on the current working directory and its contents.)

Thanks again Ken for you suggestion on this bug and on the last one to do with 
NSTask.

Right, I have changed the example a bit so that the NSTask in this case is 
using notifications. I also get it to print the output. Actually now I can see 
this is reproducing the old problem I had of some of the output results being 
just simply dropped.

Eg Here is the new code (header):

@interface ShellTask : NSObject
{
        NSString*               generatingCmd_;         // The command that was 
executed
        NSArray*                generatingArgs_;        // The arguments used 
to the command
        int                             result_;                        // The 
result of executing the command
        NSTask*                 task_;                          // The NSTask 
we are trying to perform
        NSFileHandle*   outHandle_;
        NSFileHandle*   errHandle_;
    NSMutableData*      outputData_;
        NSMutableData*  errorData_;
}

@property (nonatomic, assign) NSTask* task;

+ (void) execute:(NSString*)cmd withArgs:(NSArray*)args;

@end


Here is the new code (body):
-------------------------------


- (void) doWorkerLaunches
{
        for (int i = 1; i <50; i++)
        {
                dispatch_async(dispatch_get_global_queue(0,0), ^{
                        [ShellTask execute:@"/bin/ls" withArgs:[NSArray 
arrayWithObjects: @"-l", @"-a", @"-t", nil]];
                });
        }       
}



@implementation ShellTask

@synthesize task = task_;

- (void) gotOutput:(NSNotification*)notification
{
    NSData* data = [notification.userInfo 
objectForKey:NSFileHandleNotificationDataItem];
    if (notification.object == outHandle_)
        {
        if (data.length > 0)
                {
                        [outputData_ appendData:data];
            [outHandle_ readInBackgroundAndNotify];
        }
                else
            outHandle_ = nil;
    }
}

- (void) gotError:(NSNotification*)notification
{
    if (notification.object == errHandle_)
        {
        NSData* data = [notification.userInfo 
objectForKey:NSFileHandleNotificationDataItem];
        if (data.length > 0)
                {
            [errorData_ appendData:data];
            [errHandle_ readInBackgroundAndNotify];
        }
                else
            errHandle_ = nil;
    }
}

- (BOOL) waitTillFinished
{
        [task_ waitUntilExit];  
        [[NSNotificationCenter defaultCenter] removeObserver:self 
name:NSFileHandleReadCompletionNotification object:nil];
        
        [task_ terminate];
        result_ = [task_ terminationStatus];
        outHandle_ = nil;
        errHandle_ = nil;
        
    return (result_ == 0);
}


- (id) initWithCommand:(NSString*)cmd andArgs:(NSArray*)args
{       
        generatingCmd_ = cmd;
        generatingArgs_ = args;
        task_ = [[NSTask alloc] init];  
        
        NSPipe* outPipe    = [[NSPipe alloc] init];     // Create the pipe to 
write standard out to
        NSPipe* errPipe    = [[NSPipe alloc] init];     // Create the pipe to 
write standard error to
        outHandle_  = [outPipe fileHandleForReading];
        errHandle_  = [errPipe fileHandleForReading];
        outputData_ = [[NSMutableData alloc] init];
        errorData_  = [[NSMutableData alloc] init];
        
        [task_ setLaunchPath:cmd];
        [task_ setArguments:args];
        [task_ setStandardInput:[NSPipe pipe]];
        [task_ setStandardOutput:outPipe];
        [task_ setStandardError:errPipe];
        
        [[NSNotificationCenter defaultCenter] addObserver:self 
selector:@selector(gotOutput:) name:NSFileHandleReadCompletionNotification 
object:nil];
        [[NSNotificationCenter defaultCenter] addObserver:self 
selector:@selector(gotError:)  name:NSFileHandleReadCompletionNotification 
object:nil];

        [outHandle_ readInBackgroundAndNotify];
        [errHandle_ readInBackgroundAndNotify];
        
        return self;
}


+ (void) execute:(NSString*)cmd withArgs:(NSArray*)args
{
        ShellTask* shellTask = [[ShellTask alloc] initWithCommand:cmd 
andArgs:args];
        
        [shellTask->task_ launch];                      // Start the process

        [shellTask waitTillFinished];

        NSString* outStr = [[NSString alloc] 
initWithData:shellTask->outputData_ encoding:NSUTF8StringEncoding];
        NSString* errStr = [[NSString alloc] initWithData:shellTask->errorData_ 
 encoding:NSUTF8StringEncoding];
        fprintf(stderr, "output:\n%s\n", [outStr UTF8String]);
        if ([errStr length] > 0)
                fprintf(stderr, "error:\n%s\n", [errStr UTF8String]);
}

@end

-------------

Interestingly now the output now just looks like:

output:
total 0
drwxr-xr-x  3 jason  staff  102 Mar 27 23:57 gcdGroups.app
drwxr-xr-x  3 jason  staff  102 Mar 27 23:02 .
drwxr-xr-x@ 4 jason  staff  136 Mar 27 23:02 ..

output:
total 0
drwxr-xr-x  3 jason  staff  102 Mar 27 23:57 gcdGroups.app
drwxr-xr-x  3 jason  staff  102 Mar 27 23:02 .
drwxr-xr-x@ 4 jason  staff  136 Mar 27 23:02 ..

output:

output:

output:
total 0
drwxr-xr-x  3 jason  staff  102 Mar 27 23:57 gcdGroups.app
drwxr-xr-x  3 jason  staff  102 Mar 27 23:02 .
drwxr-xr-x@ 4 jason  staff  136 Mar 27 23:02 ..

output:

output:

output:

output:

output:
total 0
drwxr-xr-x  3 jason  staff  102 Mar 27 23:57 gcdGroups.app
drwxr-xr-x  3 jason  staff  102 Mar 27 23:02 .
drwxr-xr-x@ 4 jason  staff  136 Mar 27 23:02 ..

output:

output:
total 0
drwxr-xr-x  3 jason  staff  102 Mar 27 23:57 gcdGroups.app
drwxr-xr-x  3 jason  staff  102 Mar 27 23:02 .
drwxr-xr-x@ 4 jason  staff  136 Mar 27 23:02 ..

So you can see various tasks where just "skipped".  I think this corresponds to 
the cases where maybe the DispatchWorker is not quitting...

Complete ready to run project and code is at: 
http://jasonfharris.com/files/misc_snippets/gcdGroupsNotifications.zip_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to