mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-03-22 15:19:59 +07:00
[startup] speed up bootstrap when new modular loading approach is used (IJPL-128)
Before, ModularMain loaded information about all dependencies of the root modules to configure classpath. Now, it takes the pre-computed classpath of 'intellij.platform.bootstrap' module from MANIFEST.MF, adds it to the classpath of the system classloader (which initially contains platform-loader.jar only), load and calls com.intellij.idea.Main.main. Other product-specific modules from the main module group are added by calling ProductLoadingStrategy::addMainModuleGroupToClassPath function. In order to postpone this computation, it's done via 'mainClassLoaderDeferred' async task which is explicitly passed to functions which need to have the fully configured main class loader. When the old approach is used and all platform JARs are added to JVM classpath via command line argument, 'addMainModuleGroupToClassPath' does nothing. GitOrigin-RevId: a8590c71dc9f7ac351c0628bcb9b3e15a92b704b
This commit is contained in:
committed by
intellij-monorepo-bot
parent
47d65ace9e
commit
a55827a69c
@@ -8,6 +8,7 @@ import com.intellij.concurrency.IdeaForkJoinWorkerThreadFactory
|
||||
import com.intellij.diagnostic.StartUpMeasurer
|
||||
import com.intellij.ide.BootstrapBundle
|
||||
import com.intellij.ide.BytecodeTransformer
|
||||
import com.intellij.ide.plugins.ProductLoadingStrategy
|
||||
import com.intellij.ide.plugins.StartupAbortedException
|
||||
import com.intellij.ide.startup.StartupActionScriptManager
|
||||
import com.intellij.openapi.application.ApplicationNamesInfo
|
||||
@@ -56,9 +57,15 @@ fun main(rawArgs: Array<String>) {
|
||||
addBootstrapTiming("init scope creating", startupTimings)
|
||||
StartUpMeasurer.addTimings(startupTimings, "bootstrap", startTimeUnixNano)
|
||||
span("startApplication") {
|
||||
val mainClassLoaderDeferred = async(CoroutineName("main class loader initializing")) {
|
||||
val classLoader = AppStarter::class.java.classLoader
|
||||
ProductLoadingStrategy.strategy.addMainModuleGroupToClassPath(classLoader)
|
||||
return@async classLoader
|
||||
}
|
||||
|
||||
// not IO-, but CPU-bound due to descrambling, don't use here IO dispatcher
|
||||
val appStarterDeferred = async(CoroutineName("main class loading")) {
|
||||
val aClass = AppStarter::class.java.classLoader.loadClass("com.intellij.idea.MainImpl")
|
||||
val aClass = mainClassLoaderDeferred.await().loadClass("com.intellij.idea.MainImpl")
|
||||
MethodHandles.lookup().findConstructor(aClass, MethodType.methodType(Void.TYPE)).invoke() as AppStarter
|
||||
}
|
||||
|
||||
@@ -72,7 +79,7 @@ fun main(rawArgs: Array<String>) {
|
||||
}
|
||||
}
|
||||
|
||||
startApplication(args = args, appStarterDeferred = appStarterDeferred, mainScope = this@runBlocking, busyThread = busyThread)
|
||||
startApplication(args = args, mainClassLoaderDeferred = mainClassLoaderDeferred, appStarterDeferred = appStarterDeferred, mainScope = this@runBlocking, busyThread = busyThread)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ package com.intellij.platform.bootstrap;
|
||||
|
||||
import com.intellij.ide.plugins.ProductLoadingStrategy;
|
||||
import com.intellij.idea.Main;
|
||||
import com.intellij.platform.runtime.repository.ProductModules;
|
||||
import com.intellij.platform.runtime.repository.RuntimeModuleRepository;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -14,9 +13,9 @@ import org.jetbrains.annotations.NotNull;
|
||||
@SuppressWarnings("unused")
|
||||
public final class ModularMain {
|
||||
@SuppressWarnings("ConfusingMainMethod")
|
||||
public static void main(@NotNull RuntimeModuleRepository moduleRepository, @NotNull ProductModules productModules, String @NotNull [] args) {
|
||||
public static void main(@NotNull RuntimeModuleRepository moduleRepository, String @NotNull [] args) {
|
||||
//when this new way to load the platform will become default, strategy instance may be passed explicitly instead
|
||||
ProductLoadingStrategy.setStrategy(new ModuleBasedProductLoadingStrategy(productModules));
|
||||
ProductLoadingStrategy.setStrategy(new ModuleBasedProductLoadingStrategy(moduleRepository));
|
||||
Main.main(args);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,12 @@
|
||||
package com.intellij.platform.bootstrap
|
||||
|
||||
import com.intellij.ide.plugins.*
|
||||
import com.intellij.platform.runtime.repository.ProductModules
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.platform.runtime.repository.RuntimeModuleGroup
|
||||
import com.intellij.platform.runtime.repository.RuntimeModuleId
|
||||
import com.intellij.platform.runtime.repository.RuntimeModuleRepository
|
||||
import com.intellij.platform.runtime.repository.serialization.RuntimeModuleRepositorySerialization
|
||||
import com.intellij.util.lang.PathClassLoader
|
||||
import com.intellij.util.lang.ZipFilePool
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Deferred
|
||||
@@ -11,7 +15,30 @@ import kotlinx.coroutines.async
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
|
||||
class ModuleBasedProductLoadingStrategy(private val productModules: ProductModules) : ProductLoadingStrategy() {
|
||||
class ModuleBasedProductLoadingStrategy(private val moduleRepository: RuntimeModuleRepository) : ProductLoadingStrategy() {
|
||||
private val productModules by lazy {
|
||||
val rootModuleName = System.getProperty(PLATFORM_ROOT_MODULE_PROPERTY)
|
||||
if (rootModuleName == null) {
|
||||
error("'$PLATFORM_ROOT_MODULE_PROPERTY' system property is not specified")
|
||||
}
|
||||
val rootModule = moduleRepository.getModule(RuntimeModuleId.module(rootModuleName))
|
||||
val productModulesPath = "META-INF/$rootModuleName/product-modules.xml"
|
||||
val moduleGroupStream = rootModule.readFile(productModulesPath)
|
||||
if (moduleGroupStream == null) {
|
||||
error("$productModulesPath is not found in '$rootModuleName' module")
|
||||
}
|
||||
RuntimeModuleRepositorySerialization.loadProductModules(moduleGroupStream, productModulesPath, moduleRepository)
|
||||
}
|
||||
|
||||
override fun addMainModuleGroupToClassPath(bootstrapClassLoader: ClassLoader) {
|
||||
val mainGroupClassPath = productModules.mainModuleGroup.includedModules.flatMapTo(LinkedHashSet()) {
|
||||
it.moduleDescriptor.resourceRootPaths
|
||||
}
|
||||
val classPath = (bootstrapClassLoader as PathClassLoader).classPath
|
||||
logger<ModuleBasedProductLoadingStrategy>().info("New classpath roots:\n${(mainGroupClassPath - classPath.baseUrls.toSet()).joinToString("\n")}")
|
||||
classPath.addFiles(mainGroupClassPath)
|
||||
}
|
||||
|
||||
override fun loadBundledPluginDescriptors(scope: CoroutineScope,
|
||||
bundledPluginDir: Path?,
|
||||
isUnitTestMode: Boolean,
|
||||
@@ -58,3 +85,5 @@ class ModuleBasedProductLoadingStrategy(private val productModules: ProductModul
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const val PLATFORM_ROOT_MODULE_PROPERTY = "intellij.platform.root.module"
|
||||
@@ -382,6 +382,7 @@ private fun CoroutineScope.loadDescriptorsFromProperty(context: DescriptorListLo
|
||||
|
||||
@Suppress("DeferredIsResult")
|
||||
internal fun CoroutineScope.scheduleLoading(zipFilePoolDeferred: Deferred<ZipFilePool>?,
|
||||
mainClassLoaderDeferred: Deferred<ClassLoader>,
|
||||
logDeferred: Deferred<Logger>?): Deferred<PluginSet> {
|
||||
val resultDeferred = async(CoroutineName("plugin descriptor loading")) {
|
||||
val isUnitTestMode = PluginManagerCore.isUnitTestMode
|
||||
@@ -396,6 +397,7 @@ internal fun CoroutineScope.scheduleLoading(zipFilePoolDeferred: Deferred<ZipFil
|
||||
isUnitTestMode = isUnitTestMode,
|
||||
isRunningFromSources = isRunningFromSources,
|
||||
zipFilePoolDeferred = zipFilePoolDeferred,
|
||||
mainClassLoaderDeferred = mainClassLoaderDeferred
|
||||
)
|
||||
}
|
||||
result
|
||||
@@ -490,7 +492,8 @@ fun loadDescriptorsForDeprecatedWizard(): PluginLoadingResult {
|
||||
isMissingIncludeIgnored = isUnitTestMode,
|
||||
checkOptionalConfigFileUniqueness = isUnitTestMode || isRunningFromSources,
|
||||
).use { context ->
|
||||
loadDescriptors(context = context, isUnitTestMode = isUnitTestMode, isRunningFromSources = isRunningFromSources)
|
||||
loadDescriptors(context = context, isUnitTestMode = isUnitTestMode, isRunningFromSources = isRunningFromSources,
|
||||
mainClassLoaderDeferred = CompletableDeferred(DescriptorListLoadingContext::class.java.classLoader))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -507,10 +510,12 @@ suspend fun loadDescriptors(
|
||||
isUnitTestMode: Boolean = PluginManagerCore.isUnitTestMode,
|
||||
isRunningFromSources: Boolean,
|
||||
zipFilePoolDeferred: Deferred<ZipFilePool>? = null,
|
||||
mainClassLoaderDeferred: Deferred<ClassLoader>,
|
||||
): PluginLoadingResult {
|
||||
val listDeferred: List<Deferred<IdeaPluginDescriptorImpl?>>
|
||||
val extraListDeferred: List<Deferred<IdeaPluginDescriptorImpl?>>
|
||||
val zipFilePool = if (context.transient) null else zipFilePoolDeferred?.await()
|
||||
val mainClassLoader = mainClassLoaderDeferred.await()
|
||||
withContext(Dispatchers.IO) {
|
||||
listDeferred = loadDescriptorsFromDirs(
|
||||
context = context,
|
||||
@@ -518,6 +523,7 @@ suspend fun loadDescriptors(
|
||||
isUnitTestMode = isUnitTestMode,
|
||||
isRunningFromSources = isRunningFromSources,
|
||||
zipFilePool = zipFilePool,
|
||||
mainClassLoader = mainClassLoader
|
||||
)
|
||||
extraListDeferred = loadDescriptorsFromProperty(context, zipFilePool)
|
||||
}
|
||||
@@ -577,6 +583,7 @@ private fun CoroutineScope.loadDescriptorsFromDirs(
|
||||
isUnitTestMode: Boolean = PluginManagerCore.isUnitTestMode,
|
||||
isRunningFromSources: Boolean = PluginManagerCore.isRunningFromSources(),
|
||||
zipFilePool: ZipFilePool?,
|
||||
mainClassLoader: ClassLoader,
|
||||
): List<Deferred<IdeaPluginDescriptorImpl?>> {
|
||||
val platformPrefixProperty = PlatformUtils.getPlatformPrefix()
|
||||
val platformPrefix = if (platformPrefixProperty == PlatformUtils.QODANA_PREFIX) {
|
||||
@@ -591,7 +598,8 @@ private fun CoroutineScope.loadDescriptorsFromDirs(
|
||||
isUnitTestMode = isUnitTestMode,
|
||||
isInDevServerMode = AppMode.isDevServer(),
|
||||
isRunningFromSources = isRunningFromSources,
|
||||
pool = zipFilePool)
|
||||
pool = zipFilePool,
|
||||
classLoader = mainClassLoader)
|
||||
|
||||
val custom = loadDescriptorsFromDir(dir = customPluginDir, context = context, isBundled = false, pool = zipFilePool)
|
||||
|
||||
@@ -605,8 +613,8 @@ private fun CoroutineScope.loadCoreModules(context: DescriptorListLoadingContext
|
||||
isUnitTestMode: Boolean,
|
||||
isInDevServerMode: Boolean,
|
||||
isRunningFromSources: Boolean,
|
||||
pool: ZipFilePool?): List<Deferred<IdeaPluginDescriptorImpl?>> {
|
||||
val classLoader = DescriptorListLoadingContext::class.java.classLoader
|
||||
pool: ZipFilePool?,
|
||||
classLoader: ClassLoader): List<Deferred<IdeaPluginDescriptorImpl?>> {
|
||||
val pathResolver = ClassPathXmlPathResolver(classLoader = classLoader, isRunningFromSources = isRunningFromSources && !isInDevServerMode)
|
||||
val useCoreClassLoader = pathResolver.isRunningFromSources ||
|
||||
platformPrefix.startsWith("CodeServer") ||
|
||||
@@ -795,6 +803,7 @@ fun loadDescriptorsFromOtherIde(
|
||||
customPluginDir = customPluginDir,
|
||||
bundledPluginDir = bundledPluginDir,
|
||||
zipFilePool = null,
|
||||
mainClassLoader = DescriptorListLoadingContext::class.java.classLoader,
|
||||
)
|
||||
}, isMainProcess()),
|
||||
overrideUseIfCompatible = false,
|
||||
|
||||
@@ -27,6 +27,7 @@ import com.intellij.util.Java11Shim
|
||||
import com.intellij.util.PlatformUtils
|
||||
import com.intellij.util.lang.UrlClassLoader
|
||||
import com.intellij.util.lang.ZipFilePool
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.future.asCompletableFuture
|
||||
@@ -361,17 +362,21 @@ object PluginManagerCore {
|
||||
|
||||
@Internal
|
||||
fun scheduleDescriptorLoading(coroutineScope: CoroutineScope) {
|
||||
scheduleDescriptorLoading(coroutineScope = coroutineScope, zipFilePoolDeferred = null, logDeferred = null)
|
||||
scheduleDescriptorLoading(coroutineScope = coroutineScope,
|
||||
zipFilePoolDeferred = null,
|
||||
mainClassLoaderDeferred = CompletableDeferred(PluginManagerCore::class.java.classLoader),
|
||||
logDeferred = null)
|
||||
}
|
||||
|
||||
@Internal
|
||||
@Synchronized
|
||||
fun scheduleDescriptorLoading(coroutineScope: CoroutineScope,
|
||||
zipFilePoolDeferred: Deferred<ZipFilePool>?,
|
||||
mainClassLoaderDeferred: Deferred<ClassLoader>,
|
||||
logDeferred: Deferred<Logger>?): Deferred<PluginSet> {
|
||||
var result = initFuture
|
||||
if (result == null) {
|
||||
result = coroutineScope.scheduleLoading(zipFilePoolDeferred, logDeferred)
|
||||
result = coroutineScope.scheduleLoading(zipFilePoolDeferred, mainClassLoaderDeferred, logDeferred)
|
||||
initFuture = result
|
||||
}
|
||||
return result
|
||||
|
||||
@@ -34,6 +34,11 @@ abstract class ProductLoadingStrategy {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds roots of all modules from the main module group and their dependencies to the classpath of [bootstrapClassLoader].
|
||||
*/
|
||||
abstract fun addMainModuleGroupToClassPath(bootstrapClassLoader: ClassLoader)
|
||||
|
||||
abstract fun loadBundledPluginDescriptors(scope: CoroutineScope,
|
||||
bundledPluginDir: Path?,
|
||||
isUnitTestMode: Boolean,
|
||||
@@ -42,6 +47,9 @@ abstract class ProductLoadingStrategy {
|
||||
}
|
||||
|
||||
private class PathBasedProductLoadingStrategy : ProductLoadingStrategy() {
|
||||
override fun addMainModuleGroupToClassPath(bootstrapClassLoader: ClassLoader) {
|
||||
}
|
||||
|
||||
override fun loadBundledPluginDescriptors(scope: CoroutineScope,
|
||||
bundledPluginDir: Path?,
|
||||
isUnitTestMode: Boolean,
|
||||
|
||||
@@ -98,8 +98,9 @@ private val commandProcessor: AtomicReference<(List<String>) -> Deferred<CliResu
|
||||
internal var shellEnvDeferred: Deferred<Boolean?>? = null
|
||||
private set
|
||||
|
||||
// the main thread's dispatcher is sequential - use it with care
|
||||
// the main thread's dispatcher is sequential - use it with care
|
||||
fun CoroutineScope.startApplication(args: List<String>,
|
||||
mainClassLoaderDeferred: Deferred<ClassLoader>,
|
||||
appStarterDeferred: Deferred<AppStarter>,
|
||||
mainScope: CoroutineScope,
|
||||
busyThread: Thread) {
|
||||
@@ -113,6 +114,7 @@ fun CoroutineScope.startApplication(args: List<String>,
|
||||
}
|
||||
|
||||
val appInfoDeferred = async(CoroutineName("app info")) {
|
||||
mainClassLoaderDeferred.await()
|
||||
// required for DisabledPluginsState and EUA
|
||||
ApplicationInfoImpl.getShadowInstance()
|
||||
}
|
||||
@@ -241,6 +243,7 @@ fun CoroutineScope.startApplication(args: List<String>,
|
||||
|
||||
PluginManagerCore.scheduleDescriptorLoading(coroutineScope = asyncScope,
|
||||
zipFilePoolDeferred = zipFilePoolDeferred,
|
||||
mainClassLoaderDeferred = mainClassLoaderDeferred,
|
||||
logDeferred = logDeferred)
|
||||
}
|
||||
|
||||
@@ -746,4 +749,13 @@ fun getServer(): BuiltInServer? {
|
||||
val candidate = instance.serverDisposable
|
||||
return if (candidate is BuiltInServer) candidate else null
|
||||
}
|
||||
|
||||
@Deprecated("Use 'startApplication' with 'mainClassLoaderDeferred' parameter instead",
|
||||
ReplaceWith(
|
||||
"startApplication(args, CompletableDeferred(AppStarter::class.java.classLoader), appStarterDeferred, mainScope, busyThread)",
|
||||
"kotlinx.coroutines.CompletableDeferred"))
|
||||
fun CoroutineScope.startApplication(args: List<String>, appStarterDeferred: Deferred<AppStarter>, mainScope: CoroutineScope,
|
||||
busyThread: Thread) {
|
||||
startApplication(args, CompletableDeferred(AppStarter::class.java.classLoader), appStarterDeferred, mainScope, busyThread)
|
||||
}
|
||||
//</editor-fold>
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.platform.runtime.loader;
|
||||
|
||||
import com.intellij.platform.runtime.repository.*;
|
||||
import com.intellij.platform.runtime.repository.serialization.RuntimeModuleRepositorySerialization;
|
||||
import com.intellij.platform.runtime.repository.ProductModules;
|
||||
import com.intellij.platform.runtime.repository.RuntimeModuleRepository;
|
||||
import com.intellij.util.lang.PathClassLoader;
|
||||
import com.intellij.util.lang.UrlClassLoader;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Initiates loading of a product based on IntelliJ platform. It loads information about the product modules from {@link RuntimeModuleRepository}
|
||||
@@ -22,7 +19,6 @@ import java.util.Set;
|
||||
*/
|
||||
public final class IntellijLoader {
|
||||
private static final String RUNTIME_REPOSITORY_PATH_PROPERTY = "intellij.platform.runtime.repository.path";
|
||||
private static final String PLATFORM_ROOT_MODULE_PROPERTY = "intellij.platform.root.module";
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
String repositoryPathString = System.getProperty(RUNTIME_REPOSITORY_PATH_PROPERTY);
|
||||
@@ -30,31 +26,20 @@ public final class IntellijLoader {
|
||||
reportError(RUNTIME_REPOSITORY_PATH_PROPERTY + " is not specified");
|
||||
}
|
||||
|
||||
String rootModuleName = System.getProperty(PLATFORM_ROOT_MODULE_PROPERTY);
|
||||
if (rootModuleName == null) {
|
||||
reportError(PLATFORM_ROOT_MODULE_PROPERTY + " is not specified");
|
||||
}
|
||||
|
||||
RuntimeModuleRepository repository = RuntimeModuleRepository.create(Path.of(repositoryPathString));
|
||||
RuntimeModuleDescriptor rootModule = repository.getModule(RuntimeModuleId.module(rootModuleName));
|
||||
String productModulesPath = "META-INF/" + rootModuleName + "/product-modules.xml";
|
||||
InputStream moduleGroupStream = rootModule.readFile(productModulesPath);
|
||||
if (moduleGroupStream == null) {
|
||||
reportError(productModulesPath + " is not found in " + rootModuleName + " module");
|
||||
List<Path> bootstrapClasspath = repository.getBootstrapClasspath("intellij.platform.bootstrap");
|
||||
ClassLoader appClassLoader = IntellijLoader.class.getClassLoader();
|
||||
if (!(appClassLoader instanceof PathClassLoader)) {
|
||||
reportError("JVM for IntelliJ must be started with -Djava.system.class.loader=com.intellij.util.lang.PathClassLoader parameter");
|
||||
}
|
||||
ProductModules productModules = RuntimeModuleRepositorySerialization.loadProductModules(moduleGroupStream, productModulesPath, repository);
|
||||
String bootstrapModuleName = System.getProperty("intellij.platform.bootstrap.module", "intellij.platform.bootstrap");
|
||||
Set<Path> classpath = new LinkedHashSet<>(repository.getModule(RuntimeModuleId.module(bootstrapModuleName)).getModuleClasspath());
|
||||
for (IncludedRuntimeModule item : productModules.getMainModuleGroup().getIncludedModules()) {
|
||||
classpath.addAll(item.getModuleDescriptor().getResourceRootPaths());
|
||||
}
|
||||
PathClassLoader classLoader = new PathClassLoader(UrlClassLoader.build().files(new ArrayList<>(classpath)).parent(IntellijLoader.class.getClassLoader()));
|
||||
((PathClassLoader)appClassLoader).getClassPath().addFiles(bootstrapClasspath);
|
||||
|
||||
String bootstrapClassName = "com.intellij.platform.bootstrap.ModularMain";
|
||||
Class<?> bootstrapClass = Class.forName(bootstrapClassName, true, classLoader);
|
||||
MethodHandles.publicLookup()
|
||||
.findStatic(bootstrapClass, "main", MethodType.methodType(void.class, RuntimeModuleRepository.class, ProductModules.class, String[].class))
|
||||
.invokeExact(repository, productModules, args);
|
||||
Class<?> bootstrapClass = Class.forName(bootstrapClassName, true, appClassLoader);
|
||||
MethodHandle methodHandle = MethodHandles.publicLookup()
|
||||
.findStatic(bootstrapClass, "main",
|
||||
MethodType.methodType(void.class, RuntimeModuleRepository.class, String[].class));
|
||||
methodHandle.invokeExact(repository, args);
|
||||
}
|
||||
|
||||
@Contract("_ -> fail")
|
||||
|
||||
Reference in New Issue
Block a user