| Index: runtime/bin/process_patch.dart
|
| diff --git a/runtime/bin/process_patch.dart b/runtime/bin/process_patch.dart
|
| index edc29e294c144f62518e7a32dd1fdb5dc0226fe6..29913b126ae02e81645e3ed34159bc7588b674b4 100644
|
| --- a/runtime/bin/process_patch.dart
|
| +++ b/runtime/bin/process_patch.dart
|
| @@ -25,7 +25,7 @@ patch class Process {
|
| /* patch */ static Future<ProcessResult> run(String executable,
|
| List<String> arguments,
|
| [ProcessOptions options]) {
|
| - return new _NonInteractiveProcess(executable, arguments, options)._result;
|
| + return _runNonInteractiveProcess(executable, arguments, options);
|
| }
|
| }
|
|
|
| @@ -88,13 +88,15 @@ class _ProcessImpl extends NativeFieldWrapperClass1 implements Process {
|
| });
|
| }
|
|
|
| - _in = new _Socket._internalReadOnly(); // stdout coming from process.
|
| - _out = new _Socket._internalWriteOnly(); // stdin going to process.
|
| - _err = new _Socket._internalReadOnly(); // stderr coming from process.
|
| - _exitHandler = new _Socket._internalReadOnly();
|
| + // stdin going to process.
|
| + _stdin = new _Socket._writePipe();
|
| + // stdout coming from process.
|
| + _stdout = new _Socket._readPipe();
|
| + // stderr coming from process.
|
| + _stderr = new _Socket._readPipe();
|
| + _exitHandler = new _Socket._readPipe();
|
| _ended = false;
|
| _started = false;
|
| - _onExit = null;
|
| }
|
|
|
| String _windowsArgumentEscape(String argument) {
|
| @@ -160,16 +162,12 @@ class _ProcessImpl extends NativeFieldWrapperClass1 implements Process {
|
| _arguments,
|
| _workingDirectory,
|
| _environment,
|
| - _in,
|
| - _out,
|
| - _err,
|
| - _exitHandler,
|
| + _stdin._nativeSocket,
|
| + _stdout._nativeSocket,
|
| + _stderr._nativeSocket,
|
| + _exitHandler._nativeSocket,
|
| status);
|
| if (!success) {
|
| - _in.close();
|
| - _out.close();
|
| - _err.close();
|
| - _exitHandler.close();
|
| completer.completeError(
|
| new ProcessException(_path,
|
| _arguments,
|
| @@ -179,23 +177,12 @@ class _ProcessImpl extends NativeFieldWrapperClass1 implements Process {
|
| }
|
| _started = true;
|
|
|
| - _in._closed = false;
|
| - _out._closed = false;
|
| - _err._closed = false;
|
| - _exitHandler._closed = false;
|
| -
|
| - // Make sure to activate socket handlers now that the file
|
| - // descriptors have been set.
|
| - _in._activateHandlers();
|
| - _out._activateHandlers();
|
| - _err._activateHandlers();
|
| -
|
| // Setup an exit handler to handle internal cleanup and possible
|
| // callback when a process terminates.
|
| int exitDataRead = 0;
|
| final int EXIT_DATA_SIZE = 8;
|
| List<int> exitDataBuffer = new List<int>.fixedLength(EXIT_DATA_SIZE);
|
| - _exitHandler.inputStream.onData = () {
|
| + _exitHandler.listen((data) {
|
|
|
| int exitCode(List<int> ints) {
|
| var code = _intFromBytes(ints, 0);
|
| @@ -206,18 +193,17 @@ class _ProcessImpl extends NativeFieldWrapperClass1 implements Process {
|
|
|
| void handleExit() {
|
| _ended = true;
|
| - _exitCode = exitCode(exitDataBuffer);
|
| - if (_onExit != null) _onExit(_exitCode);
|
| - _out.close();
|
| + _exitCode.complete(exitCode(exitDataBuffer));
|
| + // Kill stdin, helping hand if the user forgot to do it.
|
| + _stdin.destroy();
|
| }
|
|
|
| - exitDataRead += _exitHandler.inputStream.readInto(
|
| - exitDataBuffer, exitDataRead, EXIT_DATA_SIZE - exitDataRead);
|
| + exitDataBuffer.setRange(exitDataRead, data.length, data);
|
| + exitDataRead += data.length;
|
| if (exitDataRead == EXIT_DATA_SIZE) {
|
| - _exitHandler.close();
|
| handleExit();
|
| }
|
| - };
|
| + });
|
|
|
| completer.complete(this);
|
| });
|
| @@ -228,24 +214,29 @@ class _ProcessImpl extends NativeFieldWrapperClass1 implements Process {
|
| List<String> arguments,
|
| String workingDirectory,
|
| List<String> environment,
|
| - Socket input,
|
| - Socket output,
|
| - Socket error,
|
| - Socket exitHandler,
|
| + _NativeSocket stdin,
|
| + _NativeSocket stdout,
|
| + _NativeSocket stderr,
|
| + _NativeSocket exitHandler,
|
| _ProcessStartStatus status) native "Process_Start";
|
|
|
| - InputStream get stdout {
|
| - return _in.inputStream;
|
| + Stream<List<int>> get stdout {
|
| + // TODO(ajohnsen): Get stream object only.
|
| + return _stdout;
|
| }
|
|
|
| - InputStream get stderr {
|
| - return _err.inputStream;
|
| + Stream<List<int>> get stderr {
|
| + // TODO(ajohnsen): Get stream object only.
|
| + return _stderr;
|
| }
|
|
|
| - OutputStream get stdin {
|
| - return _out.outputStream;
|
| + IOSink get stdin {
|
| + // TODO(ajohnsen): Get consumer object only.
|
| + return _stdin;
|
| }
|
|
|
| + Future<int> get exitCode => _exitCode.future;
|
| +
|
| bool kill([ProcessSignal signal = ProcessSignal.SIGTERM]) {
|
| if (signal is! ProcessSignal) {
|
| throw new ArgumentError(
|
| @@ -258,24 +249,18 @@ class _ProcessImpl extends NativeFieldWrapperClass1 implements Process {
|
|
|
| bool _kill(Process p, int signal) native "Process_Kill";
|
|
|
| - void set onExit(void callback(int exitCode)) {
|
| - if (_ended) callback(_exitCode);
|
| - _onExit = callback;
|
| - }
|
| -
|
| String _path;
|
| List<String> _arguments;
|
| String _workingDirectory;
|
| List<String> _environment;
|
| - // Private methods of _Socket are used by _in, _out, and _err.
|
| - _Socket _in;
|
| - _Socket _out;
|
| - _Socket _err;
|
| + // Private methods of Socket are used by _in, _out, and _err.
|
| + Socket _stdin;
|
| + Socket _stdout;
|
| + Socket _stderr;
|
| Socket _exitHandler;
|
| - int _exitCode;
|
| bool _ended;
|
| bool _started;
|
| - Function _onExit;
|
| + final Completer<int> _exitCode = new Completer<int>();
|
| }
|
|
|
|
|
| @@ -283,88 +268,59 @@ class _ProcessImpl extends NativeFieldWrapperClass1 implements Process {
|
| // that buffers output so it can be delivered when the process exits.
|
| // _NonInteractiveProcess is used to implement the Process.run
|
| // method.
|
| -class _NonInteractiveProcess {
|
| - _NonInteractiveProcess(String path,
|
| - List<String> arguments,
|
| - ProcessOptions options) {
|
| - _completer = new Completer<ProcessResult>();
|
| - // Extract output encoding options and verify arguments.
|
| - var stdoutEncoding = Encoding.SYSTEM;
|
| - var stderrEncoding = Encoding.SYSTEM;
|
| - if (options != null) {
|
| - if (options.stdoutEncoding != null) {
|
| - stdoutEncoding = options.stdoutEncoding;
|
| - if (stdoutEncoding is !Encoding) {
|
| - throw new ArgumentError(
|
| - 'stdoutEncoding option is not an encoding: $stdoutEncoding');
|
| - }
|
| - }
|
| - if (options.stderrEncoding != null) {
|
| - stderrEncoding = options.stderrEncoding;
|
| - if (stderrEncoding is !Encoding) {
|
| - throw new ArgumentError(
|
| - 'stderrEncoding option is not an encoding: $stderrEncoding');
|
| - }
|
| +Future<ProcessResult> _runNonInteractiveProcess(String path,
|
| + List<String> arguments,
|
| + ProcessOptions options) {
|
| + // Extract output encoding options and verify arguments.
|
| + var stdoutEncoding = Encoding.SYSTEM;
|
| + var stderrEncoding = Encoding.SYSTEM;
|
| + if (options != null) {
|
| + if (options.stdoutEncoding != null) {
|
| + stdoutEncoding = options.stdoutEncoding;
|
| + if (stdoutEncoding is !Encoding) {
|
| + throw new ArgumentError(
|
| + 'stdoutEncoding option is not an encoding: $stdoutEncoding');
|
| }
|
| }
|
| -
|
| - // Start the underlying process.
|
| - var processFuture = new _ProcessImpl(path, arguments, options)._start();
|
| -
|
| - processFuture.then((Process p) {
|
| - // Make sure the process stdin is closed.
|
| - p.stdin.close();
|
| -
|
| - // Setup process exit handling.
|
| - p.onExit = (exitCode) {
|
| - _exitCode = exitCode;
|
| - _checkDone();
|
| - };
|
| -
|
| - // Setup stdout handling.
|
| - _stdoutBuffer = new StringBuffer();
|
| - var stdoutStream = new StringInputStream(p.stdout, stdoutEncoding);
|
| - stdoutStream.onData = () {
|
| - var data = stdoutStream.read();
|
| - if (data != null) _stdoutBuffer.add(data);
|
| - };
|
| - stdoutStream.onClosed = () {
|
| - _stdoutClosed = true;
|
| - _checkDone();
|
| - };
|
| -
|
| - // Setup stderr handling.
|
| - _stderrBuffer = new StringBuffer();
|
| - var stderrStream = new StringInputStream(p.stderr, stderrEncoding);
|
| - stderrStream.onData = () {
|
| - var data = stderrStream.read();
|
| - if (data != null) _stderrBuffer.add(data);
|
| - };
|
| - stderrStream.onClosed = () {
|
| - _stderrClosed = true;
|
| - _checkDone();
|
| - };
|
| - }).catchError((error) {
|
| - _completer.completeError(error.error);
|
| - });
|
| - }
|
| -
|
| - void _checkDone() {
|
| - if (_exitCode != null && _stderrClosed && _stdoutClosed) {
|
| - _completer.complete(new _ProcessResult(_exitCode,
|
| - _stdoutBuffer.toString(),
|
| - _stderrBuffer.toString()));
|
| + if (options.stderrEncoding != null) {
|
| + stderrEncoding = options.stderrEncoding;
|
| + if (stderrEncoding is !Encoding) {
|
| + throw new ArgumentError(
|
| + 'stderrEncoding option is not an encoding: $stderrEncoding');
|
| + }
|
| }
|
| }
|
|
|
| - Future<ProcessResult> get _result => _completer.future;
|
| -
|
| - Completer<ProcessResult> _completer;
|
| - StringBuffer _stdoutBuffer;
|
| - StringBuffer _stderrBuffer;
|
| - int _exitCode;
|
| - bool _stdoutClosed = false;
|
| - bool _stderrClosed = false;
|
| + // Start the underlying process.
|
| + return Process.start(path, arguments, options).then((Process p) {
|
| + // Make sure the process stdin is closed.
|
| + p.stdin.close();
|
| +
|
| + // Setup stdout handling.
|
| + Future<StringBuffer> stdout = p.stdout
|
| + .transform(new StringDecoder(stdoutEncoding))
|
| + .reduce(
|
| + new StringBuffer(),
|
| + (buf, data) {
|
| + buf.add(data);
|
| + return buf;
|
| + });
|
| +
|
| + Future<StringBuffer> stderr = p.stderr
|
| + .transform(new StringDecoder(stderrEncoding))
|
| + .reduce(
|
| + new StringBuffer(),
|
| + (buf, data) {
|
| + buf.add(data);
|
| + return buf;
|
| + });
|
| +
|
| + return Future.wait([p.exitCode, stdout, stderr]).then((result) {
|
| + return new _ProcessResult(result[0],
|
| + result[1].toString(),
|
| + result[2].toString());
|
| + });
|
| + });
|
| }
|
|
|
|
|
|
|