Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(276)

Side by Side Diff: runtime/bin/process_impl.dart

Issue 11091070: Change Process.start to return a future that completes with a (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Restructure to get rid of _onStart and _onError Created 8 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « runtime/bin/process.dart ('k') | runtime/bin/process_win.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 _exit(int status) native "Exit"; 5 _exit(int status) native "Exit";
6 6
7 class _ProcessStartStatus { 7 class _ProcessStartStatus {
8 int _errorCode; // Set to OS error code if process start failed. 8 int _errorCode; // Set to OS error code if process start failed.
9 String _errorMessage; // Set to OS error message if process start failed. 9 String _errorMessage; // Set to OS error message if process start failed.
10 } 10 }
11 11
12 12
13 class _Process extends NativeFieldWrapperClass1 implements Process { 13 class _Process extends NativeFieldWrapperClass1 implements Process {
14 static Future<ProcessResult> run(String path, 14 static Future<ProcessResult> run(String path,
15 List<String> arguments, 15 List<String> arguments,
16 [ProcessOptions options]) { 16 [ProcessOptions options]) {
17 return new _NonInteractiveProcess._start(path, arguments, options)._result; 17 return new _NonInteractiveProcess(path, arguments, options)._result;
18 } 18 }
19 19
20 _Process.start(String path, 20 static Future<Process> start(String path,
21 List<String> arguments, 21 List<String> arguments,
22 ProcessOptions options) { 22 ProcessOptions options) {
23 _Process process = new _Process(path, arguments, options);
24 return process._start();
25 }
26
27 _Process(String path, List<String> arguments, ProcessOptions options) {
23 if (path is !String) { 28 if (path is !String) {
24 throw new ArgumentError("Path is not a String: $path"); 29 throw new ArgumentError("Path is not a String: $path");
25 } 30 }
26 _path = path; 31 _path = path;
27 32
28 if (arguments is !List) { 33 if (arguments is !List) {
29 throw new ArgumentError("Arguments is not a List: $arguments"); 34 throw new ArgumentError("Arguments is not a List: $arguments");
30 } 35 }
31 int len = arguments.length; 36 int len = arguments.length;
32 _arguments = new List<String>(len); 37 _arguments = new List<String>(len);
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
65 } 70 }
66 71
67 _in = new _Socket._internalReadOnly(); // stdout coming from process. 72 _in = new _Socket._internalReadOnly(); // stdout coming from process.
68 _out = new _Socket._internalWriteOnly(); // stdin going to process. 73 _out = new _Socket._internalWriteOnly(); // stdin going to process.
69 _err = new _Socket._internalReadOnly(); // stderr coming from process. 74 _err = new _Socket._internalReadOnly(); // stderr coming from process.
70 _exitHandler = new _Socket._internalReadOnly(); 75 _exitHandler = new _Socket._internalReadOnly();
71 _closed = false; 76 _closed = false;
72 _ended = false; 77 _ended = false;
73 _started = false; 78 _started = false;
74 _onExit = null; 79 _onExit = null;
75 // TODO(ager): Make the actual process starting really async instead of
76 // simulating it with a timer.
77 new Timer(0, (Timer ignore) => _start());
78 } 80 }
79 81
80 String _windowsArgumentEscape(String argument) { 82 String _windowsArgumentEscape(String argument) {
81 var result = argument; 83 var result = argument;
82 if (argument.contains('\t') || argument.contains(' ')) { 84 if (argument.contains('\t') || argument.contains(' ')) {
83 // Produce something that the C runtime on Windows will parse 85 // Produce something that the C runtime on Windows will parse
84 // back as this string. 86 // back as this string.
85 87
86 // Replace any number of '\' followed by '"' with 88 // Replace any number of '\' followed by '"' with
87 // twice as many '\' followed by '\"'. 89 // twice as many '\' followed by '\"'.
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
123 return result; 125 return result;
124 } 126 }
125 127
126 int _intFromBytes(List<int> bytes, int offset) { 128 int _intFromBytes(List<int> bytes, int offset) {
127 return (bytes[offset] + 129 return (bytes[offset] +
128 (bytes[offset + 1] << 8) + 130 (bytes[offset + 1] << 8) +
129 (bytes[offset + 2] << 16) + 131 (bytes[offset + 2] << 16) +
130 (bytes[offset + 3] << 24)); 132 (bytes[offset + 3] << 24));
131 } 133 }
132 134
133 void _start() { 135 Future<Process> _start() {
134 var status = new _ProcessStartStatus(); 136 var completer = new Completer();
135 bool success = _startNative(_path, 137 // TODO(ager): Make the actual process starting really async instead of
136 _arguments, 138 // simulating it with a timer.
137 _workingDirectory, 139 new Timer(0, (_) {
138 _environment, 140 var status = new _ProcessStartStatus();
139 _in, 141 bool success = _startNative(_path,
140 _out, 142 _arguments,
141 _err, 143 _workingDirectory,
142 _exitHandler, 144 _environment,
143 status); 145 _in,
144 if (!success) { 146 _out,
145 close(); 147 _err,
146 _reportError(new ProcessException(status._errorMessage, 148 _exitHandler,
147 status._errorCode)); 149 status);
148 return; 150 if (!success) {
149 } 151 close();
150 _started = true; 152 completer.completeException(
153 new ProcessException(status._errorMessage, status._errorCode));
154 return;
155 }
156 _started = true;
151 157
152 _in._closed = false; 158 _in._closed = false;
153 _out._closed = false; 159 _out._closed = false;
154 _err._closed = false; 160 _err._closed = false;
155 _exitHandler._closed = false; 161 _exitHandler._closed = false;
156 162
157 // Make sure to activate socket handlers now that the file 163 // Make sure to activate socket handlers now that the file
158 // descriptors have been set. 164 // descriptors have been set.
159 _in._activateHandlers(); 165 _in._activateHandlers();
160 _out._activateHandlers(); 166 _out._activateHandlers();
161 _err._activateHandlers(); 167 _err._activateHandlers();
162 168
163 // Setup an exit handler to handle internal cleanup and possible 169 // Setup an exit handler to handle internal cleanup and possible
164 // callback when a process terminates. 170 // callback when a process terminates.
165 int exitDataRead = 0; 171 int exitDataRead = 0;
166 final int EXIT_DATA_SIZE = 8; 172 final int EXIT_DATA_SIZE = 8;
167 List<int> exitDataBuffer = new List<int>(EXIT_DATA_SIZE); 173 List<int> exitDataBuffer = new List<int>(EXIT_DATA_SIZE);
168 _exitHandler.inputStream.onData = () { 174 _exitHandler.inputStream.onData = () {
169 175
170 int exitCode(List<int> ints) { 176 int exitCode(List<int> ints) {
171 var code = _intFromBytes(ints, 0); 177 var code = _intFromBytes(ints, 0);
172 var negative = _intFromBytes(ints, 4); 178 var negative = _intFromBytes(ints, 4);
173 assert(negative == 0 || negative == 1); 179 assert(negative == 0 || negative == 1);
174 return (negative == 0) ? code : -code; 180 return (negative == 0) ? code : -code;
175 } 181 }
176 182
177 void handleExit() { 183 void handleExit() {
178 _ended = true; 184 _ended = true;
179 if (_onExit !== null) { 185 if (_onExit !== null) {
180 _onExit(exitCode(exitDataBuffer)); 186 _onExit(exitCode(exitDataBuffer));
187 }
181 } 188 }
182 }
183 189
184 exitDataRead += _exitHandler.inputStream.readInto( 190 exitDataRead += _exitHandler.inputStream.readInto(
185 exitDataBuffer, exitDataRead, EXIT_DATA_SIZE - exitDataRead); 191 exitDataBuffer, exitDataRead, EXIT_DATA_SIZE - exitDataRead);
186 if (exitDataRead == EXIT_DATA_SIZE) handleExit(); 192 if (exitDataRead == EXIT_DATA_SIZE) handleExit();
187 }; 193 };
188 194
189 if (_onStart !== null) { 195 completer.complete(this);
190 _onStart(); 196 });
191 } 197 return completer.future;
192 } 198 }
193 199
194 bool _startNative(String path, 200 bool _startNative(String path,
195 List<String> arguments, 201 List<String> arguments,
196 String workingDirectory, 202 String workingDirectory,
197 List<String> environment, 203 List<String> environment,
198 Socket input, 204 Socket input,
199 Socket output, 205 Socket output,
200 Socket error, 206 Socket error,
201 Socket exitHandler, 207 Socket exitHandler,
(...skipping 18 matching lines...) Expand all
220 throw new ProcessException("Process closed"); 226 throw new ProcessException("Process closed");
221 } 227 }
222 return _out.outputStream; 228 return _out.outputStream;
223 } 229 }
224 230
225 void kill([ProcessSignal signal = ProcessSignal.SIGTERM]) { 231 void kill([ProcessSignal signal = ProcessSignal.SIGTERM]) {
226 if (signal is! ProcessSignal) { 232 if (signal is! ProcessSignal) {
227 throw new ArgumentError( 233 throw new ArgumentError(
228 "Argument 'signal' must be a ProcessSignal"); 234 "Argument 'signal' must be a ProcessSignal");
229 } 235 }
230 if (!_started) { 236 assert(_started);
231 var e = new ProcessException("Cannot kill process that is not started"); 237 if (_ended) return;
232 _reportError(e); 238 if (_kill(this, signal._signalNumber)) return;
233 return; 239 throw new ProcessException("Could not kill process");
234 }
235 if (_ended) {
236 return;
237 }
238 if (_kill(this, signal._signalNumber)) {
239 return;
240 }
241 _reportError(new ProcessException("Could not kill process"));
242 return;
243 } 240 }
244 241
245 bool _kill(Process p, int signal) native "Process_Kill"; 242 bool _kill(Process p, int signal) native "Process_Kill";
246 243
247 void close() { 244 void close() {
248 if (_closed) { 245 if (_closed) {
249 throw new ProcessException("Process closed"); 246 throw new ProcessException("Process closed");
250 } 247 }
251 _in.close(); 248 _in.close();
252 _out.close(); 249 _out.close();
253 _err.close(); 250 _err.close();
254 _exitHandler.close(); 251 _exitHandler.close();
255 _closed = true; 252 _closed = true;
256 } 253 }
257 254
258 void set onExit(void callback(int exitCode)) { 255 void set onExit(void callback(int exitCode)) {
259 if (_closed) { 256 if (_closed) {
260 throw new ProcessException("Process closed"); 257 throw new ProcessException("Process closed");
261 } 258 }
262 if (_ended) { 259 if (_ended) {
263 throw new ProcessException("Process killed"); 260 throw new ProcessException("Process killed");
264 } 261 }
265 _onExit = callback; 262 _onExit = callback;
266 } 263 }
267 264
268 void set onError(void callback(e)) {
269 _onError = callback;
270 }
271
272 void set onStart(void callback()) {
273 _onStart = callback;
274 }
275
276 void _reportError(e) {
277 if (_onError != null) {
278 _onError(e);
279 } else {
280 throw e;
281 }
282 }
283
284 String _path; 265 String _path;
285 List<String> _arguments; 266 List<String> _arguments;
286 String _workingDirectory; 267 String _workingDirectory;
287 List<String> _environment; 268 List<String> _environment;
288 // Private methods of _Socket are used by _in, _out, and _err. 269 // Private methods of _Socket are used by _in, _out, and _err.
289 _Socket _in; 270 _Socket _in;
290 _Socket _out; 271 _Socket _out;
291 _Socket _err; 272 _Socket _err;
292 Socket _exitHandler; 273 Socket _exitHandler;
293 bool _closed; 274 bool _closed;
294 bool _ended; 275 bool _ended;
295 bool _started; 276 bool _started;
296 Function _onExit; 277 Function _onExit;
297 Function _onError;
298 Function _onStart;
299 } 278 }
300 279
301 280
302 // _NonInteractiveProcess is a wrapper around an interactive process 281 // _NonInteractiveProcess is a wrapper around an interactive process
303 // that buffers output so it can be delivered when the process exits. 282 // that buffers output so it can be delivered when the process exits.
304 // _NonInteractiveProcess is used to implement the Process.run 283 // _NonInteractiveProcess is used to implement the Process.run
305 // method. 284 // method.
306 class _NonInteractiveProcess { 285 class _NonInteractiveProcess {
307 _NonInteractiveProcess._start(String path, 286 _NonInteractiveProcess(String path,
308 List<String> arguments, 287 List<String> arguments,
309 ProcessOptions options) { 288 ProcessOptions options) {
310 _completer = new Completer<ProcessResult>(); 289 _completer = new Completer<ProcessResult>();
311 // Extract output encoding options and verify arguments. 290 // Extract output encoding options and verify arguments.
312 var stdoutEncoding = Encoding.UTF_8; 291 var stdoutEncoding = Encoding.UTF_8;
313 var stderrEncoding = Encoding.UTF_8; 292 var stderrEncoding = Encoding.UTF_8;
314 if (options !== null) { 293 if (options !== null) {
315 if (options.stdoutEncoding !== null) { 294 if (options.stdoutEncoding !== null) {
316 stdoutEncoding = options.stdoutEncoding; 295 stdoutEncoding = options.stdoutEncoding;
317 if (stdoutEncoding is !Encoding) { 296 if (stdoutEncoding is !Encoding) {
318 throw new ArgumentError( 297 throw new ArgumentError(
319 'stdoutEncoding option is not an encoding: $stdoutEncoding'); 298 'stdoutEncoding option is not an encoding: $stdoutEncoding');
320 } 299 }
321 } 300 }
322 if (options.stderrEncoding !== null) { 301 if (options.stderrEncoding !== null) {
323 stderrEncoding = options.stderrEncoding; 302 stderrEncoding = options.stderrEncoding;
324 if (stderrEncoding is !Encoding) { 303 if (stderrEncoding is !Encoding) {
325 throw new ArgumentError( 304 throw new ArgumentError(
326 'stderrEncoding option is not an encoding: $stderrEncoding'); 305 'stderrEncoding option is not an encoding: $stderrEncoding');
327 } 306 }
328 } 307 }
329 } 308 }
330 309
331 // Start the underlying process. 310 // Start the underlying process.
332 _process = new _Process.start(path, arguments, options); 311 var processFuture = new _Process(path, arguments, options)._start();
333 312
334 // Make sure stdin is closed. 313 processFuture.then((Process p) {
335 _process.onStart = _process.stdin.close; 314 // Make sure the process stdin is closed.
315 p.stdin.close;
336 316
337 // Setup process error handling. 317 // Setup process exit handling.
338 _process.onError = (e) => _completer.completeException(e); 318 p.onExit = (exitCode) {
319 _exitCode = exitCode;
320 _checkDone();
321 };
339 322
340 // Setup process exit handling. 323 // Setup stdout handling.
341 _process.onExit = (exitCode) { 324 _stdoutBuffer = new StringBuffer();
342 _exitCode = exitCode; 325 var stdoutStream = new StringInputStream(p.stdout, stdoutEncoding);
343 _checkDone(); 326 stdoutStream.onData = () {
344 }; 327 var data = stdoutStream.read();
328 if (data != null) _stdoutBuffer.add(data);
329 };
330 stdoutStream.onClosed = () {
331 _stdoutClosed = true;
332 _checkDone();
333 };
345 334
346 // Setup stdout handling. 335 // Setup stderr handling.
347 _stdoutBuffer = new StringBuffer(); 336 _stderrBuffer = new StringBuffer();
348 var stdoutStream = new StringInputStream(_process.stdout, stdoutEncoding); 337 var stderrStream = new StringInputStream(p.stderr, stderrEncoding);
349 stdoutStream.onData = () { 338 stderrStream.onData = () {
350 var data = stdoutStream.read(); 339 var data = stderrStream.read();
351 if (data != null) _stdoutBuffer.add(data); 340 if (data != null) _stderrBuffer.add(data);
352 }; 341 };
353 stdoutStream.onClosed = () { 342 stderrStream.onClosed = () {
354 _stdoutClosed = true; 343 _stderrClosed = true;
355 _checkDone(); 344 _checkDone();
356 }; 345 };
346 });
357 347
358 // Setup stderr handling. 348 processFuture.handleException((error) {
359 _stderrBuffer = new StringBuffer(); 349 _completer.completeException(error);
360 var stderrStream = new StringInputStream(_process.stderr, stderrEncoding); 350 return true;
361 stderrStream.onData = () { 351 });
362 var data = stderrStream.read();
363 if (data != null) _stderrBuffer.add(data);
364 };
365 stderrStream.onClosed = () {
366 _stderrClosed = true;
367 _checkDone();
368 };
369 } 352 }
370 353
371 void _checkDone() { 354 void _checkDone() {
372 if (_exitCode != null && _stderrClosed && _stdoutClosed) { 355 if (_exitCode != null && _stderrClosed && _stdoutClosed) {
373 _completer.complete(new _ProcessResult(_exitCode, 356 _completer.complete(new _ProcessResult(_exitCode,
374 _stdoutBuffer.toString(), 357 _stdoutBuffer.toString(),
375 _stderrBuffer.toString())); 358 _stderrBuffer.toString()));
376 } 359 }
377 } 360 }
378 361
379 Future<ProcessResult> get _result => _completer.future; 362 Future<ProcessResult> get _result => _completer.future;
380 363
381 Completer<ProcessResult> _completer; 364 Completer<ProcessResult> _completer;
382 Process _process;
383 StringBuffer _stdoutBuffer; 365 StringBuffer _stdoutBuffer;
384 StringBuffer _stderrBuffer; 366 StringBuffer _stderrBuffer;
385 int _exitCode; 367 int _exitCode;
386 bool _stdoutClosed = false; 368 bool _stdoutClosed = false;
387 bool _stderrClosed = false; 369 bool _stderrClosed = false;
388 } 370 }
389 371
390 372
391 class _ProcessResult implements ProcessResult { 373 class _ProcessResult implements ProcessResult {
392 const _ProcessResult(int this.exitCode, 374 const _ProcessResult(int this.exitCode,
393 String this.stdout, 375 String this.stdout,
394 String this.stderr); 376 String this.stderr);
395 377
396 final int exitCode; 378 final int exitCode;
397 final String stdout; 379 final String stdout;
398 final String stderr; 380 final String stderr;
399 } 381 }
OLDNEW
« no previous file with comments | « runtime/bin/process.dart ('k') | runtime/bin/process_win.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698