• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Re: GCD dispatch workers and termination
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: GCD dispatch workers and termination


  • Subject: Re: GCD dispatch workers and termination
  • From: Jason Harris <email@hidden>
  • Date: Mon, 28 Mar 2011 00:08:31 +0200

> 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 (email@hidden)

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:

This email sent to email@hidden

References: 
 >GCD dispatch workers and termination (From: Jason Harris <email@hidden>)
 >Re: GCD dispatch workers and termination (From: Ken Thomases <email@hidden>)

  • Prev by Date: Re: GCD dispatch workers and termination
  • Next by Date: Re: GCD dispatch workers and termination
  • Previous by thread: Re: GCD dispatch workers and termination
  • Next by thread: Re: GCD dispatch workers and termination
  • Index(es):
    • Date
    • Thread