Fix race condition between spawning and killing isolates during shutdown (#2007)

Co-authored-by: Natalie Weizenbaum <nweiz@google.com>
This commit is contained in:
なつき 2023-06-09 15:55:47 -07:00 committed by GitHub
parent 760fa2ead1
commit aa59a5f4a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 23 additions and 4 deletions

View File

@ -2,6 +2,10 @@
### Embedded Sass
* Fix a race condition where closing standard input while requests are in-flight
could sometimes cause the process to hang rather than shutting down
gracefully.
* Properly include the root stylesheet's URL in the set of loaded URLs when it
fails to parse.

View File

@ -46,7 +46,7 @@ class IsolateDispatcher {
/// The actual isolate objects that have been spawned.
///
/// Only used for cleaning up the process when the underlying channel closes.
final _allIsolates = <Isolate>[];
final _allIsolates = <Future<Isolate>>[];
/// A pool controlling how many isolates (and thus concurrent compilations)
/// may be live at once.
@ -101,10 +101,10 @@ class IsolateDispatcher {
}
}, onError: (Object error, StackTrace stackTrace) {
_handleError(error, stackTrace);
}, onDone: () {
}, onDone: () async {
_closed = true;
for (var isolate in _allIsolates) {
isolate.kill();
(await isolate).kill();
}
// Killing isolates isn't sufficient to make sure the process closes; we
@ -130,7 +130,9 @@ class IsolateDispatcher {
}
var receivePort = ReceivePort();
_allIsolates.add(await Isolate.spawn(_isolateMain, receivePort.sendPort));
var future = Isolate.spawn(_isolateMain, receivePort.sendPort);
_allIsolates.add(future);
await future;
var channel = IsolateChannel<_InitialMessage?>.connectReceive(receivePort)
.transform(const ExplicitCloseTransformer());

View File

@ -224,6 +224,19 @@ void main() {
await process.shouldExit(0);
});
test("closes gracefully with many in-flight compilations", () async {
// This should always be equal to the size of
// [IsolateDispatcher._isolatePool], since that's as many concurrent
// compilations as we can realistically have anyway.
var totalRequests = 15;
for (var i = 1; i <= totalRequests; i++) {
process.inbound
.add((i, compileString("a {b: foo() + 2px}", functions: [r"foo()"])));
}
await process.close();
}, skip: "Enable once dart-lang/stream_channel#92 is released");
test("doesn't include a source map by default", () async {
process.send(compileString("a {b: 1px + 2px}"));
await expectSuccess(process, "a { b: 3px; }", sourceMap: isEmpty);