Skip to content

Commit 6a841fd

Browse files
committed
Reduce freeze in ProjectUpdater
fixes bazelbuild#7149
1 parent 35ffeec commit 6a841fd

File tree

2 files changed

+127
-79
lines changed

2 files changed

+127
-79
lines changed

base/src/com/google/idea/blaze/base/qsync/ProjectUpdater.java

Lines changed: 94 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,6 @@
1515
*/
1616
package com.google.idea.blaze.base.qsync;
1717

18-
import static com.google.common.collect.ImmutableList.toImmutableList;
19-
import static com.google.common.collect.ImmutableSet.toImmutableSet;
20-
import static java.util.Arrays.stream;
21-
2218
import com.google.common.collect.ImmutableList;
2319
import com.google.common.collect.ImmutableMap;
2420
import com.google.common.collect.ImmutableSet;
@@ -37,7 +33,6 @@
3733
import com.google.idea.blaze.qsync.project.ProjectPath;
3834
import com.google.idea.blaze.qsync.project.ProjectProto;
3935
import com.google.idea.blaze.qsync.project.ProjectProto.LibrarySource;
40-
import com.google.idea.common.util.Transactions;
4136
import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider;
4237
import com.intellij.openapi.externalSystem.service.project.ProjectDataManager;
4338
import com.intellij.openapi.module.Module;
@@ -54,20 +49,30 @@
5449
import com.intellij.openapi.roots.libraries.Library;
5550
import com.intellij.openapi.roots.libraries.Library.ModifiableModel;
5651
import com.intellij.openapi.vfs.VfsUtil;
52+
import org.jetbrains.jps.model.java.JavaSourceRootProperties;
53+
import org.jetbrains.jps.model.java.JavaSourceRootType;
54+
import org.jetbrains.jps.model.java.JpsJavaExtensionService;
55+
5756
import java.io.File;
5857
import java.nio.file.Path;
5958
import java.nio.file.Paths;
59+
import java.util.AbstractMap;
6060
import java.util.List;
6161
import java.util.Set;
6262
import java.util.function.Function;
63-
import org.jetbrains.jps.model.java.JavaSourceRootProperties;
64-
import org.jetbrains.jps.model.java.JavaSourceRootType;
65-
import org.jetbrains.jps.model.java.JpsJavaExtensionService;
6663

67-
/** An object that monitors the build graph and applies the changes to the project structure. */
64+
import static com.google.common.collect.ImmutableList.toImmutableList;
65+
import static com.google.common.collect.ImmutableSet.toImmutableSet;
66+
import static java.util.Arrays.stream;
67+
68+
/**
69+
* An object that monitors the build graph and applies the changes to the project structure.
70+
*/
6871
public class ProjectUpdater implements QuerySyncProjectListener {
6972

70-
/** Entry point for instantiating {@link ProjectUpdater}. */
73+
/**
74+
* Entry point for instantiating {@link ProjectUpdater}.
75+
*/
7176
public static class Provider implements QuerySyncProjectListenerProvider {
7277
@Override
7378
public QuerySyncProjectListener createListener(QuerySyncProject querySyncProject) {
@@ -116,14 +121,16 @@ public void onNewProjectSnapshot(Context<?> context, QuerySyncProjectSnapshot gr
116121

117122
private void updateProjectModel(ProjectProto.Project spec, Context<?> context) {
118123
File imlDirectory = new File(BlazeDataStorage.getProjectDataDir(importSettings), "modules");
119-
Transactions.submitWriteActionTransactionAndWait(
124+
ProjectUpdaterThreadingUtils.Companion.performWriteAction(() -> {
125+
for (BlazeQuerySyncPlugin syncPlugin : BlazeQuerySyncPlugin.EP_NAME.getExtensions()) {
126+
syncPlugin.updateProjectSettingsForQuerySync(project, context, projectViewSet);
127+
}
128+
});
129+
ProjectUpdaterThreadingUtils.Companion.readWriteAction(
120130
() -> {
121131
IdeModifiableModelsProvider models =
122132
ProjectDataManager.getInstance().createModifiableModelsProvider(project);
123133

124-
for (BlazeQuerySyncPlugin syncPlugin : BlazeQuerySyncPlugin.EP_NAME.getExtensions()) {
125-
syncPlugin.updateProjectSettingsForQuerySync(project, context, projectViewSet);
126-
}
127134
int removedLibCount = removeUnusedLibraries(models, spec.getLibraryList());
128135
if (removedLibCount > 0) {
129136
context.output(PrintOutput.output("Removed " + removedLibCount + " libs"));
@@ -135,90 +142,98 @@ private void updateProjectModel(ProjectProto.Project spec, Context<?> context) {
135142
}
136143
ImmutableMap<String, Library> libMap = libMapBuilder.buildOrThrow();
137144

138-
for (ProjectProto.Module moduleSpec : spec.getModulesList()) {
139-
Module module =
140-
models.newModule(
141-
imlDirectory.toPath().resolve(moduleSpec.getName() + ".iml").toString(),
142-
mapModuleType(moduleSpec.getType()).getId());
145+
List<AbstractMap.SimpleImmutableEntry<Module, ProjectProto.Module>> modules =
146+
spec.getModulesList().stream().map(moduleSpec -> {
147+
Module module =
148+
models.newModule(
149+
imlDirectory.toPath().resolve(moduleSpec.getName() + ".iml").toString(),
150+
mapModuleType(moduleSpec.getType()).getId());
143151

144-
ModifiableRootModel roots = models.getModifiableRootModel(module);
145-
ImmutableList<OrderEntry> existingLibraryOrderEntries =
146-
stream(roots.getOrderEntries())
147-
.filter(it -> it instanceof LibraryOrderEntry)
148-
.collect(toImmutableList());
149-
for (OrderEntry entry : existingLibraryOrderEntries) {
150-
roots.removeOrderEntry(entry);
151-
}
152-
// TODO: should this be encapsulated in ProjectProto.Module?
153-
roots.inheritSdk();
154-
155-
// TODO instead of removing all content entries and re-adding, we should calculate the
156-
// diff.
157-
for (ContentEntry entry : roots.getContentEntries()) {
158-
roots.removeContentEntry(entry);
159-
}
160-
for (ProjectProto.ContentEntry ceSpec : moduleSpec.getContentEntriesList()) {
161-
ProjectPath projectPath = ProjectPath.create(ceSpec.getRoot());
152+
ModifiableRootModel roots = models.getModifiableRootModel(module);
153+
ImmutableList<OrderEntry> existingLibraryOrderEntries =
154+
stream(roots.getOrderEntries())
155+
.filter(it -> it instanceof LibraryOrderEntry)
156+
.collect(toImmutableList());
157+
for (OrderEntry entry : existingLibraryOrderEntries) {
158+
roots.removeOrderEntry(entry);
159+
}
160+
// TODO: should this be encapsulated in ProjectProto.Module?
161+
roots.inheritSdk();
162162

163-
ContentEntry contentEntry =
164-
roots.addContentEntry(
165-
UrlUtil.pathToUrl(projectPathResolver.resolve(projectPath).toString()));
166-
for (ProjectProto.SourceFolder sfSpec : ceSpec.getSourcesList()) {
167-
ProjectPath sourceFolderProjectPath = ProjectPath.create(sfSpec.getProjectPath());
163+
// TODO instead of removing all content entries and re-adding, we should calculate the
164+
// diff.
165+
for (ContentEntry entry : roots.getContentEntries()) {
166+
roots.removeContentEntry(entry);
167+
}
168+
for (ProjectProto.ContentEntry ceSpec : moduleSpec.getContentEntriesList()) {
169+
ProjectPath projectPath = ProjectPath.create(ceSpec.getRoot());
168170

169-
JavaSourceRootProperties properties =
170-
JpsJavaExtensionService.getInstance()
171-
.createSourceRootProperties(
172-
sfSpec.getPackagePrefix(), sfSpec.getIsGenerated());
173-
JavaSourceRootType rootType =
174-
sfSpec.getIsTest() ? JavaSourceRootType.TEST_SOURCE : JavaSourceRootType.SOURCE;
175-
String url =
176-
UrlUtil.pathToUrl(
177-
projectPathResolver.resolve(sourceFolderProjectPath).toString(),
178-
sourceFolderProjectPath.innerJarPath());
179-
SourceFolder unused = contentEntry.addSourceFolder(url, rootType, properties);
180-
}
181-
for (String exclude : ceSpec.getExcludesList()) {
182-
contentEntry.addExcludeFolder(
183-
UrlUtil.pathToIdeaDirectoryUrl(workspaceRoot.absolutePathFor(exclude)));
184-
}
185-
}
171+
ContentEntry contentEntry =
172+
roots.addContentEntry(
173+
UrlUtil.pathToUrl(projectPathResolver.resolve(projectPath).toString()));
174+
for (ProjectProto.SourceFolder sfSpec : ceSpec.getSourcesList()) {
175+
ProjectPath sourceFolderProjectPath = ProjectPath.create(sfSpec.getProjectPath());
186176

187-
for (String lib : moduleSpec.getLibraryNameList()) {
188-
Library library = libMap.get(lib);
189-
if (library == null) {
190-
throw new IllegalStateException(
191-
"Module refers to library " + lib + " not present in the project spec");
192-
}
193-
LibraryOrderEntry entry = roots.addLibraryEntry(library);
194-
// TODO should this stuff be specified by the Module proto too?
195-
entry.setScope(DependencyScope.COMPILE);
196-
entry.setExported(false);
197-
}
177+
JavaSourceRootProperties properties =
178+
JpsJavaExtensionService.getInstance()
179+
.createSourceRootProperties(
180+
sfSpec.getPackagePrefix(), sfSpec.getIsGenerated());
181+
JavaSourceRootType rootType =
182+
sfSpec.getIsTest() ? JavaSourceRootType.TEST_SOURCE : JavaSourceRootType.SOURCE;
183+
String url =
184+
UrlUtil.pathToUrl(
185+
projectPathResolver.resolve(sourceFolderProjectPath).toString(),
186+
sourceFolderProjectPath.innerJarPath());
187+
SourceFolder unused = contentEntry.addSourceFolder(url, rootType, properties);
188+
}
189+
for (String exclude : ceSpec.getExcludesList()) {
190+
contentEntry.addExcludeFolder(
191+
UrlUtil.pathToIdeaDirectoryUrl(workspaceRoot.absolutePathFor(exclude)));
192+
}
193+
}
198194

199-
WorkspaceLanguageSettings workspaceLanguageSettings =
200-
LanguageSupport.createWorkspaceLanguageSettings(projectViewSet);
195+
for (String lib : moduleSpec.getLibraryNameList()) {
196+
Library library = libMap.get(lib);
197+
if (library == null) {
198+
throw new IllegalStateException(
199+
"Module refers to library " + lib + " not present in the project spec");
200+
}
201+
LibraryOrderEntry entry = roots.addLibraryEntry(library);
202+
// TODO should this stuff be specified by the Module proto too?
203+
entry.setScope(DependencyScope.COMPILE);
204+
entry.setExported(false);
205+
}
206+
return new AbstractMap.SimpleImmutableEntry<>(module, moduleSpec);
207+
}).toList();
208+
return new AbstractMap.SimpleImmutableEntry<>(models, modules);
209+
},
210+
readValue -> {
211+
IdeModifiableModelsProvider models = readValue.getKey();
212+
WorkspaceLanguageSettings workspaceLanguageSettings =
213+
LanguageSupport.createWorkspaceLanguageSettings(projectViewSet);
201214

202-
for (BlazeQuerySyncPlugin syncPlugin : BlazeQuerySyncPlugin.EP_NAME.getExtensions()) {
203-
// TODO update ProjectProto.Module and updateProjectStructure() to allow a more
204-
// suitable
205-
// data type to be passed in here instead of androidResourceDirectories and
206-
// androidSourcePackages
215+
for (BlazeQuerySyncPlugin syncPlugin : BlazeQuerySyncPlugin.EP_NAME.getExtensions()) {
216+
// TODO update ProjectProto.Module and updateProjectStructure() to allow a more
217+
// suitable
218+
// data type to be passed in here instead of androidResourceDirectories and
219+
// androidSourcePackages
220+
for (AbstractMap.SimpleImmutableEntry<Module, ProjectProto.Module> moduleEntry : readValue.getValue()) {
221+
ProjectProto.Module moduleSpec = moduleEntry.getValue();
207222
syncPlugin.updateProjectStructureForQuerySync(
208223
project,
209224
context,
210225
models,
211226
workspaceRoot,
212-
module,
227+
moduleEntry.getKey(),
213228
ImmutableSet.copyOf(moduleSpec.getAndroidResourceDirectoriesList()),
214229
ImmutableSet.<String>builder()
215230
.addAll(moduleSpec.getAndroidSourcePackagesList())
216231
.addAll(moduleSpec.getAndroidCustomPackagesList())
217232
.build(),
218233
workspaceLanguageSettings);
219234
}
220-
models.commit();
221235
}
236+
models.commit();
222237
});
223238
}
224239

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.google.idea.blaze.base.qsync
2+
3+
import com.intellij.openapi.application.readAndWriteAction
4+
import com.intellij.openapi.application.writeAction
5+
import com.intellij.openapi.diagnostic.Logger
6+
import kotlinx.coroutines.runBlocking
7+
import java.util.concurrent.Callable
8+
import java.util.function.Consumer
9+
10+
class ProjectUpdaterThreadingUtils {
11+
companion object {
12+
val logger = Logger.getInstance(ProjectUpdaterThreadingUtils::class.java)
13+
fun <T> readWriteAction(readPart: Callable<T>, commit: Consumer<T>) {
14+
runBlocking {
15+
readAndWriteAction {
16+
logger.info("Starting read operation")
17+
val ret = readPart.call();
18+
writeAction {
19+
commit.accept(ret)
20+
}
21+
}
22+
}
23+
}
24+
25+
fun performWriteAction(action: Runnable) {
26+
runBlocking {
27+
writeAction<Unit> {
28+
action.run()
29+
}
30+
}
31+
}
32+
}
33+
}

0 commit comments

Comments
 (0)