-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[ package:vm_service ] Automatically invoke VmService.dispose() when …
…the service connection closes Fixes #55559 Change-Id: I213ae3960c15bf2a68b4113a26f333090266b9c9 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/365060 Reviewed-by: Derek Xu <derekx@google.com> Commit-Queue: Ben Konyi <bkonyi@google.com>
- Loading branch information
Showing
7 changed files
with
164 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
// 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. | ||
|
||
import 'dart:convert'; | ||
import 'dart:io'; | ||
|
||
// TODO(bkonyi): Share this logic with _ServiceTesteeRunner.launch. | ||
Future<(Process, Uri)> spawnDartProcess( | ||
String script, { | ||
bool serveObservatory = true, | ||
bool pauseOnStart = true, | ||
bool disableServiceAuthCodes = false, | ||
bool subscribeToStdio = true, | ||
}) async { | ||
final executable = Platform.executable; | ||
final tmpDir = await Directory.systemTemp.createTemp('dart_service'); | ||
final serviceInfoUri = tmpDir.uri.resolve('service_info.json'); | ||
final serviceInfoFile = await File.fromUri(serviceInfoUri).create(); | ||
|
||
final arguments = [ | ||
'--disable-dart-dev', | ||
'--observe=0', | ||
if (!serveObservatory) '--no-serve-observatory', | ||
if (pauseOnStart) '--pause-isolates-on-start', | ||
if (disableServiceAuthCodes) '--disable-service-auth-codes', | ||
'--write-service-info=$serviceInfoUri', | ||
...Platform.executableArguments, | ||
Platform.script.resolve(script).toString(), | ||
]; | ||
final process = await Process.start(executable, arguments); | ||
if (subscribeToStdio) { | ||
process.stdout | ||
.transform(utf8.decoder) | ||
.listen((line) => print('TESTEE OUT: $line')); | ||
process.stderr | ||
.transform(utf8.decoder) | ||
.listen((line) => print('TESTEE ERR: $line')); | ||
} | ||
while ((await serviceInfoFile.length()) <= 5) { | ||
await Future.delayed(const Duration(milliseconds: 50)); | ||
} | ||
final content = await serviceInfoFile.readAsString(); | ||
final infoJson = json.decode(content); | ||
return (process, Uri.parse(infoJson['uri'])); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
// 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. | ||
|
||
import 'dart:io'; | ||
|
||
void main() { | ||
// Block the thread so the isolate can't response to service requests. | ||
sleep(const Duration(hours: 1)); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
// 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. | ||
|
||
// Regression test for https://github.com/dart-lang/sdk/issues/55559. | ||
// | ||
// Ensures that the `VmService` instance calls `dispose()` automatically if the | ||
// VM service connection goes down. Without the `dispose()` call, outstanding | ||
// requests won't complete unless the developer registered a callback for | ||
// `VmService.onDone` that calls `dispose()`. | ||
|
||
import 'dart:async'; | ||
import 'dart:io'; | ||
|
||
import 'package:test/test.dart'; | ||
import 'package:vm_service/vm_service.dart'; | ||
import 'package:vm_service/vm_service_io.dart'; | ||
|
||
import 'common/utils.dart'; | ||
|
||
void main() { | ||
(Process, Uri)? state; | ||
|
||
void killProcess() { | ||
if (state != null) { | ||
final (process, _) = state!; | ||
process.kill(); | ||
state = null; | ||
} | ||
} | ||
|
||
setUp(() async { | ||
state = await spawnDartProcess( | ||
'regress_55559_script.dart', | ||
pauseOnStart: false, | ||
); | ||
}); | ||
|
||
tearDown(() { | ||
killProcess(); | ||
}); | ||
|
||
test( | ||
'Regress 55559: VmService closes outstanding requests on service disconnect', | ||
() async { | ||
final (_, uri) = state!; | ||
final wsUri = uri.replace( | ||
scheme: 'ws', | ||
pathSegments: [ | ||
// The path will have a trailing '/', so the last path segment is the | ||
// empty string and should be removed. | ||
...[...uri.pathSegments]..removeLast(), | ||
'ws', | ||
], | ||
); | ||
final service = await vmServiceConnectUri(wsUri.toString()); | ||
final vm = await service.getVM(); | ||
final isolate = vm.isolates!.first; | ||
final errorCompleter = Completer<RPCError>(); | ||
unawaited( | ||
service.getIsolate(isolate.id!).then( | ||
(_) => fail('Future should throw'), | ||
onError: (e) => errorCompleter.complete(e), | ||
), | ||
); | ||
killProcess(); | ||
|
||
// Wait for the process to exit and the service connection to close. | ||
await service.onDone; | ||
|
||
// The outstanding getIsolate request should be completed with an error. | ||
final error = await errorCompleter.future; | ||
expect(error.code, RPCErrorKind.kServerError.code); | ||
expect(error.message, 'Service connection disposed'); | ||
}, | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters