Repository navigation
[flutter_tools] Add ToolExtensionException, exit() guard, graceful shutdown, and base modifiers - #195908
Conversation
…utdown, and base modifiers Adds `ToolExtensionException` (`domainErrorCode = -32001`) to `package:flutter_tools_core` and maps it across `ToolExtensionEntryPoint.run`, `ExtensionConnection.sendRequest`, and host extension clients/managers so extensions can surface actionable operational errors without triggering crash reports. Wraps `ToolExtensionEntryPoint.run` in `IOOverrides.runZoned` with an `exit` guard so extension isolates cannot terminate the host CLI process via `exit()`, wires a bounded `extension.shutdown` RPC into `ExtensionConnection.dispose()`, `ExtensionDiscovery.dispose()`, and host `ShutdownHooks` so registered `ToolExtensionService.shutdown()` hooks run before isolate termination, and adds `abstract base class` modifiers to the service and target hierarchies in `package:flutter_tools_extension`. Part of flutter#190755 Part of flutter#190746
…dening-and-shutdown # Conflicts: # packages/flutter_tools/lib/src/experimental/extension_build_manager.dart
…dening-and-shutdown # Conflicts: # packages/flutter_tools/test/general.shard/extension_protocol/extension_manager_test.dart
Adds `abstract base class` modifiers to `ToolExtensionService`, `ConfigurationExtension`, `DiagnosticsExtension`, `BuildTarget`, and `AssembleTarget` in `package:flutter_tools_extension` (updating subclasses across the host tool, Linux prototype, and unit tests to `final class`), and memoizes `_disposeFuture` in `ExtensionConnection.dispose()` so concurrent callers await in-flight `extension.shutdown` teardown before resolving. Part of flutter#190755 Part of flutter#190746
There was a problem hiding this comment.
Code Review
This pull request introduces ToolExtensionException to represent user-actionable operational failures over JSON-RPC 2.0 (using error code -32001) without triggering crash reports. It hardens the tool extension protocol by catching this exception across various subsystems (configuration, diagnostics, builds, cleans, device management, and templates) to log warnings or return failed results gracefully instead of crashing. Additionally, it implements a graceful shutdown mechanism (extension.shutdown RPC) with a bounded timeout when disposing of extension connections, registers the extension manager's disposal as a shutdown hook, and uses IOOverrides to prevent extensions from calling exit() directly. I have no feedback to provide as there are no review comments.
|
autosubmit label was removed for flutter/flutter/195908, because - The status or check suite Google testing has failed. Please fix the issues identified (or deflake) before re-applying this label. |
| ), | ||
| exit: (int code) => throw UnsupportedError( | ||
| 'Tool extensions must not call exit($code); throw ToolExtensionException instead.', | ||
| ), |
| /// Invoked by `ToolExtensionEntryPoint.run` when the host tool sends an | ||
| /// `extension.shutdown` RPC on CLI exit or connection disposal. Runs | ||
| /// concurrently across all registered services in the isolate via | ||
| /// `Future.wait`, with a bounded grace period of approximately 2 seconds on |
There was a problem hiding this comment.
Nit: maybe consider not specifying how long the host will wait for? This might get stale easily. Maybe:
| /// `Future.wait`, with a bounded grace period of approximately 2 seconds on | |
| /// `Future.wait`, with a short bounded grace period before the | |
| /// extension isolate is terminated. |
| hostPlatform: finalOS.hostPlatform, | ||
| logger: finalLogger, | ||
| ); | ||
| finalShutdownHooks.addShutdownHook(extensionManager.dispose); |
There was a problem hiding this comment.
Opus bubbled up that there's a potential races between ExtensionManager.initialize() and ExtensionManager.dispose() because dispose() doesn't wait until initialize() completes. This could cause an issue if a user presses Ctrl+C while extensions are initializing. Also because dispose() resets _initFuture, a later ensureInitialized() call during shutdown could cause extension to be initialized again.
Opus suggests:
- Make
dispose()wait onExtensionManager._initFuture - Add a
_disposedflag that makesensureInitialized()andinitialize()no-op.
I'm fine with landing this as-is and fixing this in a follow-up PR if that makes things easier.
…13653) Manual roll Flutter from 9c427b5d7b1c to 1b9585481095 (32 revisions) Manual roll requested by [email protected] flutter/flutter@9c427b5...1b95854 2026-11-13 [email protected] Roll pub packages (flutter/flutter#196541) 2026-11-13 [email protected] Do not route PRs for review without cla, convert to draft (flutter/flutter#196341) 2026-11-13 [email protected] Roll Skia from d87a6c04da74 to 11f46cfe8838 (13 revisions) (flutter/flutter#196533) 2026-11-13 [email protected] [flutter_tools, flutter_test] Deprecate --experimental-faster-testing (flutter/flutter#196123) 2026-11-13 [email protected] [flutter_tools] Handle RPCError in ScreenshotCommand.runSkia (flutter/flutter#196132) 2026-11-13 [email protected] Roll Dart SDK from a8b961fd48bb to c91e8b6674e2 (2 revisions) (flutter/flutter#196502) 2026-11-13 [email protected] Roll Skia from 0c5ce4960cef to d87a6c04da74 (12 revisions) (flutter/flutter#196497) 2026-11-13 [email protected] Don't call `skia_image()` for raster-owned images in `DlImage::Equals` (flutter/flutter#196473) 2026-11-13 [email protected] [flutter_tools] Require FlutterCommandRunner DI fields and extract findCommandName (flutter/flutter#196331) 2026-11-13 [email protected] [web] Unify ui.Canvas behind a shared EngineCanvas frontend (flutter/flutter#196348) 2026-11-13 [email protected] Link to MediaQuery.displayFeaturesOf from DisplayFeatures (flutter/flutter#193728) 2026-11-13 [email protected] Removes reference to unnecessary ensureSemantics call in doc (flutter/flutter#195831) 2026-11-13 [email protected] [flutter_tools] Register trackWidgetCreation in AppleBuildOptionsBundle (flutter/flutter#196078) 2026-11-12 [email protected] [Android] Skip the HardwareBuffer defect fallback for platform views (flutter/flutter#193441) 2026-11-12 [email protected] Fix supports_decal check in framebuffer_blend.frag (flutter/flutter#196227) 2026-11-12 [email protected] [ios] Update the screen in didUpdateEffectiveGeometry (flutter/flutter#195508) 2026-11-12 [email protected] [flutter_tools] Add `Cache-Control` headers for web test assets (flutter/flutter#196414) 2026-11-12 [email protected] Roll Skia from 3b71ec08218f to 0c5ce4960cef (16 revisions) (flutter/flutter#196472) 2026-11-12 [email protected] [web] Render bounded ImageFilter.blur on wimp (flutter/flutter#196081) 2026-11-12 [email protected] Roll Dart SDK from 69567bd3c427 to a8b961fd48bb (2 revisions) (flutter/flutter#196464) 2026-11-12 [email protected] [engine] Release the Skia OpenGL resource context on shutdown (flutter/flutter#193592) 2026-11-12 [email protected] [web] Fix decoding of still AVIF images on Chrome (flutter/flutter#195958) 2026-11-12 [email protected] Gate SurfaceControl context setting on API 34+ (flutter/flutter#196394) 2026-11-12 [email protected] [flutter_tools] Only pass on a valid root tool in DASH__TOOL (flutter/flutter#196228) 2026-11-12 [email protected] Support GL_TEXTURE_EXTERNAL_OES embedder textures in Impeller (flutter/flutter#195857) 2026-11-12 [email protected] Stop building linux/wasm_release in local_engine_builds (flutter/flutter#196345) 2026-11-12 [email protected] Split flame out of Linux customer_testing (flutter/flutter#196347) 2026-11-12 Jiwon Lee [Windows] Fix Space input after Alt+Space with previous Microsoft IME (flutter/flutter#192105) 2026-11-12 [email protected] [flutter_tools] Add ToolExtensionException, exit() guard, graceful shutdown, and base modifiers (flutter/flutter#195908) 2026-11-12 [email protected] Roll Packages from 653628e to 14f9933 (9 revisions) (flutter/flutter#196450) 2026-11-12 [email protected] Roll Dart SDK from e5add7a25b32 to 69567bd3c427 (2 revisions) (flutter/flutter#196437) 2026-11-12 [email protected] Roll Skia from 9aa2513a4ac9 to 3b71ec08218f (1 revision) (flutter/flutter#196436) If this roll has caused a breakage, revert this CL and set the roller to dry run mode using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC [email protected],[email protected] on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 ...
Adds
ToolExtensionException(domainErrorCode = -32001) topackage:flutter_tools_coreand maps it acrossToolExtensionEntryPoint.run,ExtensionConnection.sendRequest, and host extension clients/managers so extensions can surface actionable operational errors without triggering crash reports. WrapsToolExtensionEntryPoint.runinIOOverrides.runZonedwith anexitguard so extension isolates cannot terminate the host CLI process viaexit(), wires a boundedextension.shutdownRPC intoExtensionConnection.dispose(),ExtensionDiscovery.dispose(), and hostShutdownHooksso registeredToolExtensionService.shutdown()hooks run before isolate termination, and addsabstract base classmodifiers to the service and target hierarchies inpackage:flutter_tools_extension.Part of #190755
Part of #190746