KT-76018 [kotlin] Always do `countDown()` in `finally` in `KaFirStopWorldCacheCleaner`
This would prevent hard freezes if `performCleanup` re-throws
IJ platform exceptions (which might happen, see KT-72388).
If `countDown` never happens, then any number of
threads can be forever blocked, waiting to enter
the analysis.
^KT-76018 Fixed
diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/utils/KaFirCacheCleaner.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/utils/KaFirCacheCleaner.kt
index b561776..1c2ac23 100644
--- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/utils/KaFirCacheCleaner.kt
+++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/utils/KaFirCacheCleaner.kt
@@ -177,12 +177,14 @@
if (analyzerCount == 0) {
val existingLatch = cleanupLatch
if (existingLatch != null) {
- performCleanup()
-
- // Unpause all waiting analyses.
- // Even if some new block comes before the 'cleanupLatch' is set to `null`, the old latch will be already open.
- existingLatch.countDown()
- cleanupLatch = null
+ try {
+ performCleanup()
+ } finally {
+ // Unpause all waiting analyses.
+ // Even if some new block comes before the 'cleanupLatch' is set to `null`, the old latch will be already open.
+ existingLatch.countDown()
+ cleanupLatch = null
+ }
}
}
}
@@ -208,14 +210,17 @@
// We cannot start a new read/write action here as there might be already a pending write action waiting for 'this' monitor.
// However, we are still sure no threads can get a session until the cleanup is complete.
cleanupScheduleMs = System.currentTimeMillis()
- performCleanup()
- if (existingLatch != null) {
- // Error recovery in case if things went really bad.
- // Should never happen unless there is some flaw in the algorithm
- existingLatch.countDown()
- cleanupLatch = null
- LOG.error("K2 cache cleanup was expected to happen right after the last analysis block completion")
+ try {
+ performCleanup()
+ } finally {
+ if (existingLatch != null) {
+ // Error recovery in case if things went really bad.
+ // Should never happen unless there is some flaw in the algorithm
+ existingLatch.countDown()
+ cleanupLatch = null
+ LOG.error("K2 cache cleanup was expected to happen right after the last analysis block completion")
+ }
}
} else if (existingLatch == null) {
LOG.debug { "K2 cache cleanup scheduled from ${Thread.currentThread()}, $analyzerCount analyses left" }
@@ -229,6 +234,8 @@
* Cleans all K2 resolution caches.
*
* Must always run in `synchronized(this)` to prevent concurrent cleanups.
+ *
+ * N.B.: May re-throw exceptions from IJ Platform [rethrowIntellijPlatformExceptionIfNeeded].
*/
@OptIn(LLFirInternals::class)
private fun performCleanup() {