diff --git a/core/util.runtime/src/org/jetbrains/kotlin/storage/LockBasedStorageManager.java b/core/util.runtime/src/org/jetbrains/kotlin/storage/LockBasedStorageManager.java index 8b504c97393..dd2f785e203 100644 --- a/core/util.runtime/src/org/jetbrains/kotlin/storage/LockBasedStorageManager.java +++ b/core/util.runtime/src/org/jetbrains/kotlin/storage/LockBasedStorageManager.java @@ -290,7 +290,18 @@ public class LockBasedStorageManager implements StorageManager { RECURSION_WAS_DETECTED } - // Being static is memory optimization to prevent capturing outer-class reference at each level of inheritance hierarchy + /** + * Important thread-safety note! + * + * This implementation publishes value **BEFORE** calling postCompute on it. + * + * It means that thread-safety of actions in postCompute() and recursion prevention + * rely *solely* on the `storageManager.lock()`. + * + * And yes, there are a LockBasedStorageManager.NO_LOCKS, which doesn't have lock at all, + * so if you have some `StorageManager` (or even if it is an instanceof `LockBasedStorageManager`), + * thread-safety of produced lazy values still not guaranteed. + */ private static class LockBasedLazyValue implements NullableLazyValue { private final LockBasedStorageManager storageManager; private final Function0 computable; diff --git a/core/util.runtime/src/org/jetbrains/kotlin/storage/StorageManager.kt b/core/util.runtime/src/org/jetbrains/kotlin/storage/StorageManager.kt index 3c4de1d5f48..4aa74b688d4 100644 --- a/core/util.runtime/src/org/jetbrains/kotlin/storage/StorageManager.kt +++ b/core/util.runtime/src/org/jetbrains/kotlin/storage/StorageManager.kt @@ -47,7 +47,11 @@ interface StorageManager { * The parameter to it is {@code true} for the first call, {@code false} otherwise. * If {@code onRecursiveCall} is {@code null}, an exception will be thrown on a recursive call, * otherwise it's executed and its result is returned - * @param postCompute is called after the value is computed, but before any other thread sees it + * + * @param postCompute is called after the value is computed AND published (and some clients rely on that + * behavior - notably, AbstractTypeConstructor). It means that it is up to particular implementation + * to provide (or not to provide) thread-safety guarantees on writes made in postCompute -- see javadoc for + * LockBasedLazyValue for details. */ fun createLazyValueWithPostCompute(computable: () -> T, onRecursiveCall: ((Boolean) -> T)?, postCompute: (T) -> Unit): NotNullLazyValue @@ -56,8 +60,7 @@ interface StorageManager { fun createRecursionTolerantNullableLazyValue(computable: () -> T?, onRecursiveCall: T?): NullableLazyValue /** - * {@code postCompute} is called after the value is computed, but before any other thread sees it (the current thread may - * see it in between) + * See javadoc for createLazyValueWithPostCompute */ fun createNullableLazyValueWithPostCompute(computable: () -> T?, postCompute: (T?) -> Unit): NullableLazyValue