ChildProcess can handle commands that return large amounts of data.

When a command returned a large amount of data, it was blocking on the stderr  callback when `[fileHandle availableData]` was called. From what I can tell, this is because stderr was being called with a zero-length string.

This was fixed when `[fileHandle availableData]` was moved to run inside the NSTask thread (instead of on the main thread). It now returns a zero-length string rather than blocking forever.

An unresolved question is why stderr is being called with zero-length strings.
This commit is contained in:
Corey Johnson & Nathan Sobo 2012-12-14 13:58:58 -08:00 committed by Corey Johnson
parent ea088db19d
commit 1bd0cc4152
2 changed files with 33 additions and 8 deletions

View File

@ -406,15 +406,12 @@ bool Native::Execute(const CefString& name,
[task setStandardError:stderr];
CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
void (^outputHandle)(NSFileHandle *fileHandle, CefRefPtr<CefV8Value> function) = nil;
void (^outputHandle)(NSString *contents, CefRefPtr<CefV8Value> function) = nil;
void (^taskTerminatedHandle)() = nil;
outputHandle = ^(NSFileHandle *fileHandle, CefRefPtr<CefV8Value> function) {
outputHandle = ^(NSString *contents, CefRefPtr<CefV8Value> function) {
context->Enter();
NSData *data = [fileHandle availableData];
NSString *contents = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
CefV8ValueList args;
args.push_back(CefV8Value::CreateString(std::string([contents UTF8String], [contents lengthOfBytesUsingEncoding:NSUTF8StringEncoding])));
CefRefPtr<CefV8Value> retval = function->ExecuteFunction(function, args);
@ -423,7 +420,6 @@ bool Native::Execute(const CefString& name,
throwException(context->GetGlobal(), function->GetException(), @"Error thrown in OutputHandle");
}
[contents release];
context->Exit();
};
@ -457,18 +453,24 @@ bool Native::Execute(const CefString& name,
CefRefPtr<CefV8Value> stdoutFunction = options->GetValue("stdout");
if (stdoutFunction->IsFunction()) {
stdout.fileHandleForReading.writeabilityHandler = ^(NSFileHandle *fileHandle) {
NSData *data = [fileHandle availableData];
NSString *contents = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
dispatch_sync(dispatch_get_main_queue(), ^() {
outputHandle(fileHandle, stdoutFunction);
outputHandle(contents, stdoutFunction);
});
[contents release];
};
}
CefRefPtr<CefV8Value> stderrFunction = options->GetValue("stderr");
if (stderrFunction->IsFunction()) {
stderr.fileHandleForReading.writeabilityHandler = ^(NSFileHandle *fileHandle) {
NSData *data = [fileHandle availableData];
NSString *contents = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
dispatch_sync(dispatch_get_main_queue(), ^() {
outputHandle(fileHandle, stderrFunction);
outputHandle(contents, stderrFunction);
});
[contents release];
};
}

View File

@ -104,3 +104,26 @@ describe 'Child Processes', ->
ChildProcess.exec(cmd, options).fail (error) ->
expect(error.exitStatus).toBe 2
expect(errorOutput).toBe "bad\n"
describe "when a command returns a large amount of data (over 10k)", ->
originalTimeout = null
beforeEach ->
originalTimeout = jasmine.getEnv().defaultTimeoutInterval
jasmine.getEnv().defaultTimeoutInterval = 1000
afterEach ->
jasmine.getEnv().defaultTimeoutInterval = originalTimeout
it "does not block indefinitally on stdout or stderr callbacks (regression)", ->
output = []
waitsForPromise ->
cmd = "for i in {1..20000}; do echo $RANDOM; done"
options =
stdout: (data) -> output.push(data)
stderr: (data) -> console.log data.length
ChildProcess.exec(cmd, options)
runs ->
expect(output.length).toBeGreaterThan 1