diff --git a/dwds/CHANGELOG.md b/dwds/CHANGELOG.md index 24ad55485..356b6c957 100644 --- a/dwds/CHANGELOG.md +++ b/dwds/CHANGELOG.md @@ -13,6 +13,7 @@ or not. - Pre-warm expression compiler cache to speed up Flutter Inspector loading. - Remove `ChromeProxyService.setExceptionPauseMode()`. +- Wait for an old isolate to be destroyed before creating a new one. ## 16.0.1 diff --git a/dwds/lib/src/services/chrome_proxy_service.dart b/dwds/lib/src/services/chrome_proxy_service.dart index aa21b3540..254e0c793 100644 --- a/dwds/lib/src/services/chrome_proxy_service.dart +++ b/dwds/lib/src/services/chrome_proxy_service.dart @@ -47,9 +47,13 @@ class ChromeProxyService implements VmServiceInterface { Future get isInitialized => _initializedCompleter.future; Completer _initializedCompleter = Completer(); + /// Signals when isolate is destroyed. + Future get isolateDestroyed => _isolateDestroyedCompleter.future; + Completer _isolateDestroyedCompleter = Completer()..complete(); + /// Signals when isolate starts. - Future get isStarted => _startedCompleter.future; - Completer _startedCompleter = Completer(); + Future get isIsolateStarted => _isolateStartedCompleter.future; + Completer _isolateStartedCompleter = Completer(); /// Signals when expression compiler is ready to evaluate. Future get isCompilerInitialized => _compilerCompleter.future; @@ -237,6 +241,12 @@ class ChromeProxyService implements VmServiceInterface { /// with [destroyIsolate] and recreated with this method there is a hot /// restart or full page refresh. Future createIsolate(AppConnection appConnection) async { + // Wait for previous isolate to finish before starting the new one. + // The isolate is marked as destroyed on start. + final isolateDestroyed = _isolateDestroyedCompleter.future; + _isolateDestroyedCompleter = Completer(); + await isolateDestroyed; + // Inspector is null if the previous isolate is destroyed. if (_isIsolateRunning) { throw UnsupportedError( @@ -266,6 +276,7 @@ class ChromeProxyService implements VmServiceInterface { sdkConfiguration, ); debugger.updateInspector(inspector); + _logger.fine('Creating isolate: ${inspector.isolate.id}'); final compiler = _compiler; _expressionEvaluator = compiler == null @@ -287,7 +298,7 @@ class ChromeProxyService implements VmServiceInterface { unawaited(appConnection.onStart.then((_) async { await debugger.resumeFromStart(); - _startedCompleter.complete(); + _isolateStartedCompleter.complete(); })); unawaited(appConnection.onDone.then((_) => destroyIsolate())); @@ -334,13 +345,14 @@ class ChromeProxyService implements VmServiceInterface { /// /// Clears out the [_inspector] and all related cached information. void destroyIsolate() { - _logger.fine('Destroying isolate'); if (!_isIsolateRunning) return; + final isolate = inspector.isolate; final isolateRef = inspector.isolateRef; + _logger.fine('Destroying isolate ${isolateRef.id}'); _initializedCompleter = Completer(); - _startedCompleter = Completer(); + _isolateStartedCompleter = Completer(); _compilerCompleter = Completer(); _streamNotify( 'Isolate', @@ -355,6 +367,7 @@ class ChromeProxyService implements VmServiceInterface { _expressionEvaluator?.close(); _consoleSubscription?.cancel(); _consoleSubscription = null; + _isolateDestroyedCompleter.complete(); } Future disableBreakpoints() async { @@ -443,10 +456,19 @@ ${globalLoadStrategy.loadModuleSnippet}("dart_sdk").developer.invokeExtension( return _rpcNotSupportedFuture('clearVMTimeline'); } - Future _getEvaluationResult( + Future _getEvaluationResult(String isolateId, Future Function() evaluation, String expression) async { try { final result = await evaluation(); + if (!_isIsolateRunning || isolateId != inspector.isolate.id) { + _logger.fine('Cannot get evaluation result for isolate $isolateId: ' + ' isolate exited.'); + return ErrorRef( + kind: 'error', + message: 'isolate exited', + id: createId(), + ); + } // Handle compilation errors, internal errors, // and reference errors from JavaScript evaluation in chrome. if (result.type.contains('Error')) { @@ -497,6 +519,7 @@ ${globalLoadStrategy.loadModuleSnippet}("dart_sdk").developer.invokeExtension( final library = await inspector.getLibrary(targetId); return await _getEvaluationResult( + isolateId, () => evaluator.evaluateExpression( isolateId, library?.uri, expression, scope), expression); @@ -520,6 +543,7 @@ ${globalLoadStrategy.loadModuleSnippet}("dart_sdk").developer.invokeExtension( _checkIsolate('evaluateInFrame', isolateId); return await _getEvaluationResult( + isolateId, () => evaluator.evaluateExpressionInFrame( isolateId, frameIndex, expression, scope), expression); @@ -620,7 +644,7 @@ ${globalLoadStrategy.loadModuleSnippet}("dart_sdk").developer.invokeExtension( @override Future getStack(String isolateId, {int? limit}) async { await isInitialized; - await isStarted; + await isIsolateStarted; _checkIsolate('getStack', isolateId); return (await debuggerFuture).getStack(limit: limit); } @@ -760,7 +784,7 @@ ${globalLoadStrategy.loadModuleSnippet}("dart_sdk").developer.invokeExtension( if (inspector.appConnection.isStarted) { return captureElapsedTime(() async { await isInitialized; - await isStarted; + await isIsolateStarted; _checkIsolate('resume', isolateId); return await (await debuggerFuture) .resume(step: step, frameIndex: frameIndex); diff --git a/dwds/lib/src/services/expression_evaluator.dart b/dwds/lib/src/services/expression_evaluator.dart index d947e09c3..67ccea601 100644 --- a/dwds/lib/src/services/expression_evaluator.dart +++ b/dwds/lib/src/services/expression_evaluator.dart @@ -118,7 +118,8 @@ class ExpressionEvaluator { var result = await _inspector.callFunction(jsCode, scope.values); result = await _formatEvaluationError(result); - _logger.finest('Evaluated "$expression" to "$result"'); + _logger + .finest('Evaluated "$expression" to "$result" for isolate $isolateId'); return result; }