mirror of
https://github.com/atom/atom.git
synced 2026-01-25 06:48:28 -05:00
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:
committed by
Corey Johnson
parent
ea088db19d
commit
1bd0cc4152
@@ -406,15 +406,12 @@ bool Native::Execute(const CefString& name,
|
|||||||
[task setStandardError:stderr];
|
[task setStandardError:stderr];
|
||||||
|
|
||||||
CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
|
CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
|
||||||
void (^outputHandle)(NSFileHandle *fileHandle, CefRefPtr<CefV8Value> function) = nil;
|
void (^outputHandle)(NSString *contents, CefRefPtr<CefV8Value> function) = nil;
|
||||||
void (^taskTerminatedHandle)() = nil;
|
void (^taskTerminatedHandle)() = nil;
|
||||||
|
|
||||||
outputHandle = ^(NSFileHandle *fileHandle, CefRefPtr<CefV8Value> function) {
|
outputHandle = ^(NSString *contents, CefRefPtr<CefV8Value> function) {
|
||||||
context->Enter();
|
context->Enter();
|
||||||
|
|
||||||
NSData *data = [fileHandle availableData];
|
|
||||||
NSString *contents = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
|
||||||
|
|
||||||
CefV8ValueList args;
|
CefV8ValueList args;
|
||||||
args.push_back(CefV8Value::CreateString(std::string([contents UTF8String], [contents lengthOfBytesUsingEncoding:NSUTF8StringEncoding])));
|
args.push_back(CefV8Value::CreateString(std::string([contents UTF8String], [contents lengthOfBytesUsingEncoding:NSUTF8StringEncoding])));
|
||||||
CefRefPtr<CefV8Value> retval = function->ExecuteFunction(function, args);
|
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");
|
throwException(context->GetGlobal(), function->GetException(), @"Error thrown in OutputHandle");
|
||||||
}
|
}
|
||||||
|
|
||||||
[contents release];
|
|
||||||
context->Exit();
|
context->Exit();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -457,18 +453,24 @@ bool Native::Execute(const CefString& name,
|
|||||||
CefRefPtr<CefV8Value> stdoutFunction = options->GetValue("stdout");
|
CefRefPtr<CefV8Value> stdoutFunction = options->GetValue("stdout");
|
||||||
if (stdoutFunction->IsFunction()) {
|
if (stdoutFunction->IsFunction()) {
|
||||||
stdout.fileHandleForReading.writeabilityHandler = ^(NSFileHandle *fileHandle) {
|
stdout.fileHandleForReading.writeabilityHandler = ^(NSFileHandle *fileHandle) {
|
||||||
|
NSData *data = [fileHandle availableData];
|
||||||
|
NSString *contents = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
||||||
dispatch_sync(dispatch_get_main_queue(), ^() {
|
dispatch_sync(dispatch_get_main_queue(), ^() {
|
||||||
outputHandle(fileHandle, stdoutFunction);
|
outputHandle(contents, stdoutFunction);
|
||||||
});
|
});
|
||||||
|
[contents release];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
CefRefPtr<CefV8Value> stderrFunction = options->GetValue("stderr");
|
CefRefPtr<CefV8Value> stderrFunction = options->GetValue("stderr");
|
||||||
if (stderrFunction->IsFunction()) {
|
if (stderrFunction->IsFunction()) {
|
||||||
stderr.fileHandleForReading.writeabilityHandler = ^(NSFileHandle *fileHandle) {
|
stderr.fileHandleForReading.writeabilityHandler = ^(NSFileHandle *fileHandle) {
|
||||||
|
NSData *data = [fileHandle availableData];
|
||||||
|
NSString *contents = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
||||||
dispatch_sync(dispatch_get_main_queue(), ^() {
|
dispatch_sync(dispatch_get_main_queue(), ^() {
|
||||||
outputHandle(fileHandle, stderrFunction);
|
outputHandle(contents, stderrFunction);
|
||||||
});
|
});
|
||||||
|
[contents release];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -104,3 +104,26 @@ describe 'Child Processes', ->
|
|||||||
ChildProcess.exec(cmd, options).fail (error) ->
|
ChildProcess.exec(cmd, options).fail (error) ->
|
||||||
expect(error.exitStatus).toBe 2
|
expect(error.exitStatus).toBe 2
|
||||||
expect(errorOutput).toBe "bad\n"
|
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
|
||||||
Reference in New Issue
Block a user