[Gradle] NativeDistributionCommonizationCache: Acquire FileLock
A `.lock` file will be acquired before running the native distribution commonizer. This is done to protect data corruption when multiple process try to commonize at the same time. This could happen when e.g. two new projects are opened that both trigger the commonizer during syncing. ^KT-46343 Verification Pending
This commit is contained in:
committed by
Space
parent
fb1eac0985
commit
21f7e16ee6
+2
@@ -43,6 +43,8 @@ class CommonizerIT : BaseGradleIT() {
|
|||||||
assertTasksExecuted(":commonizeNativeDistribution")
|
assertTasksExecuted(":commonizeNativeDistribution")
|
||||||
assertContains("Native Distribution Commonization: Cache hit")
|
assertContains("Native Distribution Commonization: Cache hit")
|
||||||
assertContains("Native Distribution Commonization: All available targets are commonized already")
|
assertContains("Native Distribution Commonization: All available targets are commonized already")
|
||||||
|
assertContains("Native Distribution Commonization: Lock acquired")
|
||||||
|
assertContains("Native Distribution Commonization: Lock released")
|
||||||
assertNotContains(commonizerOutput)
|
assertNotContains(commonizerOutput)
|
||||||
assertSuccessful()
|
assertSuccessful()
|
||||||
}
|
}
|
||||||
|
|||||||
+43
-22
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.commonizer.*
|
|||||||
import org.jetbrains.kotlin.commonizer.CommonizerOutputFileLayout.resolveCommonizedDirectory
|
import org.jetbrains.kotlin.commonizer.CommonizerOutputFileLayout.resolveCommonizedDirectory
|
||||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
|
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
|
||||||
internal val Project.isNativeDistributionCommonizationCacheEnabled: Boolean
|
internal val Project.isNativeDistributionCommonizationCacheEnabled: Boolean
|
||||||
get() = PropertiesProvider(this).enableNativeDistributionCommonizationCache
|
get() = PropertiesProvider(this).enableNativeDistributionCommonizationCache
|
||||||
@@ -21,6 +22,7 @@ internal class NativeDistributionCommonizationCache(
|
|||||||
private val commonizer: NativeDistributionCommonizer
|
private val commonizer: NativeDistributionCommonizer
|
||||||
) : NativeDistributionCommonizer {
|
) : NativeDistributionCommonizer {
|
||||||
|
|
||||||
|
|
||||||
override fun commonizeNativeDistribution(
|
override fun commonizeNativeDistribution(
|
||||||
konanHome: File,
|
konanHome: File,
|
||||||
outputDirectory: File,
|
outputDirectory: File,
|
||||||
@@ -31,31 +33,33 @@ internal class NativeDistributionCommonizationCache(
|
|||||||
logInfo("Cache disabled")
|
logInfo("Cache disabled")
|
||||||
}
|
}
|
||||||
|
|
||||||
val cachedOutputTargets = outputTargets
|
withLock(outputDirectory) {
|
||||||
.filter { outputTarget -> isCached(resolveCommonizedDirectory(outputDirectory, outputTarget)) }
|
val cachedOutputTargets = outputTargets
|
||||||
.onEach { outputTarget -> logInfo("Cache hit: $outputTarget already commonized") }
|
.filter { outputTarget -> isCached(resolveCommonizedDirectory(outputDirectory, outputTarget)) }
|
||||||
.toSet()
|
.onEach { outputTarget -> logInfo("Cache hit: $outputTarget already commonized") }
|
||||||
|
.toSet()
|
||||||
|
|
||||||
val enqueuedOutputTargets = if (project.isNativeDistributionCommonizationCacheEnabled) outputTargets - cachedOutputTargets
|
val enqueuedOutputTargets = if (project.isNativeDistributionCommonizationCacheEnabled) outputTargets - cachedOutputTargets
|
||||||
else outputTargets
|
else outputTargets
|
||||||
|
|
||||||
if (canReturnFast(konanHome, enqueuedOutputTargets)) {
|
if (canReturnFast(konanHome, enqueuedOutputTargets)) {
|
||||||
logInfo("All available targets are commonized already - Nothing to do")
|
logInfo("All available targets are commonized already - Nothing to do")
|
||||||
return
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
enqueuedOutputTargets
|
||||||
|
.map { outputTarget -> resolveCommonizedDirectory(outputDirectory, outputTarget) }
|
||||||
|
.forEach { commonizedDirectory -> if (commonizedDirectory.exists()) commonizedDirectory.deleteRecursively() }
|
||||||
|
|
||||||
|
commonizer.commonizeNativeDistribution(
|
||||||
|
konanHome, outputDirectory, enqueuedOutputTargets, logLevel
|
||||||
|
)
|
||||||
|
|
||||||
|
enqueuedOutputTargets
|
||||||
|
.map { outputTarget -> resolveCommonizedDirectory(outputDirectory, outputTarget) }
|
||||||
|
.filter { commonizedDirectory -> commonizedDirectory.isDirectory }
|
||||||
|
.forEach { commonizedDirectory -> commonizedDirectory.resolve(".success").createNewFile() }
|
||||||
}
|
}
|
||||||
|
|
||||||
enqueuedOutputTargets
|
|
||||||
.map { outputTarget -> resolveCommonizedDirectory(outputDirectory, outputTarget) }
|
|
||||||
.forEach { commonizedDirectory -> if (commonizedDirectory.exists()) commonizedDirectory.deleteRecursively() }
|
|
||||||
|
|
||||||
commonizer.commonizeNativeDistribution(
|
|
||||||
konanHome, outputDirectory, enqueuedOutputTargets, logLevel
|
|
||||||
)
|
|
||||||
|
|
||||||
enqueuedOutputTargets
|
|
||||||
.map { outputTarget -> resolveCommonizedDirectory(outputDirectory, outputTarget) }
|
|
||||||
.filter { commonizedDirectory -> commonizedDirectory.isDirectory }
|
|
||||||
.forEach { commonizedDirectory -> commonizedDirectory.resolve(".success").createNewFile() }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isCached(directory: File): Boolean {
|
private fun isCached(directory: File): Boolean {
|
||||||
@@ -76,9 +80,26 @@ internal class NativeDistributionCommonizationCache(
|
|||||||
.none { platformLibsDir -> platformLibsDir.exists() }
|
.none { platformLibsDir -> platformLibsDir.exists() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private inline fun <T> withLock(outputDirectory: File, action: () -> T): T {
|
||||||
|
val lockfile = outputDirectory.resolve(".lock")
|
||||||
|
logInfo("Acquire lock: ${lockfile.path} ...")
|
||||||
|
FileOutputStream(outputDirectory.resolve(".lock")).use { stream ->
|
||||||
|
val lock = stream.channel.lock()
|
||||||
|
assert(lock.isValid)
|
||||||
|
return try {
|
||||||
|
logInfo("Lock acquired: ${lockfile.path}")
|
||||||
|
action()
|
||||||
|
} finally {
|
||||||
|
lock.release()
|
||||||
|
logInfo("Lock released: ${lockfile.path}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun logInfo(message: String) = project.logger.info("${Logging.prefix}: $message")
|
private fun logInfo(message: String) = project.logger.info("${Logging.prefix}: $message")
|
||||||
|
|
||||||
private object Logging {
|
private object Logging {
|
||||||
const val prefix = "Native Distribution Commonization"
|
const val prefix = "Native Distribution Commonization"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user