Skip to content

Commit

Permalink
Reduce freeze in ProjectUpdater
Browse files Browse the repository at this point in the history
  • Loading branch information
sellophane committed Dec 19, 2024
1 parent 35ffeec commit eb1b62b
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 83 deletions.
173 changes: 94 additions & 79 deletions base/src/com/google/idea/blaze/base/qsync/ProjectUpdater.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,6 @@
*/
package com.google.idea.blaze.base.qsync;

import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static java.util.Arrays.stream;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
Expand All @@ -37,7 +33,6 @@
import com.google.idea.blaze.qsync.project.ProjectPath;
import com.google.idea.blaze.qsync.project.ProjectProto;
import com.google.idea.blaze.qsync.project.ProjectProto.LibrarySource;
import com.google.idea.common.util.Transactions;
import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider;
import com.intellij.openapi.externalSystem.service.project.ProjectDataManager;
import com.intellij.openapi.module.Module;
Expand All @@ -54,20 +49,30 @@
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.roots.libraries.Library.ModifiableModel;
import com.intellij.openapi.vfs.VfsUtil;
import org.jetbrains.jps.model.java.JavaSourceRootProperties;
import org.jetbrains.jps.model.java.JavaSourceRootType;
import org.jetbrains.jps.model.java.JpsJavaExtensionService;

import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.AbstractMap;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import org.jetbrains.jps.model.java.JavaSourceRootProperties;
import org.jetbrains.jps.model.java.JavaSourceRootType;
import org.jetbrains.jps.model.java.JpsJavaExtensionService;

/** An object that monitors the build graph and applies the changes to the project structure. */
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static java.util.Arrays.stream;

/**
* An object that monitors the build graph and applies the changes to the project structure.
*/
public class ProjectUpdater implements QuerySyncProjectListener {

/** Entry point for instantiating {@link ProjectUpdater}. */
/**
* Entry point for instantiating {@link ProjectUpdater}.
*/
public static class Provider implements QuerySyncProjectListenerProvider {
@Override
public QuerySyncProjectListener createListener(QuerySyncProject querySyncProject) {
Expand Down Expand Up @@ -116,14 +121,16 @@ public void onNewProjectSnapshot(Context<?> context, QuerySyncProjectSnapshot gr

private void updateProjectModel(ProjectProto.Project spec, Context<?> context) {
File imlDirectory = new File(BlazeDataStorage.getProjectDataDir(importSettings), "modules");
Transactions.submitWriteActionTransactionAndWait(
ProjectUpdaterThreadingUtils.Companion.performWriteAction(() -> {
for (BlazeQuerySyncPlugin syncPlugin : BlazeQuerySyncPlugin.EP_NAME.getExtensions()) {
syncPlugin.updateProjectSettingsForQuerySync(project, context, projectViewSet);
}
});
ProjectUpdaterThreadingUtils.Companion.readWriteAction(
() -> {
IdeModifiableModelsProvider models =
ProjectDataManager.getInstance().createModifiableModelsProvider(project);

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

for (ProjectProto.Module moduleSpec : spec.getModulesList()) {
Module module =
models.newModule(
imlDirectory.toPath().resolve(moduleSpec.getName() + ".iml").toString(),
mapModuleType(moduleSpec.getType()).getId());
List<AbstractMap.SimpleImmutableEntry<Module, ProjectProto.Module>> modules =
spec.getModulesList().stream().map(moduleSpec -> {
Module module =
models.newModule(
imlDirectory.toPath().resolve(moduleSpec.getName() + ".iml").toString(),
mapModuleType(moduleSpec.getType()).getId());

ModifiableRootModel roots = models.getModifiableRootModel(module);
ImmutableList<OrderEntry> existingLibraryOrderEntries =
stream(roots.getOrderEntries())
.filter(it -> it instanceof LibraryOrderEntry)
.collect(toImmutableList());
for (OrderEntry entry : existingLibraryOrderEntries) {
roots.removeOrderEntry(entry);
}
// TODO: should this be encapsulated in ProjectProto.Module?
roots.inheritSdk();

// TODO instead of removing all content entries and re-adding, we should calculate the
// diff.
for (ContentEntry entry : roots.getContentEntries()) {
roots.removeContentEntry(entry);
}
for (ProjectProto.ContentEntry ceSpec : moduleSpec.getContentEntriesList()) {
ProjectPath projectPath = ProjectPath.create(ceSpec.getRoot());
ModifiableRootModel roots = models.getModifiableRootModel(module);
ImmutableList<OrderEntry> existingLibraryOrderEntries =
stream(roots.getOrderEntries())
.filter(it -> it instanceof LibraryOrderEntry)
.collect(toImmutableList());
for (OrderEntry entry : existingLibraryOrderEntries) {
roots.removeOrderEntry(entry);
}
// TODO: should this be encapsulated in ProjectProto.Module?
roots.inheritSdk();

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

JavaSourceRootProperties properties =
JpsJavaExtensionService.getInstance()
.createSourceRootProperties(
sfSpec.getPackagePrefix(), sfSpec.getIsGenerated());
JavaSourceRootType rootType =
sfSpec.getIsTest() ? JavaSourceRootType.TEST_SOURCE : JavaSourceRootType.SOURCE;
String url =
UrlUtil.pathToUrl(
projectPathResolver.resolve(sourceFolderProjectPath).toString(),
sourceFolderProjectPath.innerJarPath());
SourceFolder unused = contentEntry.addSourceFolder(url, rootType, properties);
}
for (String exclude : ceSpec.getExcludesList()) {
contentEntry.addExcludeFolder(
UrlUtil.pathToIdeaDirectoryUrl(workspaceRoot.absolutePathFor(exclude)));
}
}
ContentEntry contentEntry =
roots.addContentEntry(
UrlUtil.pathToUrl(projectPathResolver.resolve(projectPath).toString()));
for (ProjectProto.SourceFolder sfSpec : ceSpec.getSourcesList()) {
ProjectPath sourceFolderProjectPath = ProjectPath.create(sfSpec.getProjectPath());

for (String lib : moduleSpec.getLibraryNameList()) {
Library library = libMap.get(lib);
if (library == null) {
throw new IllegalStateException(
"Module refers to library " + lib + " not present in the project spec");
}
LibraryOrderEntry entry = roots.addLibraryEntry(library);
// TODO should this stuff be specified by the Module proto too?
entry.setScope(DependencyScope.COMPILE);
entry.setExported(false);
}
JavaSourceRootProperties properties =
JpsJavaExtensionService.getInstance()
.createSourceRootProperties(
sfSpec.getPackagePrefix(), sfSpec.getIsGenerated());
JavaSourceRootType rootType =
sfSpec.getIsTest() ? JavaSourceRootType.TEST_SOURCE : JavaSourceRootType.SOURCE;
String url =
UrlUtil.pathToUrl(
projectPathResolver.resolve(sourceFolderProjectPath).toString(),
sourceFolderProjectPath.innerJarPath());
SourceFolder unused = contentEntry.addSourceFolder(url, rootType, properties);
}
for (String exclude : ceSpec.getExcludesList()) {
contentEntry.addExcludeFolder(
UrlUtil.pathToIdeaDirectoryUrl(workspaceRoot.absolutePathFor(exclude)));
}
}

WorkspaceLanguageSettings workspaceLanguageSettings =
LanguageSupport.createWorkspaceLanguageSettings(projectViewSet);
for (String lib : moduleSpec.getLibraryNameList()) {
Library library = libMap.get(lib);
if (library == null) {
throw new IllegalStateException(
"Module refers to library " + lib + " not present in the project spec");
}
LibraryOrderEntry entry = roots.addLibraryEntry(library);
// TODO should this stuff be specified by the Module proto too?
entry.setScope(DependencyScope.COMPILE);
entry.setExported(false);
}
return new AbstractMap.SimpleImmutableEntry<>(module, moduleSpec);
}).toList();
return new AbstractMap.SimpleImmutableEntry<>(models, modules);
},
readValue -> {
IdeModifiableModelsProvider models = readValue.getKey();
WorkspaceLanguageSettings workspaceLanguageSettings =
LanguageSupport.createWorkspaceLanguageSettings(projectViewSet);

for (BlazeQuerySyncPlugin syncPlugin : BlazeQuerySyncPlugin.EP_NAME.getExtensions()) {
// TODO update ProjectProto.Module and updateProjectStructure() to allow a more
// suitable
// data type to be passed in here instead of androidResourceDirectories and
// androidSourcePackages
for (BlazeQuerySyncPlugin syncPlugin : BlazeQuerySyncPlugin.EP_NAME.getExtensions()) {
// TODO update ProjectProto.Module and updateProjectStructure() to allow a more
// suitable
// data type to be passed in here instead of androidResourceDirectories and
// androidSourcePackages
for (AbstractMap.SimpleImmutableEntry<Module, ProjectProto.Module> moduleEntry : readValue.getValue()) {
ProjectProto.Module moduleSpec = moduleEntry.getValue();
syncPlugin.updateProjectStructureForQuerySync(
project,
context,
models,
workspaceRoot,
module,
moduleEntry.getKey(),
ImmutableSet.copyOf(moduleSpec.getAndroidResourceDirectoriesList()),
ImmutableSet.<String>builder()
.addAll(moduleSpec.getAndroidSourcePackagesList())
.addAll(moduleSpec.getAndroidCustomPackagesList())
.build(),
workspaceLanguageSettings);
}
models.commit();
}
models.commit();
});
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.google.idea.blaze.base.qsync

import com.google.idea.common.util.Transactions
import com.intellij.openapi.application.readAndWriteAction
import com.intellij.openapi.application.writeAction
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.util.registry.Registry
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import java.util.concurrent.Callable
import java.util.function.Consumer

class ProjectUpdaterThreadingUtils {
companion object {
val logger = Logger.getInstance(ProjectUpdaterThreadingUtils::class.java)
fun <T> readWriteAction(readPart: Callable<T>, commit: Consumer<T>) {
//if (Registry.`is`("bazel.qsync.enable.coroutine.project.updater")) {
runBlocking {
readAndWriteAction {
logger.info("Starting read operation")
val ret = readPart.call();
writeAction {
commit.accept(ret)
}
}
}
/*} else {
Transactions.submitWriteActionTransactionAndWait {
val ret = readPart.call()
commit.accept(ret)
}
}*/
}
fun performWriteAction(action: Runnable) {
runBlocking {
writeAction<Unit>{
action.run()
}
}
}
}
}
7 changes: 4 additions & 3 deletions querysync/java/com/google/idea/blaze/qsync/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ java_library(
name = "qsync",
srcs = glob(["*.java"]),
visibility = [
"//querysync:__subpackages__",
"//javatests/com/google/devtools/intellij/blaze/plugin/aswb:__pkg__",
"//querysync:__subpackages__",
],
deps = [
"@jsr305_annotations//jar",
"//base:blaze_executor",
"//querysync/java/com/google/idea/blaze/qsync/artifacts",
"//querysync/java/com/google/idea/blaze/qsync/cc",
"//querysync/java/com/google/idea/blaze/qsync/deps",
Expand All @@ -21,7 +21,8 @@ java_library(
"//shared:vcs",
"//third_party/java/auto_value",
"@com_google_guava_guava//jar",
"@protobuf//:protobuf_java",
"@error_prone_annotations//jar",
"@jsr305_annotations//jar",
"@protobuf//:protobuf_java",
],
)
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.io.ByteSource;
import com.google.idea.blaze.base.async.executor.BlazeExecutor;
import com.google.idea.blaze.common.Context;
import com.google.idea.blaze.exception.BuildException;
import java.util.List;
Expand Down Expand Up @@ -52,7 +53,10 @@ public void setCurrent(Context<?> context, QuerySyncProjectSnapshot newInstance)
listeners = ImmutableList.copyOf(this.listeners);
}
for (QuerySyncProjectListener l : listeners) {
l.onNewProjectSnapshot(context, newInstance);
BlazeExecutor.getInstance().submit(() -> {
l.onNewProjectSnapshot(context, newInstance);
return null;
});
}
}

Expand Down

0 comments on commit eb1b62b

Please sign in to comment.