Skip to content

Commit 29c350d

Browse files
DanTupCommit Queue
authored andcommitted
[dds/dap] Ensure DAP always sends resume on PausePostRequest
The change at https://dart-review.googlesource.com/c/sdk/+/410760 to fix a race incorrectly assumed that once we had handled startup for a thread, we would never need to send an automatic resume again. However this was not the case - after a hot reload, we need to resume the thread even though we had technically already handled startup. This is essentially a partial revert of 84e6ed0, with a test to verify we trigger readyToResume on PausePostRequest. Change-Id: I1e171043f04f38dd6f52e1692d9257d34e5248be Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/416580 Commit-Queue: Ben Konyi <[email protected]> Reviewed-by: Ben Konyi <[email protected]> Reviewed-by: Derek Xu <[email protected]>
1 parent 5c1b908 commit 29c350d

File tree

3 files changed

+69
-2
lines changed

3 files changed

+69
-2
lines changed

pkg/dds/lib/src/dap/isolate_manager.dart

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -645,7 +645,12 @@ class IsolateManager {
645645
// after a hot restart.
646646
if (eventKind == vm.EventKind.kPausePostRequest) {
647647
await _configureIsolate(thread);
648-
await handleThreadStartup(thread, sendStoppedOnEntry: false);
648+
649+
// We always want to resume here regardless of whether startupHandled was
650+
// already `true` (because that might be from before the reload).
651+
// Therefore set the flag and resume always.
652+
thread.startupHandled = true;
653+
await readyToResumeThread(thread.threadId);
649654
} else if (eventKind == vm.EventKind.kPauseStart) {
650655
handleThreadStartup(thread, sendStoppedOnEntry: true);
651656
} else {

pkg/dds/test/dap/isolate_manager_test.dart

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,5 +144,27 @@ main() {
144144
expect(receivedUri, 'scheme://file/path.dart');
145145
expect(resolved, Uri.file('/returned/file/path.dart'));
146146
});
147+
148+
test('sends resume after kPausePostRequest', () async {
149+
// Trigger initial startup.
150+
await adapter.isolateManager.handleEvent(Event(
151+
isolate: adapter.mockService.isolate1,
152+
kind: EventKind.kIsolateRunnable));
153+
isolateManager.threads[0].startupHandled = true;
154+
155+
// Clear any previously called methods.
156+
adapter.mockService.calledMethods.clear();
157+
158+
// Now simulate a hot reload causing the isolate to pause again.
159+
await adapter.isolateManager.handleEvent(Event(
160+
isolate: adapter.mockService.isolate1,
161+
kind: EventKind.kPausePostRequest));
162+
163+
// Ensure we called readyToResume.
164+
expect(adapter.mockService.calledMethods.map((cm) => cm.method),
165+
contains('readyToResume'));
166+
167+
// Expect it was resumed.
168+
});
147169
});
148170
}

pkg/dds/test/dap/mocks.dart

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import 'dart:async';
66

77
import 'package:dap/dap.dart' as dap;
8-
import 'package:dds/dap.dart';
8+
import 'package:dds/dap.dart' hide Response;
99
import 'package:dds/dds.dart';
1010
import 'package:dds/dds_launcher.dart';
1111
import 'package:dds/src/dap/adapters/dart_cli_adapter.dart';
@@ -168,6 +168,10 @@ class MockVmService implements VmService {
168168
/// tests.
169169
final List<String> requests = [];
170170

171+
/// A list of methods that were called on the Mock VM Service.
172+
List<({String method, String? isolateId, Map<String, dynamic>? args})>
173+
calledMethods = [];
174+
171175
@override
172176
Future<Success> setLibraryDebuggable(
173177
String isolateId,
@@ -213,6 +217,30 @@ class MockVmService implements VmService {
213217
);
214218
}
215219

220+
@override
221+
Future<Response> callMethod(
222+
String method, {
223+
String? isolateId,
224+
Map<String, dynamic>? args,
225+
}) async {
226+
calledMethods.add((method: method, isolateId: isolateId, args: args));
227+
228+
// Some DDS methods are implemented as extensions so we can't override
229+
// them in the mock, we need to override callMethod and handle them here
230+
// (and provide their responses) instead.
231+
232+
if (method == 'getDartDevelopmentServiceVersion') {
233+
return Version(major: 2, minor: 0);
234+
} else if (method == 'readyToResume') {
235+
isolateId ??= args != null ? args['isolateId'] : null;
236+
return {isolate1.id, isolate2.id}.contains(isolateId)
237+
? Success()
238+
: throw SentinelException.parse(method, {});
239+
}
240+
241+
throw 'MockVmService does not handle $method ($isolateId, $args)';
242+
}
243+
216244
@override
217245
Future<Success> resume(
218246
String isolateId, {
@@ -224,6 +252,18 @@ class MockVmService implements VmService {
224252
? Success()
225253
: throw SentinelException.parse('resume', {});
226254
}
255+
256+
@override
257+
Future<Success> setIsolatePauseMode(
258+
String isolateId, {
259+
/*ExceptionPauseMode*/ String? exceptionPauseMode,
260+
bool? shouldPauseOnExit,
261+
}) async {
262+
// Do nothing, just pretend.
263+
return {isolate1.id, isolate2.id}.contains(isolateId)
264+
? Success()
265+
: throw SentinelException.parse('setIsolatePauseMode', {});
266+
}
227267
}
228268

229269
class MockDartDevelopmentServiceLauncher

0 commit comments

Comments
 (0)