[reflect] Fix flaky tests that depend on Reflection.clearCaches()

* Use ReflectionFactoryImpl as single point of synchronization
* Synchronize all cache-sensitive tests on it in order to be robust in parallel test runners
* Remove redundant cache clear after each test

Merge-request: KT-MR-6842
Merged-by: Vsevolod Tolstopyatov <qwwdfsad@gmail.com>
This commit is contained in:
Vsevolod Tolstopyatov
2022-08-12 14:10:08 +00:00
committed by Space
parent 94abeb64c5
commit e32e5c26a4
8 changed files with 30 additions and 33 deletions
@@ -2,12 +2,15 @@
// FULL_JDK // FULL_JDK
// WITH_REFLECT // WITH_REFLECT
import kotlin.jvm.internal.* import kotlin.jvm.internal.*
import kotlin.reflect.jvm.internal.*
class A class A
fun box(): String { fun box(): String {
val pckg = Reflection.getOrCreateKotlinPackage(A::class.java) return synchronized(ReflectionFactoryImpl::class.java) {
System.gc() val pckg = Reflection.getOrCreateKotlinPackage(A::class.java)
val pckg2 = Reflection.getOrCreateKotlinPackage(A::class.java) System.gc()
if (pckg === pckg2) return "OK" val pckg2 = Reflection.getOrCreateKotlinPackage(A::class.java)
return "Fail" if (pckg === pckg2) return@synchronized "OK"
return@synchronized "Fail"
}
} }
@@ -1,13 +1,16 @@
// TARGET_BACKEND: JVM_IR // TARGET_BACKEND: JVM_IR
// FULL_JDK // FULL_JDK
// WITH_REFLECT // WITH_REFLECT
import kotlin.reflect.jvm.internal.*
class A class A
fun box(): String { fun box(): String {
val clz = A::class return synchronized(ReflectionFactoryImpl::class.java) {
System.gc() val clz = A::class
val clz2 = A::class System.gc()
if (clz === clz2) return "OK" val clz2 = A::class
return "Fail" if (clz === clz2) return@synchronized "OK"
return@synchronized "Fail"
}
} }
@@ -12,7 +12,9 @@ inline fun check(message: String, generate: () -> Any?) {
x1 = generate() x1 = generate()
// Force clear the internal maps, as if the weak values in them are garbage-collected. // Force clear the internal maps, as if the weak values in them are garbage-collected.
kotlin.reflect.jvm.internal.ReflectionFactoryImpl.clearCaches() synchronized(kotlin.reflect.jvm.internal.ReflectionFactoryImpl::class.java) {
kotlin.reflect.jvm.internal.ReflectionFactoryImpl.clearCaches()
}
x2 = generate() x2 = generate()
} catch (e: Throwable) { } catch (e: Throwable) {
@@ -11,7 +11,9 @@ inline fun check(message: String, generate: () -> Any?) {
x1 = generate() x1 = generate()
// Force clear the internal maps, as if the weak values in them are garbage-collected. // Force clear the internal maps, as if the weak values in them are garbage-collected.
kotlin.reflect.jvm.internal.ReflectionFactoryImpl.clearCaches() synchronized(kotlin.reflect.jvm.internal.ReflectionFactoryImpl::class.java) {
kotlin.reflect.jvm.internal.ReflectionFactoryImpl.clearCaches()
}
x2 = generate() x2 = generate()
} catch (e: Throwable) { } catch (e: Throwable) {
+8 -5
View File
@@ -5,6 +5,7 @@ import kotlin.test.assertEquals
import kotlin.test.assertSame import kotlin.test.assertSame
import kotlin.reflect.KType import kotlin.reflect.KType
import kotlin.reflect.typeOf import kotlin.reflect.typeOf
import kotlin.reflect.jvm.internal.*
private inline fun <reified T> check(isNullable: Boolean = false) { private inline fun <reified T> check(isNullable: Boolean = false) {
val t1 = typeOf<T>() val t1 = typeOf<T>()
@@ -15,10 +16,12 @@ private inline fun <reified T> check(isNullable: Boolean = false) {
} }
fun box(): String { fun box(): String {
check<Int>() synchronized(ReflectionFactoryImpl::class.java) {
check<Int?>(true) check<Int>()
check<List<Int>?>(true) check<Int?>(true)
check<List<Int>>() check<List<Int>?>(true)
check<List<Int?>?>(true) check<List<Int>>()
check<List<Int?>?>(true)
}
return "OK" return "OK"
} }
@@ -97,8 +97,6 @@ open class JvmBoxRunner(testServices: TestServices) : JvmBinaryArtifactHandler(t
} }
} }
throw e throw e
} finally {
clearReflectionCache(classLoader)
} }
} }
@@ -21,7 +21,6 @@ import java.io.File;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.List; import java.util.List;
import static org.jetbrains.kotlin.codegen.TestUtilsKt.clearReflectionCache;
import static org.jetbrains.kotlin.test.KotlinTestUtils.assertEqualsToFile; import static org.jetbrains.kotlin.test.KotlinTestUtils.assertEqualsToFile;
import static org.jetbrains.kotlin.test.clientserver.TestProcessServerKt.getBoxMethodOrNull; import static org.jetbrains.kotlin.test.clientserver.TestProcessServerKt.getBoxMethodOrNull;
import static org.jetbrains.kotlin.test.clientserver.TestProcessServerKt.getGeneratedClass; import static org.jetbrains.kotlin.test.clientserver.TestProcessServerKt.getGeneratedClass;
@@ -100,9 +99,6 @@ public abstract class AbstractBlackBoxCodegenTest extends CodegenTestCase {
} }
throw ExceptionUtilsKt.rethrow(e); throw ExceptionUtilsKt.rethrow(e);
} }
finally {
clearReflectionCache(generatedClassLoader);
}
} }
fail("Can't find box method!"); fail("Can't find box method!");
} }
@@ -5,19 +5,9 @@
package org.jetbrains.kotlin.codegen package org.jetbrains.kotlin.codegen
import org.jetbrains.kotlin.load.java.JvmAbi
import java.net.URL import java.net.URL
import java.net.URLClassLoader import java.net.URLClassLoader
fun clearReflectionCache(classLoader: ClassLoader) {
try {
val klass = classLoader.loadClass(JvmAbi.REFLECTION_FACTORY_IMPL.asSingleFqName().asString())
val method = klass.getDeclaredMethod("clearCaches")
method.invoke(null)
} catch (e: ClassNotFoundException) {
// This is OK for a test without kotlin-reflect in the dependencies
}
}
fun ClassLoader?.extractUrls(): List<URL> { fun ClassLoader?.extractUrls(): List<URL> {
return (this as? URLClassLoader)?.let { return (this as? URLClassLoader)?.let {