Reflection: fix exceptions on concurrent access to some properties

Using `ReflectionProperties.lazy` is incorrect because it allows several
threads to observe different resulting values if they're computing it
simultaneously (unlike `lazy(PUBLICATION)`, which always returns the
value that "won the race").

In the case of property delegates, for example, if we're invoking
`isAccessible = true` and then `getDelegate()` concurrently, it might
happen that when some thread invokes `getDelegate()`, it gets the
underlying Field object which was written by another thread and which
has not yet been made accessible, leading to
IllegalPropertyDelegateAccessException.

 #KT-27585 Fixed
This commit is contained in:
Alexander Udalov
2023-01-24 16:34:00 +01:00
committed by Space Team
parent e8f95a3376
commit 99b38ccb74
9 changed files with 137 additions and 31 deletions
@@ -0,0 +1,38 @@
import java.util.concurrent.atomic.AtomicInteger
import java.util.concurrent.atomic.AtomicReference
import java.util.concurrent.CyclicBarrier
import kotlin.concurrent.thread
import kotlin.reflect.full.*
import kotlin.reflect.jvm.*
const val N_THREADS = 50
class C {
private fun function() {}
}
fun main() {
val instance = C()
val reference = C::class.functions.single { it.name == "function" }
val gate = CyclicBarrier(N_THREADS + 1)
var fail = AtomicReference<Throwable?>(null)
var finished = AtomicInteger(0)
for (i in 0 until N_THREADS) {
thread {
gate.await()
reference.isAccessible = true
try {
reference.javaMethod!!.invoke(instance)
} catch (e: Throwable) {
fail.set(e)
}
finished.incrementAndGet()
}
}
gate.await()
while (finished.get() != N_THREADS) Thread.sleep(25L)
fail.get()?.let { throw it }
}