aboutsummaryrefslogtreecommitdiff
path: root/Foundation/GTMScriptRunner.m
diff options
context:
space:
mode:
authorGravatar gtm.daemon <gtm.daemon@7dc7ac4e-7543-0410-b95c-c1676fc8e2a3>2013-07-11 17:00:19 +0000
committerGravatar gtm.daemon <gtm.daemon@7dc7ac4e-7543-0410-b95c-c1676fc8e2a3>2013-07-11 17:00:19 +0000
commit04a540b219cace3f39c7130daf2014955d79224a (patch)
tree9ce7284db678aa7ef3c089996a97790e39f5b1a3 /Foundation/GTMScriptRunner.m
parent945e1a3f1b580e2e090c61bc7144403370cb0fe3 (diff)
Fix for GTMScriptRunner: run:standardError: deadlocks with full error pipe -
Diffstat (limited to 'Foundation/GTMScriptRunner.m')
-rw-r--r--Foundation/GTMScriptRunner.m86
1 files changed, 62 insertions, 24 deletions
diff --git a/Foundation/GTMScriptRunner.m b/Foundation/GTMScriptRunner.m
index fce6915..85adae7 100644
--- a/Foundation/GTMScriptRunner.m
+++ b/Foundation/GTMScriptRunner.m
@@ -19,6 +19,8 @@
#import "GTMScriptRunner.h"
#import "GTMDefines.h"
+#include <sys/ioctl.h>
+
static BOOL LaunchNSTaskCatchingExceptions(NSTask *task);
@interface GTMScriptRunner (PrivateMethods)
@@ -93,46 +95,82 @@ static BOOL LaunchNSTaskCatchingExceptions(NSTask *task);
NSTask *task = [self interpreterTaskWithAdditionalArgs:nil];
NSFileHandle *toTask = [[task standardInput] fileHandleForWriting];
NSFileHandle *fromTask = [[task standardOutput] fileHandleForReading];
-
+ NSFileHandle *errTask = [[task standardError] fileHandleForReading];
+
if (!LaunchNSTaskCatchingExceptions(task)) {
return nil;
}
[toTask writeData:[cmds dataUsingEncoding:NSUTF8StringEncoding]];
[toTask closeFile];
-
- NSData *outData = [fromTask readDataToEndOfFile];
- NSString *output = [[[NSString alloc] initWithData:outData
- encoding:NSUTF8StringEncoding] autorelease];
-
- // Handle returning standard error if |err| is not nil
- if (err) {
- NSFileHandle *stderror = [[task standardError] fileHandleForReading];
- NSData *errData = [stderror readDataToEndOfFile];
- *err = [[[NSString alloc] initWithData:errData
- encoding:NSUTF8StringEncoding] autorelease];
- if (trimsWhitespace_) {
- *err = [*err stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
- }
- // let folks test for nil instead of @""
- if ([*err length] < 1) {
- *err = nil;
+ // Must keep both file handle buffers empty to avoid the deadlock described in
+ // http://code.google.com/p/google-toolbox-for-mac/issues/detail?id=25
+ NSMutableString *mutableOutString = [NSMutableString string];
+ NSMutableString *mutableErrString = [NSMutableString string];
+ while (true) {
+ // availableByteCountNonBlocking must be called on both fromTask and errTask
+ // each time through the loop.
+ unsigned int bytesFromTask = [self availableByteCountNonBlocking:fromTask];
+ unsigned int bytesErrTask = [self availableByteCountNonBlocking:errTask];
+ if (![task isRunning] && (bytesFromTask == 0) && (bytesErrTask == 0)) {
+ break;
+ }
+ if (bytesFromTask > 0) {
+ NSData *outData = [fromTask availableData];
+ NSString *dataString =
+ [[NSString alloc] initWithData:outData encoding:NSUTF8StringEncoding];
+ [mutableOutString appendString:dataString];
+ [dataString release];
+ }
+ if (bytesErrTask > 0 && err) {
+ NSData *errData = [errTask availableData];
+ NSString *dataString =
+ [[NSString alloc] initWithData:errData encoding:NSUTF8StringEncoding];
+ [mutableErrString appendString:dataString];
+ [dataString release];
}
}
[task terminate];
-
+
+ NSString *outString = mutableOutString;
+ NSString *errString = mutableErrString;
+
if (trimsWhitespace_) {
- output = [output stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
+ NSCharacterSet *set = [NSCharacterSet whitespaceAndNewlineCharacterSet];
+ outString = [outString stringByTrimmingCharactersInSet:set];
+ if (err) {
+ errString = [errString stringByTrimmingCharactersInSet:set];
+ }
}
// let folks test for nil instead of @""
- if ([output length] < 1) {
- output = nil;
+ if ([outString length] < 1) {
+ outString = nil;
}
-
- return output;
+
+ // Handle returning standard error if |err| is not nil
+ if (err) {
+ // let folks test for nil instead of @""
+ if ([errString length] < 1) {
+ *err = nil;
+ } else {
+ *err = errString;
+ }
+ }
+
+ return outString;
+}
+
+- (unsigned int)availableByteCountNonBlocking:(NSFileHandle *)file {
+ int fd = [file fileDescriptor];
+ int numBytes;
+ if (ioctl(fd, FIONREAD, (char *) &numBytes) == -1) {
+ [NSException raise:NSFileHandleOperationException
+ format:@"ioctl() error %d", errno];
+ }
+ return numBytes;
}
- (NSString *)runScript:(NSString *)path {