fixup! [Gradle] Use locks to prevent concurrent modification problems in AbstractSetupTask
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BuildReportsIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BuildReportsIT.kt index cfe724b..883d42a 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BuildReportsIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BuildReportsIT.kt
@@ -20,8 +20,6 @@ import org.jetbrains.kotlin.gradle.util.replaceText import org.jetbrains.kotlin.test.TestMetadata import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.parallel.ResourceAccessMode.READ_WRITE -import org.junit.jupiter.api.parallel.ResourceLock import java.io.ObjectInputStream import java.nio.file.Files import java.nio.file.Path @@ -105,7 +103,6 @@ ) @GradleTest @TestMetadata("kotlin-js-plugin-project") - @ResourceLock("kgp-js node installation", mode = READ_WRITE) fun testBuildMetricsForJsProject(gradleVersion: GradleVersion) { testBuildReportInFile( "kotlin-js-plugin-project",
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/AbstractSetupTask.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/AbstractSetupTask.kt index 612b4fe..3bd274e 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/AbstractSetupTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/AbstractSetupTask.kt
@@ -17,10 +17,12 @@ import org.jetbrains.kotlin.gradle.utils.getFile import org.jetbrains.kotlin.gradle.utils.mapOrNull import java.io.File +import java.io.RandomAccessFile import java.net.URI -import java.nio.file.Path import javax.inject.Inject -import kotlin.io.path.* +import kotlin.io.path.createDirectories +import kotlin.io.path.createFile +import kotlin.io.path.exists @DisableCachingByDefault abstract class AbstractSetupTask<Env : AbstractEnv, Spec : EnvSpec<Env>>( @@ -190,8 +192,8 @@ val currentHash = computeCurrentHash() val storedHash = - if (hashFile.fileSize() > 0) { - hashFile.readText().trim() + if (hashFile.length() > 0) { + hashFile.readLine().trim() } else { "<hashFile missing>" } @@ -211,7 +213,7 @@ val updatedHash = computeCurrentHash() ?: error("failed to compute hash. destination:$destination, dist:$dist.") - hashFile.writeText(updatedHash) + hashFile.writeUTF(updatedHash) } private fun computeCurrentHash(): String? { @@ -250,7 +252,7 @@ * Concurrent execution happens more often when running KGP integration tests locally, * since the tests are run in parallel with multiple Gradle versions. */ - private fun <T> runWithHashFileLock(action: (hashFile: Path) -> T): T { + private fun <T> runWithHashFileLock(action: (hashFile: RandomAccessFile) -> T): T { val hashFile = destinationHashFileProvider.get().asFile.toPath().apply { if (!exists()) { parent.createDirectories() @@ -258,8 +260,8 @@ } } - return exclusiveFileLock(hashFile) { - action(hashFile) + return exclusiveFileLock(hashFile) { lockedFile -> + action(lockedFile) } }
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/exclusiveFileLock.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/exclusiveFileLock.kt index 9f68df6..e53b536 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/exclusiveFileLock.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/exclusiveFileLock.kt
@@ -6,6 +6,7 @@ package org.jetbrains.kotlin.gradle.utils import java.io.RandomAccessFile +import java.nio.channels.FileChannel import java.nio.channels.OverlappingFileLockException import java.nio.file.Path import java.time.Instant @@ -36,7 +37,7 @@ internal fun <T> exclusiveFileLock( file: Path, lockTimeout: Duration = 5.seconds, - block: () -> T, + block: (file: RandomAccessFile) -> T, ): T { contract { callsInPlace(block, InvocationKind.AT_MOST_ONCE) } @@ -54,18 +55,12 @@ do { try { RandomAccessFile(file.toFile(), "rw").use { lockFileAccess -> - val fileLock = lockFileAccess.channel.tryLock( - 0, - 1, - // set shared=false to trigger OverlappingFileLockException - // if the lockfile is used by the same thread - false, - ) + val fileLock = lockFileAccess.channel.tryLock() if (fileLock != null) { fileLock.use { // Successfully acquired exclusive lock - return block() + return block(lockFileAccess) } } else { // Failed to acquire exclusive lock, already locked by another process