Fix synchronization when working with IC caches

IC caches could be modified and read from different threads.
In JPS builder these threads are RMI worker (invoked from Compiler
Daemon) and JPS worker thread. Proper synchronization fixes cases
when caches could become broken.
#KT-42265 Fixed
#KT-42194 Fixed
#KT-42265 Fixed
#KT-42937 Fixed
This commit is contained in:
Andrey Uskov
2020-11-12 17:25:18 +03:00
parent af95b8d1fe
commit 275a02ce88
10 changed files with 56 additions and 17 deletions
@@ -30,7 +30,12 @@ abstract class IncrementalCachesManager<PlatformCache : AbstractIncrementalCache
) {
val pathConverter = IncrementalFileToPathConverter(rootProjectDir)
private val caches = arrayListOf<BasicMapsOwner>()
var isClosed = false
@Synchronized
protected fun <T : BasicMapsOwner> T.registerCache() {
assert(!isClosed) { "Attempted to add new cache into closed storage." }
caches.add(this)
}
@@ -41,9 +46,12 @@ abstract class IncrementalCachesManager<PlatformCache : AbstractIncrementalCache
val lookupCache: LookupStorage = LookupStorage(lookupCacheDir, pathConverter).apply { registerCache() }
abstract val platformCache: PlatformCache
@Synchronized
fun close(flush: Boolean = false): Boolean {
if (isClosed) {
return true
}
var successful = true
for (cache in caches) {
if (flush) {
try {
@@ -62,6 +70,7 @@ abstract class IncrementalCachesManager<PlatformCache : AbstractIncrementalCache
}
}
isClosed = true
return successful
}
}
@@ -37,6 +37,7 @@ import org.jetbrains.kotlin.incremental.util.BufferingMessageCollector
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.progress.CompilationCanceledStatus
import java.io.File
import java.io.IOException
import java.util.*
abstract class IncrementalCompilerRunner<
@@ -97,7 +98,10 @@ abstract class IncrementalCompilerRunner<
val allKotlinFiles = allSourceFiles.filter { it.isKotlinFile(kotlinSourceFilesExtensions) }
return compileIncrementally(args, caches, allKotlinFiles, CompilationMode.Rebuild(reason), messageCollector)
}
// attempt IC
// if OK or failed compilation - return
// internal error - clear
// failed to close - clear
return try {
val changedFiles = providedChangedFiles ?: caches.inputsCache.sourceSnapshotMap.compareAndUpdate(allSourceFiles)
val compilationMode = sourcesToCompile(caches, changedFiles, args, messageCollector)
@@ -143,8 +147,8 @@ abstract class IncrementalCompilerRunner<
}
}
assert(!destinationDir.exists()) { "Could not delete destination dir $destinationDir" }
assert(!workingDir.exists()) { "Could not delete caches dir $workingDir" }
if (destinationDir.exists()) throw IOException("Could not delete directory $destinationDir.")
if (workingDir.exists()) throw IOException("Could not delete internal caches in folder $workingDir")
destinationDir.mkdirs()
workingDir.mkdirs()
}
@@ -26,6 +26,7 @@ class FileSnapshotMap(storageFile: File) : BasicStringMap<FileSnapshot>(storageF
override fun dumpValue(value: FileSnapshot): String =
value.toString()
@Synchronized
fun compareAndUpdate(newFiles: Iterable<File>): ChangedFiles.Known {
val snapshotProvider = SimpleFileSnapshotProviderImpl()
val newOrModified = ArrayList<File>()
@@ -12,6 +12,7 @@ class SourceToOutputFilesMap(
storageFile: File
) : BasicStringMap<Collection<String>>(storageFile, PathStringDescriptor, StringCollectionExternalizer) {
@Synchronized
operator fun set(sourceFile: File, outputFiles: Collection<File>) {
storage[sourceFile.absolutePath] = outputFiles.map { it.absolutePath }
}
@@ -22,6 +23,7 @@ class SourceToOutputFilesMap(
override fun dumpValue(value: Collection<String>) =
value.dumpCollection()
@Synchronized
fun remove(file: File): Collection<File> =
get(file).also { storage.remove(file.absolutePath) }
}