Skip to content

Commit c40ebbc

Browse files
bkonyicommit-bot@chromium.org
authored and
commit-bot@chromium.org
committed
[ DartDev ] Ignore SIGINT in DartDev process
If a user program installs custom signal handling for SIGINT, the DartDev instance may exit before its child, potentially causing confusing behavior and 'zombie' children. By ignoring these signals in DartDev, the DartDev instance will only exit once its child process exits. Fixes #42092 Change-Id: I04bf6d1f375b8bb3a4f7022f2c79ddde3bd5f414 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/149643 Commit-Queue: Ben Konyi <bkonyi@google.com> Reviewed-by: Jaime Wren <jwren@google.com>
1 parent bda4994 commit c40ebbc

File tree

5 files changed

+153
-0
lines changed

5 files changed

+153
-0
lines changed

pkg/dartdev/bin/dartdev.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,18 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5+
import 'dart:io';
6+
57
import 'package:dartdev/dartdev.dart';
8+
import 'package:pedantic/pedantic.dart' show unawaited;
69

710
/// The entry point for dartdev.
811
Future<void> main(List<String> args) async {
12+
// Ignore SIGINT to ensure DartDev doesn't exit before any of its
13+
// spawned children. Draining the stream returned by watch() effectively
14+
// sends the signals to the void.
15+
//
16+
// See https://github.com/dart-lang/sdk/issues/42092 for context.
17+
unawaited(ProcessSignal.sigint.watch().drain());
918
await runDartdev(args);
1019
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
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.
4+
5+
import 'dart:async';
6+
import 'dart:io';
7+
8+
Future<void> main() async {
9+
var count = 0;
10+
ProcessSignal.sigint.watch().forEach((s) {
11+
count++;
12+
print('child: Got a SIGINT $count times, hit it 3 times to terminate');
13+
if (count >= 3) {
14+
exit(42);
15+
}
16+
});
17+
print('Waiting...');
18+
while (true) {
19+
await Future.delayed(const Duration(seconds: 5));
20+
}
21+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
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.
4+
5+
import 'dart:async';
6+
import 'dart:convert';
7+
import 'dart:io';
8+
9+
import 'package:expect/expect.dart';
10+
11+
late Process process;
12+
bool lastKill = false;
13+
14+
Future<void> sigint(int iterations) async {
15+
for (int i = 0; i < iterations; ++i) {
16+
if (i + 1 == iterations) {
17+
lastKill = true;
18+
}
19+
process.kill(ProcessSignal.sigint);
20+
// Yield to the event loop to allow for the signal to be sent.
21+
await Future.value(null);
22+
}
23+
}
24+
25+
Future<void> main() async {
26+
process = await Process.start(
27+
Platform.resolvedExecutable,
28+
[
29+
Platform.script.resolve('regress_42092_script.dart').toString(),
30+
],
31+
);
32+
final startCompleter = Completer<void>();
33+
late StreamSubscription sub;
34+
sub = process.stdout.transform(Utf8Decoder()).listen((event) {
35+
if (event.contains('Waiting...')) {
36+
startCompleter.complete();
37+
sub.cancel();
38+
}
39+
});
40+
41+
// Wait for target script to setup its signal handling.
42+
await startCompleter.future;
43+
44+
final exitCompleter = Completer<void>();
45+
process.exitCode.then((code) {
46+
Expect.isTrue(lastKill);
47+
exitCompleter.complete();
48+
});
49+
await sigint(3);
50+
await exitCompleter.future;
51+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
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.
4+
5+
import 'dart:async';
6+
import 'dart:io';
7+
8+
Future<void> main() async {
9+
var count = 0;
10+
ProcessSignal.sigint.watch().forEach((s) {
11+
count++;
12+
print('child: Got a SIGINT $count times, hit it 3 times to terminate');
13+
if (count >= 3) {
14+
exit(42);
15+
}
16+
});
17+
print('Waiting...');
18+
while (true) {
19+
await Future.delayed(const Duration(seconds: 5));
20+
}
21+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
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.
4+
5+
import 'dart:async';
6+
import 'dart:convert';
7+
import 'dart:io';
8+
9+
import 'package:expect/expect.dart';
10+
11+
Process process;
12+
bool lastKill = false;
13+
14+
Future<void> sigint(int iterations) async {
15+
for (int i = 0; i < iterations; ++i) {
16+
if (i + 1 == iterations) {
17+
lastKill = true;
18+
}
19+
process.kill(ProcessSignal.sigint);
20+
// Yield to the event loop to allow for the signal to be sent.
21+
await Future.value(null);
22+
}
23+
}
24+
25+
Future<void> main() async {
26+
process = await Process.start(
27+
Platform.resolvedExecutable,
28+
[
29+
Platform.script.resolve('regress_42092_script.dart').toString(),
30+
],
31+
);
32+
final startCompleter = Completer<void>();
33+
StreamSubscription sub;
34+
sub = process.stdout.transform(Utf8Decoder()).listen((event) {
35+
if (event.contains('Waiting...')) {
36+
startCompleter.complete();
37+
sub.cancel();
38+
}
39+
});
40+
41+
// Wait for target script to setup its signal handling.
42+
await startCompleter.future;
43+
44+
final exitCompleter = Completer<void>();
45+
process.exitCode.then((code) {
46+
Expect.isTrue(lastKill);
47+
exitCompleter.complete();
48+
});
49+
await sigint(3);
50+
await exitCompleter.future;
51+
}

0 commit comments

Comments
 (0)