From c24f2d06d4386ba3e19dd72b151a43b2478f4d9b Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Wed, 23 Oct 2019 15:21:32 +0200 Subject: [PATCH] Mark value fields in ReflectProperties as volatile These fields were originally non-volatile because of an incorrect assumption I had at the time that a value was safely published if the underlying object's class has at least one final field. This is true for almost all values used in lazy/lazySoft: DeserializedFunctionDescriptor, DeserializedPropertyDescriptor, KTypeImpl, java.lang.reflect.Field, etc. But of course, this only means that the object was _safely initialized_ and not safely published via the non-volatile reference, where other threads can still observe null, even after that constructed object was leaked to the outer world by some other means and led to observable changes in behavior. This can fix some concurrency issues in kotlin-reflect. I wasn't able to reproduce the problem in stress tests though. --- .../src/kotlin/reflect/jvm/internal/ReflectProperties.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/ReflectProperties.java b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/ReflectProperties.java index 78eb274343b..57d097217fa 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/ReflectProperties.java +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/ReflectProperties.java @@ -46,7 +46,7 @@ public class ReflectProperties { // A delegate for a lazy property, whose initializer may be invoked multiple times including simultaneously from different threads public static class LazyVal extends Val { private final Function0 initializer; - private Object value = null; + private volatile Object value = null; public LazyVal(@NotNull Function0 initializer) { this.initializer = initializer; @@ -70,7 +70,7 @@ public class ReflectProperties { // including simultaneously from different threads public static class LazySoftVal extends Val { private final Function0 initializer; - private SoftReference value = null; + private volatile SoftReference value = null; public LazySoftVal(@Nullable T initialValue, @NotNull Function0 initializer) { this.initializer = initializer;