Skip to content

Commit 546022d

Browse files
authored
[perf] optimize EDT SdkRunConfig.getState (flutter#8149)
Speeds up `SdkRunConfig.getState` which necessarily runs on the Event Dispatch Thread. The fix has two parts: 1. Move the expensive call to `ModuleUtilCore.findModuleForFile(..)` into an async read action (because we can) and 2. Speed up `inProject` because we can't background it and there's a speedy alternative to the old more expensive approach Addresses stack traces like this: ``` java.lang.Throwable: Slow operations are prohibited on EDT. See SlowOperations.assertSlowOperationsAreAllowed javadoc. at com.intellij.openapi.diagnostic.Logger.error(Logger.java:376) at com.intellij.util.SlowOperations.assertSlowOperationsAreAllowed(SlowOperations.java:114) at com.intellij.workspaceModel.core.fileIndex.impl.WorkspaceFileIndexDataImpl.ensureIsUpToDate(WorkspaceFileIndexDataImpl.kt:153) at com.intellij.workspaceModel.core.fileIndex.impl.WorkspaceFileIndexDataImpl.getFileInfo(WorkspaceFileIndexDataImpl.kt:98) at com.intellij.workspaceModel.core.fileIndex.impl.WorkspaceFileIndexImpl.getFileInfo(WorkspaceFileIndexImpl.kt:267) at com.intellij.workspaceModel.core.fileIndex.impl.WorkspaceFileIndexImpl.findFileSetWithCustomData(WorkspaceFileIndexImpl.kt:250) at com.intellij.openapi.roots.impl.ProjectFileIndexImpl.getModuleForFile(ProjectFileIndexImpl.java:102) at com.intellij.openapi.roots.impl.ProjectFileIndexImpl.getModuleForFile(ProjectFileIndexImpl.java:95) at com.intellij.openapi.module.ModuleUtilCore.lambda$findModuleForFile$0(ModuleUtilCore.java:84) at com.intellij.openapi.application.impl.AnyThreadWriteThreadingSupport.runReadAction(AnyThreadWriteThreadingSupport.kt:272) at com.intellij.openapi.application.impl.AnyThreadWriteThreadingSupport.runReadAction(AnyThreadWriteThreadingSupport.kt:262) at com.intellij.openapi.application.impl.ApplicationImpl.runReadAction(ApplicationImpl.java:863) at com.intellij.openapi.application.ReadAction.compute(ReadAction.java:66) at com.intellij.openapi.module.ModuleUtilCore.findModuleForFile(ModuleUtilCore.java:84) at io.flutter.run.SdkRunConfig.getState(SdkRunConfig.java:139) at io.flutter.run.SdkRunConfig.getState(SdkRunConfig.java:59) ``` See: flutter#8089 --- - [x] I’ve reviewed the contributor guide and applied the relevant portions to this PR. <details> <summary>Contribution guidelines:</summary><br> - See our [contributor guide]([https://github.com/dart-lang/sdk/blob/main/CONTRIBUTING.md](https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview) for general expectations for PRs. - Larger or significant changes should be discussed in an issue before creating a PR. - Dart contributions to our repos should follow the [Dart style guide](https://dart.dev/guides/language/effective-dart) and use `dart format`. - Java and Kotlin contributions should strive to follow Java and Kotlin best practices ([discussion](flutter#8098)). </details>
1 parent 9559263 commit 546022d

File tree

2 files changed

+26
-7
lines changed

2 files changed

+26
-7
lines changed

flutter-idea/src/io/flutter/run/MainFile.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
package io.flutter.run;
77

88
import com.intellij.openapi.application.ApplicationManager;
9+
import com.intellij.openapi.project.BaseProjectDirectories;
910
import com.intellij.openapi.project.Project;
10-
import com.intellij.openapi.roots.ProjectRootManager;
1111
import com.intellij.openapi.util.io.FileUtil;
1212
import com.intellij.openapi.util.text.StringUtil;
1313
import com.intellij.openapi.vfs.LocalFileSystem;
@@ -79,11 +79,15 @@ public boolean hasFlutterImports() {
7979
* If there is an error, {@link Result#canLaunch} will return false and the error is available via {@link Result#getError}
8080
*/
8181
@NotNull
82-
public static MainFile.Result verify(@Nullable String path, Project project) {
82+
public static MainFile.Result verify(@Nullable String path, @Nullable Project project) {
8383
if (!ApplicationManager.getApplication().isReadAccessAllowed()) {
8484
throw new IllegalStateException("need read access");
8585
}
8686

87+
if (project == null) {
88+
return error("Project is not set.");
89+
}
90+
8791
if (StringUtil.isEmptyOrSpaces(path)) {
8892
return error(FlutterBundle.message("entrypoint.not.set"));
8993
}
@@ -143,7 +147,10 @@ private static boolean isAppDir(@NotNull VirtualFile dir, @NotNull Project proje
143147
}
144148

145149
private static boolean inProject(@Nullable VirtualFile file, @NotNull Project project) {
146-
return file != null && ProjectRootManager.getInstance(project).getFileIndex().isInContent(file);
150+
// Do a speedy check for containment over accessing the file index (which we did historically)
151+
// but is very slow and unacceptably blocks the UI thread.
152+
// See: https://github.com/flutter/flutter-intellij/issues/8089
153+
return file != null && BaseProjectDirectories.getInstance(project).contains(file);
147154
}
148155

149156
/**

flutter-idea/src/io/flutter/run/SdkRunConfig.java

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -136,9 +136,10 @@ public LaunchState getState(@NotNull Executor executor, @NotNull ExecutionEnviro
136136
final MainFile mainFile = MainFile.verify(launchFields.getFilePath(), env.getProject()).get();
137137
final Project project = env.getProject();
138138
final RunMode mode = RunMode.fromEnv(env);
139-
final Module module = ModuleUtilCore.findModuleForFile(mainFile.getFile(), env.getProject());
139+
140140
final LaunchState.CreateAppCallback createAppCallback = (@Nullable FlutterDevice device) -> {
141141
if (device == null) return null;
142+
if (mainFile == null) return null;
142143

143144
final GeneralCommandLine command = getCommand(env, device);
144145

@@ -190,11 +191,14 @@ public LaunchState getState(@NotNull Executor executor, @NotNull ExecutionEnviro
190191
}
191192
}
192193

194+
var module = ModuleUtilCore.findModuleForFile(mainFile.getFile(), env.getProject());
195+
if (module == null) return null;
196+
193197
return getFlutterApp(env, device, project, module, mode, command);
194198
};
195199

196200
final LaunchState launcher = new LaunchState(env, mainFile.getAppDir(), mainFile.getFile(), this, createAppCallback);
197-
addConsoleFilters(launcher, env, mainFile, module);
201+
addConsoleFilters(launcher, env, mainFile, null /* look up the module in an async read context */);
198202
return launcher;
199203
}
200204

@@ -224,20 +228,28 @@ public void flutterSdkRemoved() {
224228
protected void addConsoleFilters(@NotNull LaunchState launcher,
225229
@NotNull ExecutionEnvironment env,
226230
@NotNull MainFile mainFile,
231+
// If unspecified, we'll try and find it in a non-blocking read context.
227232
@Nullable Module module) {
228233
// Creating console filters is expensive so we want to make sure we are not blocking.
229234
// See: https://github.com/flutter/flutter-intellij/issues/8089
230235
ReadAction.nonBlocking(() -> {
236+
// Make a copy of the module reference, since we may update it in this lambda.
237+
var moduleReference = module;
238+
// If no module was passed in, try and find one.
239+
if (moduleReference == null) {
240+
moduleReference = ModuleUtilCore.findModuleForFile(mainFile.getFile(), env.getProject());
241+
}
242+
231243
// Set up additional console filters.
232244
final TextConsoleBuilder builder = launcher.getConsoleBuilder();
233245
if (builder == null) return null;
234246
// file:, package:, and dart: references
235247
builder.addFilter(new DartConsoleFilter(env.getProject(), mainFile.getFile()));
236248
//// links often found when running tests
237249
//builder.addFilter(new DartRelativePathsConsoleFilter(env.getProject(), mainFile.getAppDir().getPath()));
238-
if (module != null) {
250+
if (moduleReference != null) {
239251
// various flutter run links
240-
builder.addFilter(new FlutterConsoleFilter(module));
252+
builder.addFilter(new FlutterConsoleFilter(moduleReference));
241253
}
242254
// general urls
243255
builder.addFilter(new UrlFilter());

0 commit comments

Comments
 (0)