| // DISABLE_NATIVE: gcType=NOOP | |
| // FREE_COMPILER_ARGS: -opt-in=kotlin.native.internal.InternalForKotlinNative,kotlin.native.runtime.NativeRuntimeApi,kotlin.experimental.ExperimentalNativeApi,kotlinx.cinterop.ExperimentalForeignApi | |
| import kotlin.test.* | |
| import kotlin.native.internal.* | |
| import kotlin.concurrent.AtomicInt | |
| import kotlin.concurrent.AtomicNativePtr | |
| import kotlin.native.concurrent.* | |
| import kotlin.native.ref.WeakReference | |
| import kotlin.native.ref.Cleaner | |
| import kotlin.native.ref.createCleaner | |
| import kotlin.native.runtime.GC | |
| class AtomicBoolean(initialValue: Boolean) { | |
| private val impl = AtomicInt(if (initialValue) 1 else 0) | |
| public var value: Boolean | |
| get() = impl.value != 0 | |
| set(new) { impl.value = if (new) 1 else 0 } | |
| } | |
| class FunBox(private val impl: () -> Unit) { | |
| fun call() { | |
| impl() | |
| } | |
| } | |
| @Test | |
| fun testCleanerLambda() { | |
| val called = AtomicBoolean(false); | |
| var funBoxWeak: WeakReference<FunBox>? = null | |
| var cleanerWeak: WeakReference<Cleaner>? = null | |
| { | |
| val cleaner = { | |
| val funBox = FunBox { called.value = true } | |
| funBoxWeak = WeakReference(funBox) | |
| createCleaner(funBox) { it.call() } | |
| }() | |
| GC.collect() // Make sure local funBox reference is gone | |
| cleanerWeak = WeakReference(cleaner) | |
| assertFalse(called.value) | |
| }() | |
| GC.collect() | |
| assertNull(cleanerWeak!!.value) | |
| assertTrue(called.value) | |
| GC.collect() | |
| assertNull(funBoxWeak!!.value) | |
| } | |
| @Test | |
| fun testCleanerAnonymousFunction() { | |
| val called = AtomicBoolean(false); | |
| var funBoxWeak: WeakReference<FunBox>? = null | |
| var cleanerWeak: WeakReference<Cleaner>? = null | |
| { | |
| val cleaner = { | |
| val funBox = FunBox { called.value = true } | |
| funBoxWeak = WeakReference(funBox) | |
| createCleaner(funBox, fun (it: FunBox) { it.call() }) | |
| }() | |
| GC.collect() // Make sure local funBox reference is gone | |
| cleanerWeak = WeakReference(cleaner) | |
| assertFalse(called.value) | |
| }() | |
| GC.collect() | |
| assertNull(cleanerWeak!!.value) | |
| assertTrue(called.value) | |
| GC.collect() | |
| assertNull(funBoxWeak!!.value) | |
| } | |
| @Test | |
| fun testCleanerFunctionReference() { | |
| val called = AtomicBoolean(false); | |
| var funBoxWeak: WeakReference<FunBox>? = null | |
| var cleanerWeak: WeakReference<Cleaner>? = null | |
| { | |
| val cleaner = { | |
| val funBox = FunBox { called.value = true } | |
| funBoxWeak = WeakReference(funBox) | |
| createCleaner(funBox, FunBox::call) | |
| }() | |
| GC.collect() // Make sure local funBox reference is gone | |
| cleanerWeak = WeakReference(cleaner) | |
| assertFalse(called.value) | |
| }() | |
| GC.collect() | |
| assertNull(cleanerWeak!!.value) | |
| assertTrue(called.value) | |
| GC.collect() | |
| assertNull(funBoxWeak!!.value) | |
| } | |
| val globalInt = AtomicInt(0) | |
| @Test | |
| fun testCleanerWithInt() { | |
| var cleanerWeak: WeakReference<Cleaner>? = null | |
| { | |
| val cleaner = createCleaner(42) { | |
| globalInt.value = it | |
| } | |
| cleanerWeak = WeakReference(cleaner) | |
| assertEquals(0, globalInt.value) | |
| }() | |
| GC.collect() | |
| assertNull(cleanerWeak!!.value) | |
| assertEquals(42, globalInt.value) | |
| } | |
| val globalPtr = AtomicNativePtr(NativePtr.NULL) | |
| @Test | |
| fun testCleanerWithNativePtr() { | |
| var cleanerWeak: WeakReference<Cleaner>? = null | |
| { | |
| val cleaner = createCleaner(NativePtr.NULL + 42L) { | |
| globalPtr.value = it | |
| } | |
| cleanerWeak = WeakReference(cleaner) | |
| assertEquals(NativePtr.NULL, globalPtr.value) | |
| }() | |
| GC.collect() | |
| assertNull(cleanerWeak!!.value) | |
| assertEquals(NativePtr.NULL + 42L, globalPtr.value) | |
| } | |
| @Test | |
| fun testCleanerWithException() { | |
| val called = AtomicBoolean(false); | |
| var funBoxWeak: WeakReference<FunBox>? = null | |
| var cleanerWeak: WeakReference<Cleaner>? = null | |
| { | |
| val funBox = FunBox { called.value = true } | |
| funBoxWeak = WeakReference(funBox) | |
| val cleaner = createCleaner(funBox) { | |
| it.call() | |
| error("Cleaner block failed") | |
| } | |
| cleanerWeak = WeakReference(cleaner) | |
| }() | |
| GC.collect() | |
| assertNull(cleanerWeak!!.value) | |
| // Cleaners block started executing. | |
| assertTrue(called.value) | |
| // Even though the block failed, the captured funBox is not retained. | |
| GC.collect() | |
| assertNull(funBoxWeak!!.value) | |
| } |