From 1936efed119e6626ca210d7627ebd24b7079f442 Mon Sep 17 00:00:00 2001 From: Sebastian Sellmair Date: Fri, 10 Feb 2023 15:35:41 +0100 Subject: [PATCH] [MPP] NativeDistributionCommonizerLock: Support classpath isolation ^KT-56510 Verification Pending --- .../NativeDistributionCommonizerLock.kt | 35 +++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/NativeDistributionCommonizerLock.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/NativeDistributionCommonizerLock.kt index 8eed0c00e29..073e34d5f38 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/NativeDistributionCommonizerLock.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/NativeDistributionCommonizerLock.kt @@ -7,10 +7,13 @@ package org.jetbrains.kotlin.gradle.targets.native.internal import java.io.File import java.io.FileOutputStream +import java.nio.channels.FileChannel +import java.nio.channels.FileLock +import java.nio.channels.OverlappingFileLockException import java.util.concurrent.locks.ReentrantLock import kotlin.concurrent.withLock -internal class NativeDistributionCommonizerLock( +internal class NativeDistributionCommonizerLock @JvmOverloads constructor( private val outputDirectory: File, private val logInfo: (message: String) -> Unit = {} ) { @@ -32,7 +35,7 @@ internal class NativeDistributionCommonizerLock( outputDirectory.mkdirs() logInfo("Acquire lock: ${lockFile.path} ...") FileOutputStream(outputDirectory.resolve(".lock")).use { stream -> - val lock = stream.channel.lock() + val lock: FileLock = stream.channel.lockWithRetries(lockFile) assert(lock.isValid) return try { logInfo("Lock acquired: ${lockFile.path}") @@ -47,6 +50,34 @@ internal class NativeDistributionCommonizerLock( } } + private fun FileChannel.lockWithRetries(file: File): FileLock { + var retries = 0 + while (true) { + try { + return lock() + } + /* + Catching the OverlappingFileLockException which is caused by the same jvm (process) already having locked the file. + Since we do use a static re-entrant lock as a monitor to the cache, this can only happen + when this code is running in the same JVM but with in complete isolation + (e.g. Gradle classpath isolation, or composite builds). + + If we detect this case, we retry the locking after a short period, constantly logging that we're blocked + by some other thread using the cache. + + The risk of deadlocking here is low, since we can only get into this code path, *if* + the code is very isolated and somebody locked the file. + */ + catch (t: OverlappingFileLockException) { + Thread.sleep(25) + retries++ + if (retries % 10 == 0) { + logInfo("Waiting to acquire lock: $file") + } + } + } + } + fun checkLocked(outputDirectory: File) { check(intraProcessLock.isHeldByCurrentThread) { "Expected lock to be held by current thread ${Thread.currentThread().name}"