From 9a5739555c5f471bb6abf55de7b67853b58f04cd Mon Sep 17 00:00:00 2001 From: Anton Lakotka Date: Thu, 16 Jun 2022 12:13:14 +0200 Subject: [PATCH] [Gradle] Refactor NativeDistributionCommonizerCache to be GCC friendly Change NativeDistributionCommonizerCache from "decorator" pattern to be used as composition in related GradleTask. This allows to decouple cache from actual commonizer instance. Allowing it to be stored in Gradle Configuration Cache. Since Commonizer instance is created only during execution phase. Don't serialise `Lock` instance as Configuration Cache. Instead, reinstantiate it everytime after deserialization. --- ...t => NativeDistributionCommonizerCache.kt} | 74 ++++++++++--------- .../NativeDistributionCommonizerTask.kt | 40 +++++----- 2 files changed, 58 insertions(+), 56 deletions(-) rename libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/{NativeDistributionCommonizationCache.kt => NativeDistributionCommonizerCache.kt} (77%) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/NativeDistributionCommonizationCache.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/NativeDistributionCommonizerCache.kt similarity index 77% rename from libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/NativeDistributionCommonizationCache.kt rename to libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/NativeDistributionCommonizerCache.kt index 9bc5f7a75d1..732eab90e7f 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/NativeDistributionCommonizationCache.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/NativeDistributionCommonizerCache.kt @@ -12,37 +12,35 @@ import org.jetbrains.kotlin.commonizer.* import org.jetbrains.kotlin.commonizer.CommonizerOutputFileLayout.resolveCommonizedDirectory import java.io.File import java.io.FileOutputStream +import java.io.ObjectInputStream +import java.io.Serializable import java.util.concurrent.locks.ReentrantLock import kotlin.concurrent.withLock -internal class NativeDistributionCommonizationCache( +class NativeDistributionCommonizerCache( + private val outputDirectory: File, + private val konanHome: File, private val logger: Logger, - private val isCachingEnabled: Boolean, - private val commonizer: NativeDistributionCommonizer -) : NativeDistributionCommonizer { - + private val isCachingEnabled: Boolean +) : Serializable { fun isUpToDate( - konanHome: File, outputDirectory: File, outputTargets: Set - ): Boolean = lock.withLock(outputDirectory) { - todoTargets(konanHome, outputDirectory, outputTargets) + outputTargets: Set + ): Boolean = lock.withLock { + todoTargets(outputTargets) }.isEmpty() - override fun commonizeNativeDistribution( - konanHome: File, - outputDirectory: File, + /** + * Calls [writeCacheAction] for uncached targets and marks them as cached if it succeeds + */ + fun writeCacheForUncachedTargets( outputTargets: Set, - logLevel: CommonizerLogLevel, - additionalSettings: List>, - ): Unit = lock.withLock(outputDirectory) { - val todoOutputTargets = todoTargets(konanHome, outputDirectory, outputTargets) + writeCacheAction: (todoTargets: Set) -> Unit + ) = lock.withLock { + val todoOutputTargets = todoTargets(outputTargets) if (todoOutputTargets.isEmpty()) return@withLock - /* Invoke commonizer with only 'to do' targets */ - commonizer.commonizeNativeDistribution( - konanHome, outputDirectory, todoOutputTargets, logLevel, additionalSettings - ) + writeCacheAction(todoOutputTargets) - /* Mark targets as successfully commonized */ todoOutputTargets .map { outputTarget -> resolveCommonizedDirectory(outputDirectory, outputTarget) } .filter { commonizedDirectory -> commonizedDirectory.isDirectory } @@ -50,14 +48,14 @@ internal class NativeDistributionCommonizationCache( } private fun todoTargets( - konanHome: File, outputDirectory: File, outputTargets: Set + outputTargets: Set ): Set { lock.checkLocked(outputDirectory) logInfo("Calculating cache state for $outputTargets") if (!isCachingEnabled) { logInfo("Cache disabled") - return if (isMissingPlatformLibraries(konanHome, outputTargets)) return emptySet() + return if (isMissingPlatformLibraries(outputTargets)) return emptySet() else outputTargets } @@ -68,7 +66,7 @@ internal class NativeDistributionCommonizationCache( val todoOutputTargets = outputTargets - cachedOutputTargets - if (todoOutputTargets.isEmpty() || isMissingPlatformLibraries(konanHome, todoOutputTargets)) { + if (todoOutputTargets.isEmpty() || isMissingPlatformLibraries(todoOutputTargets)) { logInfo("All available targets are commonized already - Nothing to do") if (todoOutputTargets.isNotEmpty()) { logInfo("Platforms cannot be commonized, because of missing platform libraries: $todoOutputTargets") @@ -80,13 +78,8 @@ internal class NativeDistributionCommonizationCache( return todoOutputTargets } - private fun isCached(directory: File): Boolean { - val successMarkerFile = directory.resolve(".success") - return successMarkerFile.isFile - } - private fun isMissingPlatformLibraries( - konanHome: File, missingOutputTargets: Set + missingOutputTargets: Set ): Boolean { // If all platform lib dirs are missing, we can also return fast from the cache without invoking // the commonizer @@ -96,15 +89,26 @@ internal class NativeDistributionCommonizationCache( .none { platformLibsDir -> platformLibsDir.exists() } } + private fun isCached(directory: File): Boolean { + val successMarkerFile = directory.resolve(".success") + return successMarkerFile.isFile + } + /** * Re-entrant lock implementation capable of locking a given output directory * even between multiple process (Gradle Daemons) */ - private val lock = object { + @Transient + private var lock = Lock() + + private fun logInfo(message: String) = + logger.info("Native Distribution Commonization: $message") + + private inner class Lock { private val reentrantLock = ReentrantLock() private val lockedOutputDirectories = mutableSetOf() - fun withLock(outputDirectory: File, action: () -> T): T { + fun withLock(action: () -> T): T { /* Enter intra-process wide lock */ reentrantLock.withLock { if (outputDirectory in lockedOutputDirectories) { @@ -143,6 +147,8 @@ internal class NativeDistributionCommonizationCache( } } - private fun logInfo(message: String) = - logger.info("Native Distribution Commonization: $message") -} + private fun readObject(input: ObjectInputStream) { + input.defaultReadObject() + lock = Lock() + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/NativeDistributionCommonizerTask.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/NativeDistributionCommonizerTask.kt index 8393c1180a7..aaf09b4cd3b 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/NativeDistributionCommonizerTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/NativeDistributionCommonizerTask.kt @@ -39,22 +39,25 @@ internal open class NativeDistributionCommonizerTask private val runnerSettings = KotlinNativeCommonizerToolRunner.Settings(project) - private val isCachingEnabled = project.kotlinPropertiesProvider.enableNativeDistributionCommonizationCache - private val logLevel = project.commonizerLogLevel private val additionalSettings = project.additionalCommonizerSettings - private val kotlinVersion by lazy { project.getKotlinPluginVersion() } - @get:Internal internal val rootOutputDirectory: File = project.file { - project.file(konanHome) + project.file(project.konanHome) .resolve(KONAN_DISTRIBUTION_KLIB_DIR) .resolve(KONAN_DISTRIBUTION_COMMONIZED_LIBS_DIR) .resolve(URLEncoder.encode(project.getKotlinPluginVersion(), Charsets.UTF_8.name())) } + private val commonizerCache = NativeDistributionCommonizerCache( + outputDirectory = rootOutputDirectory, + konanHome = konanHome, + logger = logger, + isCachingEnabled = project.kotlinPropertiesProvider.enableNativeDistributionCommonizationCache + ) + @TaskAction protected fun run() { val commonizerRunner = KotlinNativeCommonizerToolRunner( @@ -62,27 +65,20 @@ internal open class NativeDistributionCommonizerTask settings = runnerSettings ) - val commonizer = NativeDistributionCommonizationCache( - isCachingEnabled = isCachingEnabled, - logger = logger, - commonizer = GradleCliCommonizer(commonizerRunner) - ) - - commonizer.commonizeNativeDistribution( - konanHome = konanHome, - outputDirectory = rootOutputDirectory, - outputTargets = commonizerTargets, - logLevel = logLevel, - additionalSettings = additionalSettings, - ) + commonizerCache.writeCacheForUncachedTargets(commonizerTargets) { todoOutputTargets -> + val commonizer = GradleCliCommonizer(commonizerRunner) + /* Invoke commonizer with only 'to do' targets */ + commonizer.commonizeNativeDistribution( + konanHome, rootOutputDirectory, todoOutputTargets, logLevel, additionalSettings + ) + } } init { project.registerCommonizerClasspathConfigurationIfNecessary() - // TODO(alakotka): Support upToDate checks -// outputs.upToDateWhen { -// commonizer.isUpToDate(konanHome, rootOutputDirectory, commonizerTargets) -// } + outputs.upToDateWhen { + commonizerCache.isUpToDate(commonizerTargets) + } } }