[io] Fix a bug where Process.stdin.add exceptions could not be caught

Change-Id: I2383a74bfa6950ab8f8934087fb68218f06dd681
Bug:https://github.com/dart-lang/sdk/issues/48501
Tested: Unit test
CoreLibraryReviewExempt: dart:io only
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/351380
Reviewed-by: Alexander Aprelev <aam@google.com>
Commit-Queue: Brian Quinlan <bquinlan@google.com>
This commit is contained in:
Brian Quinlan 2024-02-13 18:26:49 +00:00 committed by Commit Queue
parent 6ca01d0854
commit 9967075787
3 changed files with 82 additions and 0 deletions

View file

@ -282,6 +282,11 @@ base class _ProcessImpl extends _ProcessImplNativeWrapper implements _Process {
if (_modeHasStdio(_mode)) {
// stdin going to process.
_stdin = new _StdSink(new _Socket._writePipe().._owner = this);
// Ignore errors if the `Process.stdin.done` future is not consumed.
// Developers catch errors writing to `Process.stdin` by consuming
// `Process.stdin.done` or calling `Process.stdin.flush()`.
_stdin!.done.ignore();
// stdout coming from process.
_stdout = new _StdStream(new _Socket._readPipe().._owner = this);
// stderr coming from process.

View file

@ -492,6 +492,29 @@ abstract interface class Process {
Stream<List<int>> get stderr;
/// The standard input stream of the process as an [IOSink].
///
/// `stdin` is implemented as a pipe between the parent process and the
/// spawned subprocess.
///
/// Data added to the [IOSink] (E.g. `Process.stdin.writeln('Hello!')`) is
/// written to to the pipe asynchronously.
///
/// Errors writing the data (e.g. due to the subprocess exiting) can be
/// caught by awaiting `Process.stdin.flush()`. For example:
///
/// ```dart
/// import 'dart:io';
///
/// main() async {
/// final process = await Process.start('false', const <String>[]);
/// process.stdin.writeln('Hello World\n'); // May already have exited.
/// try {
/// await process.stdin.flush();
/// } catch (e) {
/// print(e);
/// }
/// }
/// ```
IOSink get stdin;
/// The process id of the process.

View file

@ -0,0 +1,54 @@
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
//
// Verify that failing to write to `Process.stdin` results in an exception
// being thrown by `process.stdin.flush()` and `process.stdin.done`.
//
// See https://github.com/dart-lang/sdk/issues/48501
//
// VMOptions=
// VMOptions=--short_socket_read
// VMOptions=--short_socket_write
// VMOptions=--short_socket_read --short_socket_write
import "package:expect/expect.dart";
import 'dart:async';
import 'dart:io';
import 'dart:math';
import "process_test_util.dart";
Future test(Process process) async {}
void main() async {
if (!Platform.isLinux && !Platform.isMacOS) {
print('test not supported on ${Platform.operatingSystem}');
return;
}
final process = await Process.start('false', const <String>[]);
try {
for (var i = 0; i < 20; ++i) {
// Ensure that the pipe is broken while we are writing.
process.stdin.add([1, 2, 3]);
await Future.delayed(const Duration(milliseconds: 50));
}
try {
await process.stdin.flush();
Expect.fail('await process.stdin.flush(): expected exception');
} on SocketException catch (e) {
Expect.equals(32, e.osError!.errorCode); // Broken pipe
}
try {
await process.stdin.done;
Expect.fail('await process.stdin.done: expected exception');
} on SocketException catch (e) {
Expect.equals(32, e.osError!.errorCode); // Broken pipe
}
} finally {
process.kill();
}
}