Skip to content

Commit b1504e7

Browse files
Aleksei.CherepanovSpace Team
Aleksei.Cherepanov
authored and
Space Team
committed
[IC] Add synchronized clean methods to storage classes
Return synchronized clean methods across various storage classes to ensure proper cleanup and avoid race conditions. This change is critical for preventing "Storage already closed" exceptions during JPS builds. ^KTIJ-31276 Fixed Merge-request: KT-MR-17937 Merged-by: Aleksei Cherepanov <[email protected]> (cherry picked from commit 5f337f8) Merge-request: KT-MR-18073 Merged-by: Aleksei Cherepanov <[email protected]>
1 parent 273abb8 commit b1504e7

File tree

6 files changed

+58
-21
lines changed

6 files changed

+58
-21
lines changed

Diff for: build-common/src/org/jetbrains/kotlin/incremental/storage/BasicMap.kt

+5
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,11 @@ abstract class AppendableSetBasicMap<KEY, E>(
166166
fun append(key: KEY, elements: Set<E>) {
167167
storage.append(key, elements)
168168
}
169+
170+
@Synchronized
171+
override fun clean() {
172+
storage.clean()
173+
}
169174
}
170175

171176
abstract class BasicStringMap<VALUE>(

Diff for: build-common/src/org/jetbrains/kotlin/incremental/storage/BasicMapsOwner.kt

+22-20
Original file line numberDiff line numberDiff line change
@@ -41,40 +41,42 @@ open class BasicMapsOwner(val cachesDir: File) : Closeable {
4141
forEachMapSafe("flush", BasicMap<*, *>::flush)
4242
}
4343

44-
override fun close() {
45-
forEachMapSafe("close", BasicMap<*, *>::close)
46-
}
47-
4844
open fun deleteStorageFiles() {
4945
forEachMapSafe("deleteStorageFiles", BasicMap<*, *>::deleteStorageFiles)
5046
}
5147

5248
/**
53-
* DEPRECATED: This API should be removed because
54-
* - It's not clear what [memoryCachesOnly] means.
55-
* - In the past, when `memoryCachesOnly=true` we applied a small optimization: Checking
56-
* [com.intellij.util.io.PersistentHashMap.isDirty] before calling [com.intellij.util.io.PersistentHashMap.force]. However, if that
57-
* optimization is useful, it's better to always do it (perhaps inside the [com.intellij.util.io.PersistentHashMap.force] method
58-
* itself) rather than doing it based on the value of this parameter.
59-
*
60-
* Instead, just call [flush] (without a parameter) directly.
49+
* Please do not remove or modify this function.
50+
* It is implementing [org.jetbrains.jps.incremental.storage.StorageOwner] interface and needed for correct JPS compilation.
51+
*/
52+
override fun close() {
53+
forEachMapSafe("close", BasicMap<*, *>::close)
54+
}
55+
56+
/**
57+
* Please do not remove or modify this function.
58+
* It is implementing [org.jetbrains.jps.incremental.storage.StorageOwner] interface and needed for correct JPS compilation.
6159
*/
6260
fun flush(@Suppress("UNUSED_PARAMETER") memoryCachesOnly: Boolean) {
6361
flush()
6462
}
6563

6664
/**
67-
* DEPRECATED: This API should be removed because:
68-
* - It's not obvious what "clean" means: It does not exactly describe the current implementation, and it also sounds similar to
69-
* "clear" which means removing all the map entries, but this method does not do that.
70-
* - This method currently calls [close] (and [deleteStorageFiles]). However, [close] is often already called separately and
71-
* automatically, so this API makes it more likely for [close] to be accidentally called twice.
65+
* Please do not remove or modify this function.
66+
* It is implementing [org.jetbrains.jps.incremental.storage.StorageOwner] interface and needed for correct JPS compilation.
67+
* Calling [org.jetbrains.kotlin.incremental.storage.BasicMapsOwner.close] here is unnecessary and will produce race conditions
68+
* for JPS build because JPS can modify caches of dependant modules.
7269
*
73-
* Instead, just call [close] and/or [deleteStorageFiles] explicitly.
70+
* More context:
71+
* 1) While compiling module Foo, thread A can open caches of dependent module Bar
72+
* 2) When we will compile module Bar in thread B we can decide to rebuild the module (e.g. configuration of facet changed)
73+
* 3) Thread B will call `clean` action on caches of module Bar
74+
* 4) If `clean` action also call `close` action,
75+
* it will close opened map and will make it unusable when it tries to add info after recompilation,
76+
* which will cause a "Storage already closed" exception.
7477
*/
7578
fun clean() {
76-
close()
77-
deleteStorageFiles()
79+
forEachMapSafe("clean", BasicMap<*, *>::clean)
7880
}
7981

8082
@Synchronized

Diff for: build-common/src/org/jetbrains/kotlin/incremental/storage/InMemoryStorage.kt

+4
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,10 @@ open class InMemoryStorage<KEY, VALUE>(
137137
storage.close()
138138
}
139139

140+
@Synchronized
141+
override fun clean() {
142+
storage.clean()
143+
}
140144
}
141145

142146
/** [InMemoryStorage] where a map entry's value is a [Collection] of elements of type [E]. */

Diff for: build-common/src/org/jetbrains/kotlin/incremental/storage/LazyStorage.kt

+18-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.incremental.storage
1919
import com.intellij.util.CommonProcessors
2020
import com.intellij.util.io.AppendablePersistentMap
2121
import com.intellij.util.io.DataExternalizer
22+
import com.intellij.util.io.IOUtil
2223
import com.intellij.util.io.KeyDescriptor
2324
import com.intellij.util.io.PersistentHashMap
2425
import org.jetbrains.kotlin.incremental.IncrementalCompilationContext
@@ -27,6 +28,7 @@ import java.io.DataInput
2728
import java.io.DataInputStream
2829
import java.io.DataOutput
2930
import java.io.File
31+
import java.io.IOException
3032

3133
/**
3234
* [PersistentStorage] which delegates operations to a [PersistentHashMap]. Note that the [PersistentHashMap] is created lazily (only when
@@ -90,9 +92,24 @@ open class LazyStorage<KEY, VALUE>(
9092

9193
@Synchronized
9294
override fun close() {
93-
storage?.close()
95+
try {
96+
storage?.close()
97+
} finally {
98+
storage = null
99+
}
94100
}
95101

102+
@Synchronized
103+
override fun clean() {
104+
try {
105+
storage?.close()
106+
} finally {
107+
storage = null
108+
if (!IOUtil.deleteAllFilesStartingWith(storageFile)) {
109+
throw IOException("Could not delete internal storage: ${storageFile.absolutePath}")
110+
}
111+
}
112+
}
96113
}
97114

98115
/** [LazyStorage] where a map entry's value is a [Collection] of elements of type [E]. */

Diff for: build-common/src/org/jetbrains/kotlin/incremental/storage/PersistentStorage.kt

+7
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ interface PersistentStorage<KEY, VALUE> : Closeable {
4848

4949
/** Writes any remaining in-memory changes to [storageFile] ([flush]) and closes this map. */
5050
override fun close()
51+
52+
fun clean()
5153
}
5254

5355
/** [PersistentStorage] where a map entry's value is a [Collection] of elements of type [E]. */
@@ -108,6 +110,11 @@ abstract class PersistentStorageWrapper<KEY, VALUE>(
108110
override fun close() {
109111
storage.close()
110112
}
113+
114+
@Synchronized
115+
override fun clean() {
116+
storage.clean()
117+
}
111118
}
112119

113120
/** [PersistentStorageWrapper] where a map entry's value is a [Collection] of elements of type [E]. */

Diff for: build-common/test/org/jetbrains/kotlin/incremental/CompilationTransactionTest.kt

+2
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ private class InMemoryStorageWrapperMock : InMemoryStorageInterface<Any, Any> {
5050
override fun get(key: Any) = null
5151

5252
override fun contains(key: Any) = false
53+
54+
override fun clean() {}
5355
}
5456

5557
abstract class BaseCompilationTransactionTest {

0 commit comments

Comments
 (0)