From 8ceee3e8b925fc4e5a2e43c8dde01f18a49e29a9 Mon Sep 17 00:00:00 2001 From: "Alexander.Likhachev" Date: Tue, 21 Nov 2023 09:35:37 +0100 Subject: [PATCH] [Daemon] Improve daemon connection algorithm Before this change, the logic was to find the most suitable daemon and try connecting to it in a loop, ignoring the fact that it could be dying. Now, instead of trying to connect to the daemon dying daemon in a loop, we will make only 1 attemp and then skip if it's already dying. ^KT-55322 In Progress --- .../daemon/daemon-client/build.gradle.kts | 14 +--- .../daemon/client/KotlinCompilerClient.kt | 79 ++++++++++++------- .../kotlin/daemon/common/CompileService.kt | 2 +- .../kotlin/daemon/LastSessionDaemonTest.kt | 2 - 4 files changed, 52 insertions(+), 45 deletions(-) diff --git a/compiler/daemon/daemon-client/build.gradle.kts b/compiler/daemon/daemon-client/build.gradle.kts index f6714c01d04..dad79aa026d 100644 --- a/compiler/daemon/daemon-client/build.gradle.kts +++ b/compiler/daemon/daemon-client/build.gradle.kts @@ -1,6 +1,3 @@ -import org.jetbrains.kotlin.gradle.dsl.KotlinVersion -import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask - description = "Kotlin Daemon Client" plugins { @@ -32,16 +29,7 @@ dependencies { } } -tasks.withType> { - compilerOptions { - // This module is being run from within Gradle, older versions of which only have older kotlin-stdlib in the runtime classpath. - @Suppress("DEPRECATION") - apiVersion.set(KotlinVersion.KOTLIN_1_4) - @Suppress("DEPRECATION") - languageVersion.set(KotlinVersion.KOTLIN_1_4) - freeCompilerArgs.add("-Xsuppress-version-warnings") - } -} +configureKotlinCompileTasksGradleCompatibility() sourceSets { "main" { projectDefault() } diff --git a/compiler/daemon/daemon-client/src/org/jetbrains/kotlin/daemon/client/KotlinCompilerClient.kt b/compiler/daemon/daemon-client/src/org/jetbrains/kotlin/daemon/client/KotlinCompilerClient.kt index 1fd0b7a0c30..26dc31825ae 100644 --- a/compiler/daemon/daemon-client/src/org/jetbrains/kotlin/daemon/client/KotlinCompilerClient.kt +++ b/compiler/daemon/daemon-client/src/org/jetbrains/kotlin/daemon/client/KotlinCompilerClient.kt @@ -101,36 +101,45 @@ object KotlinCompilerClient { autostart: Boolean, leaseSession: Boolean, sessionAliveFlagFile: File? = null, - ): CompileServiceSession? = connectLoop(reportingTargets, autostart) { isLastAttempt -> + ): CompileServiceSession? { + val ignoredDaemonSessionFiles = mutableSetOf() + return connectLoop(reportingTargets, autostart) { isLastAttempt -> - fun CompileService.leaseImpl(): CompileServiceSession? { - // the newJVMOptions could be checked here for additional parameters, if needed - registerClient(clientAliveFlagFile.absolutePath) - reportingTargets.report(DaemonReportCategory.DEBUG, "connected to the daemon") + fun CompileService.tryToLeaseSession(): CompileServiceSession? { + // the newJVMOptions could be checked here for additional parameters, if needed + registerClient(clientAliveFlagFile.absolutePath) + reportingTargets.report(DaemonReportCategory.DEBUG, "connected to the daemon") - if (!leaseSession) return CompileServiceSession(this, CompileService.NO_SESSION) + if (!leaseSession) return CompileServiceSession(this, CompileService.NO_SESSION) - return leaseCompileSession(sessionAliveFlagFile?.absolutePath).takeUnless { it is CompileService.CallResult.Dying }?.let { - CompileServiceSession(this, it.get()) - } - } - - ensureServerHostnameIsSetUp() - val (service, newJVMOptions) = tryFindSuitableDaemonOrNewOpts( - File(daemonOptions.runFilesPath), - compilerId, - daemonJVMOptions - ) { cat, msg -> reportingTargets.report(cat, msg) } - - if (service != null) { - service.leaseImpl() - } else { - if (!isLastAttempt && autostart) { - if (startDaemon(compilerId, newJVMOptions, daemonOptions, reportingTargets)) { - reportingTargets.report(DaemonReportCategory.DEBUG, "new daemon started, trying to find it") + return leaseCompileSession(sessionAliveFlagFile?.absolutePath).takeUnless { it is CompileService.CallResult.Dying }?.let { + CompileServiceSession(this, it.get()) + } + } + + ensureServerHostnameIsSetUp() + val result = tryFindSuitableDaemonOrNewOpts( + File(daemonOptions.runFilesPath), + compilerId, + daemonJVMOptions, + ignoredDaemonSessionFiles, + ) { cat, msg -> reportingTargets.report(cat, msg) } + + when (result) { + is DaemonSearchResult.Found -> result.compileService.tryToLeaseSession().also { + // the null value here means that the daemon is already dying, + // so we shall query other daemons or start a new one + if (it == null) ignoredDaemonSessionFiles.add(result.runFileMarker) + } + is DaemonSearchResult.NotFound -> { + if (!isLastAttempt && autostart) { + if (startDaemon(compilerId, result.requiredJvmOptions, daemonOptions, reportingTargets)) { + reportingTargets.report(DaemonReportCategory.DEBUG, "new daemon started, trying to find it") + } + } + null } } - null } } @@ -384,16 +393,28 @@ object KotlinCompilerClient { return null } + private sealed interface DaemonSearchResult { + class Found( + val compileService: CompileService, + val runFileMarker: File, + ) : DaemonSearchResult + + class NotFound( + val requiredJvmOptions: DaemonJVMOptions, + ) : DaemonSearchResult + } + private fun tryFindSuitableDaemonOrNewOpts( registryDir: File, compilerId: CompilerId, daemonJVMOptions: DaemonJVMOptions, + ignoredDaemonSessionFiles: Set, report: (DaemonReportCategory, String) -> Unit, - ): Pair { + ): DaemonSearchResult { registryDir.mkdirs() val timestampMarker = Files.createTempFile(registryDir.toPath(), "kotlin-daemon-client-tsmarker", null).toFile() val aliveWithMetadata = try { - walkDaemons(registryDir, compilerId, timestampMarker, report = report).toList() + walkDaemons(registryDir, compilerId, timestampMarker, report = report, filter = { file, _ -> file !in ignoredDaemonSessionFiles }).toList() } finally { timestampMarker.delete() } @@ -403,10 +424,10 @@ object KotlinCompilerClient { val optsCopy = daemonJVMOptions.copy() // if required options fit into fattest running daemon - return the daemon and required options with memory params set to actual ones in the daemon return aliveWithMetadata.maxWithOrNull(comparator)?.takeIf { daemonJVMOptions memorywiseFitsInto it.jvmOptions }?.let { - Pair(it.daemon, optsCopy.updateMemoryUpperBounds(it.jvmOptions)) + DaemonSearchResult.Found(it.daemon, it.runFile) } // else combine all options from running daemon to get fattest option for a new daemon to run - ?: Pair(null, aliveWithMetadata.fold(optsCopy) { opts, d -> opts.updateMemoryUpperBounds(d.jvmOptions) }) + ?: DaemonSearchResult.NotFound(aliveWithMetadata.fold(optsCopy) { opts, d -> opts.updateMemoryUpperBounds(d.jvmOptions) }) } diff --git a/compiler/daemon/daemon-common/src/org/jetbrains/kotlin/daemon/common/CompileService.kt b/compiler/daemon/daemon-common/src/org/jetbrains/kotlin/daemon/common/CompileService.kt index 13f8e7c3b8f..e4018ec6a7e 100644 --- a/compiler/daemon/daemon-common/src/org/jetbrains/kotlin/daemon/common/CompileService.kt +++ b/compiler/daemon/daemon-common/src/org/jetbrains/kotlin/daemon/common/CompileService.kt @@ -39,7 +39,7 @@ interface CompileService : Remote { } companion object { - val NO_SESSION: Int = 0 + const val NO_SESSION: Int = 0 } sealed class CallResult : Serializable { diff --git a/compiler/daemon/daemon-tests/test/org/jetbrains/kotlin/daemon/LastSessionDaemonTest.kt b/compiler/daemon/daemon-tests/test/org/jetbrains/kotlin/daemon/LastSessionDaemonTest.kt index 2e87265febb..825340da8e5 100644 --- a/compiler/daemon/daemon-tests/test/org/jetbrains/kotlin/daemon/LastSessionDaemonTest.kt +++ b/compiler/daemon/daemon-tests/test/org/jetbrains/kotlin/daemon/LastSessionDaemonTest.kt @@ -7,7 +7,6 @@ package org.jetbrains.kotlin.daemon import org.jetbrains.kotlin.daemon.client.KotlinCompilerClient import org.jetbrains.kotlin.daemon.common.CompileService -import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test import java.io.File @@ -36,7 +35,6 @@ class LastSessionDaemonTest : BaseDaemonSessionTest() { assertEquals(0, exitCode) } - @Disabled("KT-55322") @DisplayName("can lease a session when the daemon in the LastSession state") // either by starting a new daemon or returning it to the Alive state @Test fun canLeaseNewSession() {