diff --git a/kotlin-native/backend.native/tests/runtime/basic/cleaner_basic.kt b/kotlin-native/backend.native/tests/runtime/basic/cleaner_basic.kt index b7dd61581ff..9122240460a 100644 --- a/kotlin-native/backend.native/tests/runtime/basic/cleaner_basic.kt +++ b/kotlin-native/backend.native/tests/runtime/basic/cleaner_basic.kt @@ -54,6 +54,34 @@ fun testCleanerLambda() { assertNull(funBoxWeak!!.value) } +@Test +fun testCleanerNonSharedLambda() { + // Only for experimental MM. + if (Platform.memoryModel != MemoryModel.EXPERIMENTAL) { + return + } + val called = AtomicBoolean(false); + var funBoxWeak: WeakReference? = null + var cleanerWeak: WeakReference? = 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() + performGCOnCleanerWorker() + + assertNull(cleanerWeak!!.value) + assertTrue(called.value) + assertNull(funBoxWeak!!.value) +} + @Test fun testCleanerAnonymousFunction() { val called = AtomicBoolean(false); @@ -78,6 +106,34 @@ fun testCleanerAnonymousFunction() { assertNull(funBoxWeak!!.value) } +@Test +fun testCleanerNonSharedAnonymousFunction() { + // Only for experimental MM. + if (Platform.memoryModel != MemoryModel.EXPERIMENTAL) { + return + } + val called = AtomicBoolean(false); + var funBoxWeak: WeakReference? = null + var cleanerWeak: WeakReference? = 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() + performGCOnCleanerWorker() + + assertNull(cleanerWeak!!.value) + assertTrue(called.value) + assertNull(funBoxWeak!!.value) +} + @Test fun testCleanerFunctionReference() { val called = AtomicBoolean(false); @@ -102,8 +158,40 @@ fun testCleanerFunctionReference() { assertNull(funBoxWeak!!.value) } +@Test +fun testCleanerNonSharedFunctionReference() { + // Only for experimental MM. + if (Platform.memoryModel != MemoryModel.EXPERIMENTAL) { + return + } + val called = AtomicBoolean(false); + var funBoxWeak: WeakReference? = null + var cleanerWeak: WeakReference? = 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() + performGCOnCleanerWorker() + + assertNull(cleanerWeak!!.value) + assertTrue(called.value) + assertNull(funBoxWeak!!.value) +} + @Test fun testCleanerFailWithNonShareableArgument() { + // Only for legacy MM. + if (Platform.memoryModel == MemoryModel.EXPERIMENTAL) { + return + } val funBox = FunBox {} assertFailsWith { createCleaner(funBox) {} diff --git a/kotlin-native/backend.native/tests/runtime/concurrent/worker_bound_reference0.kt b/kotlin-native/backend.native/tests/runtime/concurrent/worker_bound_reference0.kt index 108ab0a5a43..edf2cd2bb4f 100644 --- a/kotlin-native/backend.native/tests/runtime/concurrent/worker_bound_reference0.kt +++ b/kotlin-native/backend.native/tests/runtime/concurrent/worker_bound_reference0.kt @@ -37,20 +37,30 @@ fun testGlobal() { val global2: WorkerBoundReference = WorkerBoundReference(A(3)) @Test -fun testGlobalDenyAccessOnWorker() { +fun testGlobalAccessOnWorker() { assertEquals(3, global2.value.a) val worker = Worker.start() val future = worker.execute(TransferMode.SAFE, {}) { - val local = global2 - assertFailsWith { - local.value + if (Platform.memoryModel == MemoryModel.EXPERIMENTAL) { + assertEquals(global2.value, global2.valueOrNull) + global2.value.a + } else { + val local = global2 + assertFailsWith { + local.value + } + assertEquals(null, local.valueOrNull) + null } - assertEquals(null, local.valueOrNull) - Unit } - future.result + val value = future.result + if (Platform.memoryModel == MemoryModel.EXPERIMENTAL) { + assertEquals(3, value) + } else { + assertEquals(null, value) + } worker.requestTermination().result } @@ -189,20 +199,30 @@ fun testLocalFrozen() { } @Test -fun testLocalDenyAccessOnWorkerFrozen() { +fun testLocalAccessOnWorkerFrozen() { val local = WorkerBoundReference(A(3)).freeze() assertEquals(3, local.value.a) val worker = Worker.start() val future = worker.execute(TransferMode.SAFE, { local }) { local -> - assertFailsWith { - local.value + if (Platform.memoryModel == MemoryModel.EXPERIMENTAL) { + assertEquals(local.value, local.valueOrNull) + local.value.a + } else { + assertFailsWith { + local.value + } + assertEquals(null, local.valueOrNull) + null } - assertEquals(null, local.valueOrNull) - Unit } - future.result + val value = future.result + if (Platform.memoryModel == MemoryModel.EXPERIMENTAL) { + assertEquals(3, value) + } else { + assertEquals(null, value) + } worker.requestTermination().result } @@ -277,17 +297,22 @@ fun testLocalAccessOnMainThread() { } @Test -fun testLocalDenyAccessOnMainThreadFrozen() { +fun testLocalAccessOnMainThreadFrozen() { val worker = Worker.start() val future = worker.execute(TransferMode.SAFE, {}) { WorkerBoundReference(A(3)).freeze() } val value = future.result - assertFailsWith { - value.value + if (Platform.memoryModel == MemoryModel.EXPERIMENTAL) { + assertEquals(3, value.value.a) + assertEquals(value.value, value.valueOrNull) + } else { + assertFailsWith { + value.value + } + assertEquals(null, value.valueOrNull) } - assertEquals(null, value.valueOrNull) worker.requestTermination().result } @@ -385,20 +410,30 @@ fun testLocalWithWrapperFrozen() { } @Test -fun testLocalDenyAccessWithWrapperFrozen() { +fun testLocalAccessWithWrapperFrozen() { val local = Wrapper(WorkerBoundReference(A(3))).freeze() assertEquals(3, local.ref.value.a) val worker = Worker.start() val future = worker.execute(TransferMode.SAFE, { local }) { local -> - assertFailsWith { - local.ref.value + if (Platform.memoryModel == MemoryModel.EXPERIMENTAL) { + assertEquals(local.ref.value, local.ref.valueOrNull) + local.ref.value.a + } else { + assertFailsWith { + local.ref.value + } + assertEquals(null, local.ref.valueOrNull) + null } - assertEquals(null, local.ref.valueOrNull) - Unit } - future.result + val value = future.result + if (Platform.memoryModel == MemoryModel.EXPERIMENTAL) { + assertEquals(3, value) + } else { + assertEquals(null, value) + } worker.requestTermination().result } @@ -437,7 +472,14 @@ fun testCollectFrozen() { val (refOwner, refWeak, refValueWeak) = getOwnerAndWeaksFrozen(3) refOwner.value = null - GC.collect() + if (Platform.memoryModel == MemoryModel.EXPERIMENTAL) { + // This runs the finalizer on the WorkerBoundReference, which schedules removing A from the root set + GC.collect() + // This actually frees A + GC.collect() + } else { + GC.collect() + } // Last reference to WorkerBoundReference is gone, so it and it's referent are destroyed. assertNull(refWeak.value) @@ -647,7 +689,16 @@ fun collectCyclicGarbageWithAtomicsFrozen() { val (ref1Owner, ref1Weak, ref2Weak) = createCyclicGarbageWithAtomicsFrozen() dispose(ref1Owner) - GC.collect() + if (Platform.memoryModel == MemoryModel.EXPERIMENTAL) { + // Finalizes WorkerBoundReference and schedules C2 removal from the root set + GC.collect() + // Frees C2, finalizes WorkerBoundReference and schedules C1 removal from the root set + GC.collect() + // Frees C1 + GC.collect() + } else { + GC.collect() + } assertNull(ref1Weak.value) assertNull(ref2Weak.value) @@ -723,16 +774,25 @@ fun concurrentAccessFrozen() { while (workerUnlocker.value < 1) { } - assertFailsWith { - ref.value + if (Platform.memoryModel == MemoryModel.EXPERIMENTAL) { + ref.value.a + } else { + assertFailsWith { + ref.value + } + null } - Unit } } workerUnlocker.increment() for (future in futures) { - future.result + val value = future.result + if (Platform.memoryModel == MemoryModel.EXPERIMENTAL) { + assertEquals(3, value) + } else { + assertEquals(null, value) + } } for (worker in workers) { @@ -742,6 +802,11 @@ fun concurrentAccessFrozen() { @Test fun testExceptionMessageFrozen() { + // Only for legacy MM + if (Platform.memoryModel == MemoryModel.EXPERIMENTAL) { + return + } + val worker = Worker.start() val future = worker.execute(TransferMode.SAFE, {}) { WorkerBoundReference(A(3)).freeze() diff --git a/kotlin-native/backend.native/tests/runtime/workers/worker10.kt b/kotlin-native/backend.native/tests/runtime/workers/worker10.kt index 9acab985759..eb4c9622591 100644 --- a/kotlin-native/backend.native/tests/runtime/workers/worker10.kt +++ b/kotlin-native/backend.native/tests/runtime/workers/worker10.kt @@ -45,7 +45,7 @@ val topSharedData = Data(43) false } }).consume { - result -> assertEquals(Platform.memoryModel == MemoryModel.RELAXED, result) + result -> assertEquals(Platform.memoryModel != MemoryModel.STRICT, result) } worker.execute(TransferMode.SAFE, { -> }, { @@ -65,7 +65,7 @@ val topSharedData = Data(43) false } }).consume { - result -> assertEquals(Platform.memoryModel == MemoryModel.RELAXED, result) + result -> assertEquals(Platform.memoryModel != MemoryModel.STRICT, result) } worker.execute(TransferMode.SAFE, { -> }, { @@ -181,4 +181,4 @@ val atomicRef2 = AtomicReference(Any().freeze()) semaphore.increment() future.result worker.requestTermination().result -} \ No newline at end of file +}