mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-15 02:59:33 +07:00
IJPL-166 enable coroutine dump a little bit later, log error
GitOrigin-RevId: 214a83e6855634ecc47c6cf8b17f4920336f729a
This commit is contained in:
committed by
intellij-monorepo-bot
parent
082d393a4b
commit
b6bbebcdae
@@ -4,7 +4,10 @@
|
|||||||
@file:Suppress("RAW_RUN_BLOCKING")
|
@file:Suppress("RAW_RUN_BLOCKING")
|
||||||
package com.intellij.platform.ide.bootstrap
|
package com.intellij.platform.ide.bootstrap
|
||||||
|
|
||||||
|
import com.intellij.diagnostic.COROUTINE_DUMP_HEADER
|
||||||
import com.intellij.diagnostic.LoadingState
|
import com.intellij.diagnostic.LoadingState
|
||||||
|
import com.intellij.diagnostic.dumpCoroutines
|
||||||
|
import com.intellij.diagnostic.enableCoroutineDump
|
||||||
import com.intellij.ide.*
|
import com.intellij.ide.*
|
||||||
import com.intellij.ide.bootstrap.InitAppContext
|
import com.intellij.ide.bootstrap.InitAppContext
|
||||||
import com.intellij.ide.gdpr.EndUserAgreement
|
import com.intellij.ide.gdpr.EndUserAgreement
|
||||||
@@ -47,6 +50,7 @@ import com.intellij.util.PlatformUtils
|
|||||||
import com.intellij.util.io.URLUtil
|
import com.intellij.util.io.URLUtil
|
||||||
import com.intellij.util.io.createDirectories
|
import com.intellij.util.io.createDirectories
|
||||||
import com.intellij.util.lang.ZipFilePool
|
import com.intellij.util.lang.ZipFilePool
|
||||||
|
import com.jetbrains.JBR
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import org.jetbrains.annotations.ApiStatus.Internal
|
import org.jetbrains.annotations.ApiStatus.Internal
|
||||||
import org.jetbrains.annotations.VisibleForTesting
|
import org.jetbrains.annotations.VisibleForTesting
|
||||||
@@ -178,13 +182,23 @@ internal suspend fun loadApp(app: ApplicationImpl,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val coroutineDebugJob = launch(CoroutineName("coroutine debug probes init")) {
|
||||||
|
enableCoroutineDump().onFailure { e ->
|
||||||
|
LOG.error("Cannot enable coroutine debug dump", e)
|
||||||
|
}
|
||||||
|
enableJstack()
|
||||||
|
}
|
||||||
|
|
||||||
asyncScope.launch {
|
asyncScope.launch {
|
||||||
launch(CoroutineName("checkThirdPartyPluginsAllowed")) {
|
// do not use launch here - don't overload CPU, let some room for JIT and other CPU-intensive tasks during start-up
|
||||||
|
coroutineDebugJob.join()
|
||||||
|
|
||||||
|
span("checkThirdPartyPluginsAllowed") {
|
||||||
checkThirdPartyPluginsAllowed()
|
checkThirdPartyPluginsAllowed()
|
||||||
}
|
}
|
||||||
|
|
||||||
// doesn't block app start-up
|
// doesn't block app start-up
|
||||||
launch(CoroutineName("post app init tasks")) {
|
span("post app init tasks") {
|
||||||
runPostAppInitTasks()
|
runPostAppInitTasks()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,6 +211,17 @@ internal suspend fun loadApp(app: ApplicationImpl,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun enableJstack() {
|
||||||
|
span("coroutine jstack configuration") {
|
||||||
|
JBR.getJstack()?.includeInfoFrom {
|
||||||
|
"""
|
||||||
|
$COROUTINE_DUMP_HEADER
|
||||||
|
${dumpCoroutines(stripDump = false)}
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private suspend fun preInitApp(app: ApplicationImpl,
|
private suspend fun preInitApp(app: ApplicationImpl,
|
||||||
asyncScope: CoroutineScope,
|
asyncScope: CoroutineScope,
|
||||||
initLafJob: Job,
|
initLafJob: Job,
|
||||||
|
|||||||
@@ -26,9 +26,9 @@ private val LOG: Logger
|
|||||||
get() = logger<IdeStartupWizard>()
|
get() = logger<IdeStartupWizard>()
|
||||||
|
|
||||||
val isIdeStartupWizardEnabled: Boolean
|
val isIdeStartupWizardEnabled: Boolean
|
||||||
get() = !ApplicationManagerEx.isInIntegrationTest()
|
get() = !ApplicationManagerEx.isInIntegrationTest() &&
|
||||||
&& System.getProperty ("intellij.startup.wizard", "true").toBoolean()
|
System.getProperty ("intellij.startup.wizard", "true").toBoolean() &&
|
||||||
&& IdeStartupExperiment.isExperimentEnabled()
|
IdeStartupExperiment.isExperimentEnabled()
|
||||||
|
|
||||||
@ExperimentalCoroutinesApi
|
@ExperimentalCoroutinesApi
|
||||||
internal suspend fun runStartupWizard(isInitialStart: Job, app: Application) {
|
internal suspend fun runStartupWizard(isInitialStart: Job, app: Application) {
|
||||||
@@ -174,9 +174,9 @@ private object IdeStartupExperiment {
|
|||||||
|
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
private fun getGroupKind(group: Int) = when {
|
private fun getGroupKind(group: Int) = when {
|
||||||
PlatformUtils.isIdeaUltimate() || PlatformUtils.isPyCharmPro() -> when {
|
PlatformUtils.isIdeaUltimate() || PlatformUtils.isPyCharmPro() -> when (group) {
|
||||||
group in 0..7 -> GroupKind.Experimental
|
in 0..7 -> GroupKind.Experimental
|
||||||
group == 8 || group == 9 -> GroupKind.Control
|
8, 9 -> GroupKind.Control
|
||||||
else -> GroupKind.Undefined
|
else -> GroupKind.Undefined
|
||||||
}
|
}
|
||||||
else -> when (group) {
|
else -> when (group) {
|
||||||
@@ -186,12 +186,13 @@ private object IdeStartupExperiment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun String.asBucket() = MathUtil.nonNegativeAbs(this.hashCode()) % 256
|
private fun asBucket(s: String) = MathUtil.nonNegativeAbs(s.hashCode()) % 256
|
||||||
|
|
||||||
private fun getBucket(): Int {
|
private fun getBucket(): Int {
|
||||||
val deviceId = LOG.runAndLogException {
|
val deviceId = LOG.runAndLogException {
|
||||||
DeviceIdManager.getOrGenerateId(object : DeviceIdManager.DeviceIdToken {}, "FUS")
|
DeviceIdManager.getOrGenerateId(object : DeviceIdManager.DeviceIdToken {}, "FUS")
|
||||||
} ?: return 0
|
} ?: return 0
|
||||||
return deviceId.asBucket()
|
return asBucket(deviceId)
|
||||||
}
|
}
|
||||||
|
|
||||||
val experimentGroup by lazy {
|
val experimentGroup by lazy {
|
||||||
@@ -204,7 +205,7 @@ private object IdeStartupExperiment {
|
|||||||
experimentGroup
|
experimentGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
val experimentGroupKind by lazy {
|
val experimentGroupKind: GroupKind by lazy {
|
||||||
getGroupKind(experimentGroup)
|
getGroupKind(experimentGroup)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,160 @@
|
|||||||
|
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||||
|
package com.intellij.platform.ide.bootstrap
|
||||||
|
|
||||||
|
import com.intellij.accessibility.enableScreenReaderSupportIfNecessary
|
||||||
|
import com.intellij.ide.gdpr.EndUserAgreement
|
||||||
|
import com.intellij.idea.AppMode
|
||||||
|
import com.intellij.openapi.application.ConfigImportHelper
|
||||||
|
import com.intellij.openapi.application.PathManager
|
||||||
|
import com.intellij.openapi.application.impl.RawSwingDispatcher
|
||||||
|
import com.intellij.openapi.diagnostic.Logger
|
||||||
|
import com.intellij.openapi.diagnostic.getOrLogException
|
||||||
|
import com.intellij.openapi.util.IconLoader
|
||||||
|
import com.intellij.openapi.util.registry.EarlyAccessRegistryManager
|
||||||
|
import com.intellij.platform.diagnostic.telemetry.impl.span
|
||||||
|
import kotlinx.coroutines.CompletableDeferred
|
||||||
|
import kotlinx.coroutines.Deferred
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import java.nio.file.Path
|
||||||
|
import java.util.concurrent.CancellationException
|
||||||
|
|
||||||
|
internal suspend fun importConfigIfNeeded(isHeadless: Boolean,
|
||||||
|
configImportNeededDeferred: Deferred<Boolean>,
|
||||||
|
lockSystemDirsJob: Job,
|
||||||
|
logDeferred: Deferred<Logger>,
|
||||||
|
args: List<String>,
|
||||||
|
targetDirectoryToImportConfig: Path?,
|
||||||
|
appStarterDeferred: Deferred<AppStarter>,
|
||||||
|
euaDocumentDeferred: Deferred<EndUserAgreement.Document?>,
|
||||||
|
initLafJob: Job): Job? {
|
||||||
|
if (isHeadless) {
|
||||||
|
importConfigHeadless(configImportNeededDeferred = configImportNeededDeferred,
|
||||||
|
lockSystemDirsJob = lockSystemDirsJob,
|
||||||
|
logDeferred = logDeferred,
|
||||||
|
args = args,
|
||||||
|
targetDirectoryToImportConfig = targetDirectoryToImportConfig,
|
||||||
|
appStarterDeferred = appStarterDeferred,
|
||||||
|
euaDocumentDeferred = euaDocumentDeferred)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (AppMode.isRemoteDevHost() || !configImportNeededDeferred.await()) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
initLafJob.join()
|
||||||
|
val log = logDeferred.await()
|
||||||
|
importConfig(
|
||||||
|
args = args,
|
||||||
|
targetDirectoryToImportConfig = targetDirectoryToImportConfig ?: PathManager.getConfigDir(),
|
||||||
|
log = log,
|
||||||
|
appStarter = appStarterDeferred.await(),
|
||||||
|
euaDocumentDeferred = euaDocumentDeferred,
|
||||||
|
)
|
||||||
|
|
||||||
|
if (ConfigImportHelper.isNewUser()) {
|
||||||
|
enableNewUi(logDeferred)
|
||||||
|
|
||||||
|
if (isIdeStartupWizardEnabled) {
|
||||||
|
log.info("Will enter initial app wizard flow.")
|
||||||
|
val result = CompletableDeferred<Boolean>()
|
||||||
|
isInitialStart = result
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun importConfig(args: List<String>,
|
||||||
|
targetDirectoryToImportConfig: Path,
|
||||||
|
log: Logger,
|
||||||
|
appStarter: AppStarter,
|
||||||
|
euaDocumentDeferred: Deferred<EndUserAgreement.Document?>,
|
||||||
|
headlessAutoImport: Boolean = false) {
|
||||||
|
if (headlessAutoImport) {
|
||||||
|
// headless AppStarters are not notified about config import
|
||||||
|
val veryFirstStartOnThisComputer = euaDocumentDeferred.await() != null
|
||||||
|
withContext(RawSwingDispatcher) {
|
||||||
|
try {
|
||||||
|
ConfigImportHelper.importConfigsTo(veryFirstStartOnThisComputer, targetDirectoryToImportConfig, args, log, true)
|
||||||
|
log.info("Automatic config import completed")
|
||||||
|
}
|
||||||
|
catch (e: UnsupportedOperationException) {
|
||||||
|
log.info("Automatic config import is not possible", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EarlyAccessRegistryManager.invalidate()
|
||||||
|
IconLoader.clearCache()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
span("screen reader checking") {
|
||||||
|
runCatching {
|
||||||
|
enableScreenReaderSupportIfNecessary()
|
||||||
|
}.getOrLogException(log)
|
||||||
|
}
|
||||||
|
|
||||||
|
span("config importing") {
|
||||||
|
appStarter.beforeImportConfigs()
|
||||||
|
|
||||||
|
val veryFirstStartOnThisComputer = euaDocumentDeferred.await() != null
|
||||||
|
withContext(RawSwingDispatcher) {
|
||||||
|
ConfigImportHelper.importConfigsTo(veryFirstStartOnThisComputer, targetDirectoryToImportConfig, args, log)
|
||||||
|
}
|
||||||
|
appStarter.importFinished(targetDirectoryToImportConfig)
|
||||||
|
EarlyAccessRegistryManager.invalidate()
|
||||||
|
IconLoader.clearCache()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun enableNewUi(logDeferred: Deferred<Logger>) {
|
||||||
|
if (System.getProperty("ide.experimental.ui") == null) {
|
||||||
|
try {
|
||||||
|
EarlyAccessRegistryManager.setAndFlush(mapOf("ide.experimental.ui" to "true"))
|
||||||
|
}
|
||||||
|
catch (e: CancellationException) {
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
catch (e: Throwable) {
|
||||||
|
logDeferred.await().error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun importConfigHeadless(configImportNeededDeferred: Deferred<Boolean>,
|
||||||
|
lockSystemDirsJob: Job,
|
||||||
|
logDeferred: Deferred<Logger>,
|
||||||
|
args: List<String>,
|
||||||
|
targetDirectoryToImportConfig: Path?,
|
||||||
|
appStarterDeferred: Deferred<AppStarter>,
|
||||||
|
euaDocumentDeferred: Deferred<EndUserAgreement.Document?>) {
|
||||||
|
if (!configImportNeededDeferred.await()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure we lock the dir before writing
|
||||||
|
lockSystemDirsJob.join()
|
||||||
|
if (!ConfigImportHelper.isHeadlessAutomaticConfigImportAllowed()) {
|
||||||
|
enableNewUi(logDeferred)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val log = logDeferred.await()
|
||||||
|
importConfig(
|
||||||
|
args = args,
|
||||||
|
targetDirectoryToImportConfig = targetDirectoryToImportConfig ?: PathManager.getConfigDir(),
|
||||||
|
log = log,
|
||||||
|
appStarter = appStarterDeferred.await(),
|
||||||
|
euaDocumentDeferred = euaDocumentDeferred,
|
||||||
|
headlessAutoImport = true
|
||||||
|
)
|
||||||
|
if (ConfigImportHelper.isNewUser()) {
|
||||||
|
enableNewUi(logDeferred)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,11 +5,9 @@
|
|||||||
package com.intellij.platform.ide.bootstrap
|
package com.intellij.platform.ide.bootstrap
|
||||||
|
|
||||||
import com.intellij.BundleBase
|
import com.intellij.BundleBase
|
||||||
import com.intellij.accessibility.enableScreenReaderSupportIfNecessary
|
|
||||||
import com.intellij.diagnostic.*
|
import com.intellij.diagnostic.*
|
||||||
import com.intellij.ide.*
|
import com.intellij.ide.*
|
||||||
import com.intellij.ide.bootstrap.*
|
import com.intellij.ide.bootstrap.*
|
||||||
import com.intellij.ide.gdpr.EndUserAgreement
|
|
||||||
import com.intellij.ide.instrument.WriteIntentLockInstrumenter
|
import com.intellij.ide.instrument.WriteIntentLockInstrumenter
|
||||||
import com.intellij.ide.plugins.PluginManagerCore
|
import com.intellij.ide.plugins.PluginManagerCore
|
||||||
import com.intellij.idea.*
|
import com.intellij.idea.*
|
||||||
@@ -22,10 +20,8 @@ import com.intellij.openapi.diagnostic.Logger
|
|||||||
import com.intellij.openapi.diagnostic.getOrLogException
|
import com.intellij.openapi.diagnostic.getOrLogException
|
||||||
import com.intellij.openapi.diagnostic.logger
|
import com.intellij.openapi.diagnostic.logger
|
||||||
import com.intellij.openapi.util.Disposer
|
import com.intellij.openapi.util.Disposer
|
||||||
import com.intellij.openapi.util.IconLoader
|
|
||||||
import com.intellij.openapi.util.ShutDownTracker
|
import com.intellij.openapi.util.ShutDownTracker
|
||||||
import com.intellij.openapi.util.SystemInfoRt
|
import com.intellij.openapi.util.SystemInfoRt
|
||||||
import com.intellij.openapi.util.registry.EarlyAccessRegistryManager
|
|
||||||
import com.intellij.platform.diagnostic.telemetry.impl.OpenTelemetryConfigurator
|
import com.intellij.platform.diagnostic.telemetry.impl.OpenTelemetryConfigurator
|
||||||
import com.intellij.platform.diagnostic.telemetry.impl.TelemetryManagerImpl
|
import com.intellij.platform.diagnostic.telemetry.impl.TelemetryManagerImpl
|
||||||
import com.intellij.platform.diagnostic.telemetry.impl.span
|
import com.intellij.platform.diagnostic.telemetry.impl.span
|
||||||
@@ -41,7 +37,6 @@ import com.intellij.util.lang.ZipFilePool
|
|||||||
import com.jetbrains.JBR
|
import com.jetbrains.JBR
|
||||||
import io.opentelemetry.sdk.OpenTelemetrySdkBuilder
|
import io.opentelemetry.sdk.OpenTelemetrySdkBuilder
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.CancellationException
|
|
||||||
import org.jetbrains.annotations.ApiStatus.Internal
|
import org.jetbrains.annotations.ApiStatus.Internal
|
||||||
import java.awt.Toolkit
|
import java.awt.Toolkit
|
||||||
import java.lang.invoke.MethodHandles
|
import java.lang.invoke.MethodHandles
|
||||||
@@ -58,7 +53,6 @@ import java.util.function.BiConsumer
|
|||||||
import java.util.function.BiFunction
|
import java.util.function.BiFunction
|
||||||
import java.util.logging.ConsoleHandler
|
import java.util.logging.ConsoleHandler
|
||||||
import java.util.logging.Level
|
import java.util.logging.Level
|
||||||
import kotlin.concurrent.Volatile
|
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
internal const val IDE_STARTED: String = "------------------------------------------------------ IDE STARTED ------------------------------------------------------"
|
internal const val IDE_STARTED: String = "------------------------------------------------------ IDE STARTED ------------------------------------------------------"
|
||||||
@@ -89,6 +83,10 @@ private val commandProcessor: AtomicReference<(List<String>) -> Deferred<CliResu
|
|||||||
internal var shellEnvDeferred: Deferred<Boolean?>? = null
|
internal var shellEnvDeferred: Deferred<Boolean?>? = null
|
||||||
private set
|
private set
|
||||||
|
|
||||||
|
@Volatile
|
||||||
|
@JvmField
|
||||||
|
internal var isInitialStart: CompletableDeferred<Boolean>? = null
|
||||||
|
|
||||||
// the main thread's dispatcher is sequential - use it with care
|
// the main thread's dispatcher is sequential - use it with care
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
fun CoroutineScope.startApplication(args: List<String>,
|
fun CoroutineScope.startApplication(args: List<String>,
|
||||||
@@ -133,6 +131,7 @@ fun CoroutineScope.startApplication(args: List<String>,
|
|||||||
val initBaseLafJob = launch {
|
val initBaseLafJob = launch {
|
||||||
initUi(initAwtToolkitJob = initAwtToolkitJob, isHeadless = isHeadless, asyncScope = this@startApplication)
|
initUi(initAwtToolkitJob = initAwtToolkitJob, isHeadless = isHeadless, asyncScope = this@startApplication)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isHeadless) {
|
if (!isHeadless) {
|
||||||
val initUiScale = launch {
|
val initUiScale = launch {
|
||||||
if (SystemInfoRt.isMac) {
|
if (SystemInfoRt.isMac) {
|
||||||
@@ -147,7 +146,9 @@ fun CoroutineScope.startApplication(args: List<String>,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scheduleShowSplashIfNeeded(lockSystemDirsJob = lockSystemDirsJob, initUiScale = initUiScale, appInfoDeferred = appInfoDeferred,
|
scheduleShowSplashIfNeeded(lockSystemDirsJob = lockSystemDirsJob,
|
||||||
|
initUiScale = initUiScale,
|
||||||
|
appInfoDeferred = appInfoDeferred,
|
||||||
args = args)
|
args = args)
|
||||||
scheduleUpdateFrameClassAndWindowIconAndPreloadSystemFonts(initAwtToolkitJob = initAwtToolkitJob,
|
scheduleUpdateFrameClassAndWindowIconAndPreloadSystemFonts(initAwtToolkitJob = initAwtToolkitJob,
|
||||||
initUiScale = initUiScale,
|
initUiScale = initUiScale,
|
||||||
@@ -201,67 +202,24 @@ fun CoroutineScope.startApplication(args: List<String>,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scheduleLoadSystemLibsAndLogInfoAndInitMacApp(logDeferred, appInfoDeferred, initLafJob, args, mainScope)
|
scheduleLoadSystemLibsAndLogInfoAndInitMacApp(logDeferred = logDeferred,
|
||||||
|
appInfoDeferred = appInfoDeferred,
|
||||||
|
initUiDeferred = initLafJob,
|
||||||
|
args = args,
|
||||||
|
mainScope = mainScope)
|
||||||
|
|
||||||
val euaDocumentDeferred = async { loadEuaDocument(appInfoDeferred) }
|
val euaDocumentDeferred = async { loadEuaDocument(appInfoDeferred) }
|
||||||
|
|
||||||
val configImportDeferred: Deferred<Job?> = async {
|
val configImportDeferred: Deferred<Job?> = async {
|
||||||
if (isHeadless) {
|
importConfigIfNeeded(isHeadless = isHeadless,
|
||||||
if (!configImportNeededDeferred.await()) {
|
configImportNeededDeferred = configImportNeededDeferred,
|
||||||
return@async null
|
lockSystemDirsJob = lockSystemDirsJob,
|
||||||
}
|
logDeferred = logDeferred,
|
||||||
// make sure we lock the dir before writing
|
|
||||||
lockSystemDirsJob.join()
|
|
||||||
if (!ConfigImportHelper.isHeadlessAutomaticConfigImportAllowed()) {
|
|
||||||
enableNewUi(logDeferred)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
val log = logDeferred.await()
|
|
||||||
importConfig(
|
|
||||||
args = args,
|
args = args,
|
||||||
targetDirectoryToImportConfig = targetDirectoryToImportConfig ?: PathManager.getConfigDir(),
|
targetDirectoryToImportConfig = targetDirectoryToImportConfig,
|
||||||
log = log,
|
appStarterDeferred = appStarterDeferred,
|
||||||
appStarter = appStarterDeferred.await(),
|
|
||||||
euaDocumentDeferred = euaDocumentDeferred,
|
euaDocumentDeferred = euaDocumentDeferred,
|
||||||
headlessAutoImport = true
|
initLafJob =initLafJob)
|
||||||
)
|
|
||||||
if (ConfigImportHelper.isNewUser()) {
|
|
||||||
enableNewUi(logDeferred)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return@async null
|
|
||||||
}
|
|
||||||
|
|
||||||
if (AppMode.isRemoteDevHost() || !configImportNeededDeferred.await()) {
|
|
||||||
return@async null
|
|
||||||
}
|
|
||||||
|
|
||||||
initLafJob.join()
|
|
||||||
val log = logDeferred.await()
|
|
||||||
importConfig(
|
|
||||||
args = args,
|
|
||||||
targetDirectoryToImportConfig = targetDirectoryToImportConfig ?: PathManager.getConfigDir(),
|
|
||||||
log = log,
|
|
||||||
appStarter = appStarterDeferred.await(),
|
|
||||||
euaDocumentDeferred = euaDocumentDeferred,
|
|
||||||
)
|
|
||||||
|
|
||||||
if (ConfigImportHelper.isNewUser()) {
|
|
||||||
enableNewUi(logDeferred)
|
|
||||||
|
|
||||||
if (isIdeStartupWizardEnabled) {
|
|
||||||
log.info("Will enter initial app wizard flow.")
|
|
||||||
val result = CompletableDeferred<Boolean>()
|
|
||||||
isInitialStart = result
|
|
||||||
result
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val pluginSetDeferred = async {
|
val pluginSetDeferred = async {
|
||||||
@@ -295,7 +253,7 @@ fun CoroutineScope.startApplication(args: List<String>,
|
|||||||
Class.forName(OpenTelemetrySdkBuilder::class.java.name, true, classLoader)
|
Class.forName(OpenTelemetrySdkBuilder::class.java.name, true, classLoader)
|
||||||
}
|
}
|
||||||
|
|
||||||
val appLoaded = launch {
|
val appLoaded = async {
|
||||||
val initEventQueueJob = scheduleInitIdeEventQueue(initAwtToolkit = initAwtToolkitJob, isHeadless = isHeadless)
|
val initEventQueueJob = scheduleInitIdeEventQueue(initAwtToolkit = initAwtToolkitJob, isHeadless = isHeadless)
|
||||||
|
|
||||||
checkSystemDirJob.join()
|
checkSystemDirJob.join()
|
||||||
@@ -312,7 +270,7 @@ fun CoroutineScope.startApplication(args: List<String>,
|
|||||||
ApplicationImpl(CoroutineScope(mainScope.coroutineContext.job).namedChildScope("Application"), isInternal)
|
ApplicationImpl(CoroutineScope(mainScope.coroutineContext.job).namedChildScope("Application"), isInternal)
|
||||||
}
|
}
|
||||||
|
|
||||||
val starter = loadApp(app = app,
|
loadApp(app = app,
|
||||||
initAwtToolkitAndEventQueueJob = initEventQueueJob,
|
initAwtToolkitAndEventQueueJob = initEventQueueJob,
|
||||||
pluginSetDeferred = pluginSetDeferred,
|
pluginSetDeferred = pluginSetDeferred,
|
||||||
appInfoDeferred = appInfoDeferred,
|
appInfoDeferred = appInfoDeferred,
|
||||||
@@ -322,25 +280,7 @@ fun CoroutineScope.startApplication(args: List<String>,
|
|||||||
logDeferred = logDeferred,
|
logDeferred = logDeferred,
|
||||||
appRegisteredJob = appRegisteredJob,
|
appRegisteredJob = appRegisteredJob,
|
||||||
args = args.filterNot { CommandLineArgs.isKnownArgument(it) })
|
args = args.filterNot { CommandLineArgs.isKnownArgument(it) })
|
||||||
// out of appLoaded scope
|
|
||||||
this@startApplication.launch {
|
|
||||||
val isInitialStart = configImportDeferred.await()
|
|
||||||
// appLoaded not only provides starter, but also loads app, that's why it is here
|
|
||||||
IdeStartupWizardCollector.logExperimentState()
|
|
||||||
if (isInitialStart != null) {
|
|
||||||
LoadingState.compareAndSetCurrentState(LoadingState.COMPONENTS_LOADED, LoadingState.APP_READY)
|
|
||||||
val log = logDeferred.await()
|
|
||||||
runCatching {
|
|
||||||
span("startup wizard run") {
|
|
||||||
runStartupWizard(isInitialStart = isInitialStart, app = ApplicationManager.getApplication())
|
|
||||||
}
|
}
|
||||||
}.getOrLogException(log)
|
|
||||||
}
|
|
||||||
executeApplicationStarter(starter = starter, args = args)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
scheduleEnableCoroutineDumpAndJstack()
|
|
||||||
|
|
||||||
launch {
|
launch {
|
||||||
// required for appStarter.prepareStart
|
// required for appStarter.prepareStart
|
||||||
@@ -362,52 +302,31 @@ fun CoroutineScope.startApplication(args: List<String>,
|
|||||||
appStarter.start(InitAppContext(appRegistered = appRegisteredJob, appLoaded = appLoaded))
|
appStarter.start(InitAppContext(appRegistered = appRegisteredJob, appLoaded = appLoaded))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun enableNewUi(logDeferred: Deferred<Logger>) {
|
|
||||||
if (System.getProperty("ide.experimental.ui") == null) {
|
|
||||||
try {
|
|
||||||
EarlyAccessRegistryManager.setAndFlush(mapOf("ide.experimental.ui" to "true"))
|
|
||||||
}
|
|
||||||
catch (e: CancellationException) {
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
catch (e: Throwable) {
|
|
||||||
logDeferred.await().error(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Volatile
|
|
||||||
@JvmField
|
|
||||||
internal var isInitialStart: CompletableDeferred<Boolean>? = null
|
|
||||||
|
|
||||||
private fun CoroutineScope.scheduleEnableCoroutineDumpAndJstack() {
|
|
||||||
if (!System.getProperty("idea.enable.coroutine.dump", "true").toBoolean()) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// out of appLoaded scope
|
||||||
launch {
|
launch {
|
||||||
span("coroutine debug probes init") {
|
// starter is used later, but we need to wait for appLoaded completion
|
||||||
try {
|
val starter = appLoaded.await()
|
||||||
enableCoroutineDump()
|
|
||||||
}
|
val isInitialStart = configImportDeferred.await()
|
||||||
catch (ignore: NoClassDefFoundError) {
|
// appLoaded not only provides starter, but also loads app, that's why it is here
|
||||||
// if for some reason, the class loader has ByteBuddy in the classpath
|
launch {
|
||||||
// (it is an error, and should be fixed - our dev mode and production behaves correctly)
|
if (ConfigImportHelper.isFirstSession()) {
|
||||||
}
|
IdeStartupWizardCollector.logExperimentState()
|
||||||
catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
span("coroutine jstack configuration") {
|
|
||||||
JBR.getJstack()?.includeInfoFrom {
|
if (isInitialStart != null) {
|
||||||
"""
|
LoadingState.compareAndSetCurrentState(LoadingState.COMPONENTS_LOADED, LoadingState.APP_READY)
|
||||||
$COROUTINE_DUMP_HEADER
|
val log = logDeferred.await()
|
||||||
${dumpCoroutines(stripDump = false)}
|
runCatching {
|
||||||
"""
|
span("startup wizard run") {
|
||||||
|
runStartupWizard(isInitialStart = isInitialStart, app = ApplicationManager.getApplication())
|
||||||
}
|
}
|
||||||
|
}.getOrLogException(log)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
executeApplicationStarter(starter = starter, args = args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -474,8 +393,9 @@ private fun CoroutineScope.scheduleLoadSystemLibsAndLogInfoAndInitMacApp(logDefe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun processWindowsLauncherCommandLine(currentDirectory: String, args: Array<String>): Int =
|
fun processWindowsLauncherCommandLine(currentDirectory: String, args: Array<String>): Int {
|
||||||
EXTERNAL_LISTENER.apply(currentDirectory, args)
|
return EXTERNAL_LISTENER.apply(currentDirectory, args)
|
||||||
|
}
|
||||||
|
|
||||||
@get:Internal
|
@get:Internal
|
||||||
val isImplicitReadOnEDTDisabled: Boolean
|
val isImplicitReadOnEDTDisabled: Boolean
|
||||||
@@ -504,48 +424,6 @@ private suspend fun runPreAppClass(args: List<String>, classBeforeAppProperty: S
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun importConfig(args: List<String>,
|
|
||||||
targetDirectoryToImportConfig: Path,
|
|
||||||
log: Logger,
|
|
||||||
appStarter: AppStarter,
|
|
||||||
euaDocumentDeferred: Deferred<EndUserAgreement.Document?>,
|
|
||||||
headlessAutoImport: Boolean = false) {
|
|
||||||
if (headlessAutoImport) {
|
|
||||||
// headless AppStarters are not notified about config import
|
|
||||||
val veryFirstStartOnThisComputer = euaDocumentDeferred.await() != null
|
|
||||||
withContext(RawSwingDispatcher) {
|
|
||||||
try {
|
|
||||||
ConfigImportHelper.importConfigsTo(veryFirstStartOnThisComputer, targetDirectoryToImportConfig, args, log, true)
|
|
||||||
log.info("Automatic config import completed")
|
|
||||||
}
|
|
||||||
catch (e: UnsupportedOperationException) {
|
|
||||||
log.info("Automatic config import is not possible", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
EarlyAccessRegistryManager.invalidate()
|
|
||||||
IconLoader.clearCache()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
span("screen reader checking") {
|
|
||||||
runCatching {
|
|
||||||
enableScreenReaderSupportIfNecessary()
|
|
||||||
}.getOrLogException(log)
|
|
||||||
}
|
|
||||||
|
|
||||||
span("config importing") {
|
|
||||||
appStarter.beforeImportConfigs()
|
|
||||||
|
|
||||||
val veryFirstStartOnThisComputer = euaDocumentDeferred.await() != null
|
|
||||||
withContext(RawSwingDispatcher) {
|
|
||||||
ConfigImportHelper.importConfigsTo(veryFirstStartOnThisComputer, targetDirectoryToImportConfig, args, log)
|
|
||||||
}
|
|
||||||
appStarter.importFinished(targetDirectoryToImportConfig)
|
|
||||||
EarlyAccessRegistryManager.invalidate()
|
|
||||||
IconLoader.clearCache()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun CoroutineScope.configureJavaUtilLogging(): Job {
|
private fun CoroutineScope.configureJavaUtilLogging(): Job {
|
||||||
return launch(CoroutineName("console logger configuration")) {
|
return launch(CoroutineName("console logger configuration")) {
|
||||||
val rootLogger = java.util.logging.Logger.getLogger("")
|
val rootLogger = java.util.logging.Logger.getLogger("")
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ fun isCoroutineDumpHeader(line: String): Boolean {
|
|||||||
return line == COROUTINE_DUMP_HEADER || line == COROUTINE_DUMP_HEADER_STRIPPED
|
return line == COROUTINE_DUMP_HEADER || line == COROUTINE_DUMP_HEADER_STRIPPED
|
||||||
}
|
}
|
||||||
|
|
||||||
fun enableCoroutineDump() {
|
fun enableCoroutineDump(): Result<Unit> {
|
||||||
runCatching {
|
return runCatching {
|
||||||
DebugProbes.enableCreationStackTraces = false
|
DebugProbes.enableCreationStackTraces = false
|
||||||
DebugProbes.install()
|
DebugProbes.install()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user