@@ -13,6 +13,7 @@ import 'package:path/path.dart' as path;
13
13
import 'package:vm_service/vm_service.dart' as vm;
14
14
15
15
import '../../../dds.dart' ;
16
+ import '../../rpc_error_codes.dart' ;
16
17
import '../base_debug_adapter.dart' ;
17
18
import '../exceptions.dart' ;
18
19
import '../isolate_manager.dart' ;
@@ -49,6 +50,9 @@ const maxToStringsPerEvaluation = 10;
49
50
/// will work.
50
51
const threadExceptionExpression = r'$_threadException' ;
51
52
53
+ /// Typedef for handlers of VM Service stream events.
54
+ typedef _StreamEventHandler <T > = FutureOr <void > Function (T data);
55
+
52
56
/// Pattern for extracting useful error messages from an evaluation exception.
53
57
final _evalErrorMessagePattern = RegExp ('Error: (.*)' );
54
58
@@ -354,6 +358,17 @@ abstract class DartDebugAdapter<TL extends LaunchRequestArguments,
354
358
355
359
late final sendLogsToClient = args.sendLogsToClient ?? false ;
356
360
361
+ /// Whether or not the DAP is terminating.
362
+ ///
363
+ /// When set to `true` , some requests that return "Service Disappeared" errors
364
+ /// will be caught and dropped as these are expected if the process is
365
+ /// terminating.
366
+ ///
367
+ /// This flag may be set by incoming requests from the client
368
+ /// (terminateRequest/disconnectRequest) or when a process terminates, or the
369
+ /// VM Service disconnects.
370
+ bool isTerminating = false ;
371
+
357
372
DartDebugAdapter (
358
373
ByteStreamServerChannel channel, {
359
374
this .ipv6 = false ,
@@ -531,16 +546,20 @@ abstract class DartDebugAdapter<TL extends LaunchRequestArguments,
531
546
this .vmService = vmService;
532
547
533
548
unawaited (vmService.onDone.then ((_) => _handleVmServiceClosed ()));
549
+
550
+ // Handlers must be wrapped to handle Service Disappeared errors if async
551
+ // code tries to call the VM Service after termination begins.
552
+ final wrap = _wrapHandlerWithErrorHandling;
534
553
_subscriptions.addAll ([
535
- vmService.onIsolateEvent.listen (handleIsolateEvent),
536
- vmService.onDebugEvent.listen (handleDebugEvent),
537
- vmService.onLoggingEvent.listen (handleLoggingEvent),
538
- vmService.onExtensionEvent.listen (handleExtensionEvent),
539
- vmService.onServiceEvent.listen (handleServiceEvent),
554
+ vmService.onIsolateEvent.listen (wrap ( handleIsolateEvent) ),
555
+ vmService.onDebugEvent.listen (wrap ( handleDebugEvent) ),
556
+ vmService.onLoggingEvent.listen (wrap ( handleLoggingEvent) ),
557
+ vmService.onExtensionEvent.listen (wrap ( handleExtensionEvent) ),
558
+ vmService.onServiceEvent.listen (wrap ( handleServiceEvent) ),
540
559
if (_subscribeToOutputStreams)
541
- vmService.onStdoutEvent.listen (_handleStdoutEvent),
560
+ vmService.onStdoutEvent.listen (wrap ( _handleStdoutEvent) ),
542
561
if (_subscribeToOutputStreams)
543
- vmService.onStderrEvent.listen (_handleStderrEvent),
562
+ vmService.onStderrEvent.listen (wrap ( _handleStderrEvent) ),
544
563
]);
545
564
await Future .wait ([
546
565
vmService.streamListen (vm.EventStreams .kIsolate),
@@ -558,8 +577,20 @@ abstract class DartDebugAdapter<TL extends LaunchRequestArguments,
558
577
// Let the subclass do any existing setup once we have a connection.
559
578
await debuggerConnected (vmInfo);
560
579
561
- // Process any existing isolates that may have been created before the
562
- // streams above were set up.
580
+ await _withErrorHandling (
581
+ () => _configureExistingIsolates (vmService, vmInfo, resumeIfStarting),
582
+ );
583
+
584
+ _debuggerInitializedCompleter.complete ();
585
+ }
586
+
587
+ /// Process any existing isolates that may have been created before the
588
+ /// streams above were set up.
589
+ Future <void > _configureExistingIsolates (
590
+ vm.VmService vmService,
591
+ vm.VM vmInfo,
592
+ bool resumeIfStarting,
593
+ ) async {
563
594
final existingIsolateRefs = vmInfo.isolates;
564
595
final existingIsolates = existingIsolateRefs != null
565
596
? await Future .wait (existingIsolateRefs
@@ -596,8 +627,6 @@ abstract class DartDebugAdapter<TL extends LaunchRequestArguments,
596
627
}
597
628
}
598
629
}));
599
-
600
- _debuggerInitializedCompleter.complete ();
601
630
}
602
631
603
632
/// Handles the clients "continue" ("resume") request for the thread in
@@ -695,6 +724,8 @@ abstract class DartDebugAdapter<TL extends LaunchRequestArguments,
695
724
DisconnectArguments ? args,
696
725
void Function () sendResponse,
697
726
) async {
727
+ isTerminating = true ;
728
+
698
729
await disconnectImpl ();
699
730
await shutdown ();
700
731
sendResponse ();
@@ -839,6 +870,7 @@ abstract class DartDebugAdapter<TL extends LaunchRequestArguments,
839
870
return ;
840
871
}
841
872
873
+ isTerminating = true ;
842
874
_hasSentTerminatedEvent = true ;
843
875
// Always add a leading newline since the last written text might not have
844
876
// had one.
@@ -1318,6 +1350,8 @@ abstract class DartDebugAdapter<TL extends LaunchRequestArguments,
1318
1350
TerminateArguments ? args,
1319
1351
void Function () sendResponse,
1320
1352
) async {
1353
+ isTerminating = true ;
1354
+
1321
1355
await terminateImpl ();
1322
1356
await shutdown ();
1323
1357
sendResponse ();
@@ -1661,6 +1695,7 @@ abstract class DartDebugAdapter<TL extends LaunchRequestArguments,
1661
1695
}
1662
1696
1663
1697
Future <void > _handleVmServiceClosed () async {
1698
+ isTerminating = true ;
1664
1699
if (terminateOnVmServiceClose) {
1665
1700
handleSessionTerminate ();
1666
1701
}
@@ -1767,6 +1802,40 @@ abstract class DartDebugAdapter<TL extends LaunchRequestArguments,
1767
1802
streamClosed: streamClosedCompleter.future,
1768
1803
);
1769
1804
}
1805
+
1806
+ /// Wraps a function with an error handler that handles errors that occur when
1807
+ /// the VM Service/DDS shuts down.
1808
+ ///
1809
+ /// When the debug adapter is terminating, it's possible in-flight requests
1810
+ /// triggered by handlers will fail with "Service Disappeared". This is
1811
+ /// normal and such errors can be ignored, rather than allowed to pass
1812
+ /// uncaught.
1813
+ _StreamEventHandler <T > _wrapHandlerWithErrorHandling <T >(
1814
+ _StreamEventHandler <T > handler,
1815
+ ) {
1816
+ return (data) => _withErrorHandling (() => handler (data));
1817
+ }
1818
+
1819
+ /// Calls a function with an error handler that handles errors that occur when
1820
+ /// the VM Service/DDS shuts down.
1821
+ ///
1822
+ /// When the debug adapter is terminating, it's possible in-flight requests
1823
+ /// will fail with "Service Disappeared". This is normal and such errors can
1824
+ /// be ignored, rather than allowed to pass uncaught.
1825
+ FutureOr <T ?> _withErrorHandling <T >(FutureOr <T > Function () func) async {
1826
+ try {
1827
+ return await func ();
1828
+ } on vm.RPCError catch (e) {
1829
+ // If we're been asked to shut down while this request was occurring,
1830
+ // it's normal to get kServiceDisappeared so we should handle this
1831
+ // silently.
1832
+ if (isTerminating && e.code == RpcErrorCodes .kServiceDisappeared) {
1833
+ return null ;
1834
+ }
1835
+
1836
+ rethrow ;
1837
+ }
1838
+ }
1770
1839
}
1771
1840
1772
1841
/// An implementation of [LaunchRequestArguments] that includes all fields used
0 commit comments