[K/N][tests] Migrate various runtime/ tests ^KT-61259

This commit is contained in:
Alexander Shabalin
2024-03-11 22:48:50 +01:00
committed by Space Team
parent 7ad4e58a7a
commit d88092aa94
99 changed files with 2102 additions and 1905 deletions
@@ -1,302 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
@file:OptIn(kotlin.experimental.ExperimentalNativeApi::class, FreezingIsDeprecated::class,
kotlin.native.runtime.NativeRuntimeApi::class, kotlinx.cinterop.ExperimentalForeignApi::class)
package runtime.basic.cleaner_basic
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)
init {
freeze()
}
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 }.freeze()
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 testCleanerNonSharedLambda() {
// Only for experimental MM.
if (Platform.memoryModel != MemoryModel.EXPERIMENTAL) {
return
}
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()
performGCOnCleanerWorker()
assertNull(cleanerWeak!!.value)
assertTrue(called.value)
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 }.freeze()
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 testCleanerNonSharedAnonymousFunction() {
// Only for experimental MM.
if (Platform.memoryModel != MemoryModel.EXPERIMENTAL) {
return
}
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()
performGCOnCleanerWorker()
assertNull(cleanerWeak!!.value)
assertTrue(called.value)
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 }.freeze()
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 testCleanerNonSharedFunctionReference() {
// Only for experimental MM.
if (Platform.memoryModel != MemoryModel.EXPERIMENTAL) {
return
}
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()
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<IllegalArgumentException> {
createCleaner(funBox) {}
}
}
@Test
fun testCleanerCleansWithoutGC() {
val called = AtomicBoolean(false);
var funBoxWeak: WeakReference<FunBox>? = null
var cleanerWeak: WeakReference<Cleaner>? = null
{
val cleaner = {
val funBox = FunBox { called.value = true }.freeze()
funBoxWeak = WeakReference(funBox)
createCleaner(funBox) { it.call() }
}()
GC.collect() // Make sure local funBox reference is gone
cleaner.freeze()
cleanerWeak = WeakReference(cleaner)
assertFalse(called.value)
}()
GC.collect()
assertNull(cleanerWeak!!.value)
waitCleanerWorker()
assertTrue(called.value)
// Only for legacy MM.
if (Platform.memoryModel != MemoryModel.EXPERIMENTAL) {
// If this fails, GC has somehow ran on the cleaners worker.
assertNotNull(funBoxWeak!!.value)
}
}
val globalInt = AtomicInt(0)
@Test
fun testCleanerWithInt() {
var cleanerWeak: WeakReference<Cleaner>? = null
{
val cleaner = createCleaner(42) {
globalInt.value = it
}.freeze()
cleanerWeak = WeakReference(cleaner)
assertEquals(0, globalInt.value)
}()
GC.collect()
performGCOnCleanerWorker()
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()
performGCOnCleanerWorker()
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 }.freeze()
funBoxWeak = WeakReference(funBox)
val cleaner = createCleaner(funBox) {
it.call()
error("Cleaner block failed")
}
cleanerWeak = WeakReference(cleaner)
}()
GC.collect()
performGCOnCleanerWorker()
assertNull(cleanerWeak!!.value)
// Cleaners block started executing.
assertTrue(called.value)
// Even though the block failed, the captured funBox is freed.
assertNull(funBoxWeak!!.value)
}
@@ -1,22 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
@file:OptIn(kotlin.experimental.ExperimentalNativeApi::class)
import kotlin.native.ref.Cleaner
import kotlin.native.ref.createCleaner
import kotlin.native.Platform
fun main() {
// Cleaner holds onto a finalization lambda. If it doesn't get executed,
// the memory will leak. Suppress memory leak checker to check for cleaners
// leak only.
Platform.isMemoryLeakCheckerActive = false
Platform.isCleanersLeakCheckerActive = true
// This cleaner will run, because with the checker active this cleaner
// will get collected, block scheduled and executed before cleaners are disabled.
createCleaner(42) {
println(it)
}
}
@@ -1,21 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
@file:OptIn(kotlin.experimental.ExperimentalNativeApi::class)
import kotlin.native.ref.createCleaner
import kotlin.native.Platform
fun main() {
// Cleaner holds onto a finalization lambda. If it doesn't get executed,
// the memory will leak. Suppress memory leak checker to check for cleaners
// leak only.
Platform.isMemoryLeakCheckerActive = false
Platform.isCleanersLeakCheckerActive = false
// This cleaner will not run, because with the checker inactive this cleaner
// will not get collected before cleaners are disabled.
createCleaner(42) {
println(it)
}
}
@@ -1,27 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
@file:OptIn(kotlin.experimental.ExperimentalNativeApi::class)
import kotlin.test.*
import kotlin.native.concurrent.*
import kotlin.native.ref.Cleaner
import kotlin.native.ref.createCleaner
import kotlin.native.Platform
@ThreadLocal
var tlsCleaner: Cleaner? = null
fun main() {
// Cleaner holds onto a finalization lambda. If it doesn't get executed,
// the memory will leak. Suppress memory leak checker to check for cleaners
// leak only.
Platform.isMemoryLeakCheckerActive = false
Platform.isCleanersLeakCheckerActive = true
// This cleaner won't be run
tlsCleaner = createCleaner(42) {
println(it)
}
}
@@ -1,27 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
@file:OptIn(kotlin.experimental.ExperimentalNativeApi::class)
import kotlin.test.*
import kotlin.native.concurrent.*
import kotlin.native.ref.Cleaner
import kotlin.native.ref.createCleaner
import kotlin.native.Platform
@ThreadLocal
var tlsCleaner: Cleaner? = null
fun main() {
// Cleaner holds onto a finalization lambda. If it doesn't get executed,
// the memory will leak. Suppress memory leak checker to check for cleaners
// leak only.
Platform.isMemoryLeakCheckerActive = false
Platform.isCleanersLeakCheckerActive = false
// This cleaner won't be run
tlsCleaner = createCleaner(42) {
println(it)
}
}
@@ -1,36 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
@file:OptIn(kotlin.experimental.ExperimentalNativeApi::class, kotlin.native.runtime.NativeRuntimeApi::class, ObsoleteWorkersApi::class)
import kotlin.test.*
import kotlin.concurrent.AtomicInt
import kotlin.native.concurrent.*
import kotlin.native.internal.*
import kotlin.native.ref.Cleaner
import kotlin.native.ref.createCleaner
import kotlin.native.runtime.GC
@ThreadLocal
var tlsCleaner: Cleaner? = null
val value = AtomicInt(0)
fun main() {
val worker = Worker.start()
worker.execute(TransferMode.SAFE, {}) {
tlsCleaner = createCleaner(42) {
value.value = it
}
}
worker.requestTermination().result
waitWorkerTermination(worker)
GC.collect()
waitCleanerWorker()
assertEquals(42, value.value)
}
@@ -1,23 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
@file:OptIn(kotlin.experimental.ExperimentalNativeApi::class)
import kotlin.test.*
import kotlin.native.ref.Cleaner
import kotlin.native.ref.createCleaner
import kotlin.native.Platform
// This cleaner won't be run, because it's deinitialized with globals after
// cleaners are disabled.
val globalCleaner = createCleaner(42) {
println(it)
}
fun main() {
Platform.isCleanersLeakCheckerActive = true
// Make sure cleaner is initialized.
assertNotNull(globalCleaner)
}
@@ -1,21 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
@file:OptIn(kotlin.experimental.ExperimentalNativeApi::class)
import kotlin.test.*
import kotlin.native.ref.Cleaner
import kotlin.native.ref.createCleaner
// This cleaner won't be run, because it's deinitialized with globals after
// cleaners are disabled.
val globalCleaner = createCleaner(42) {
println(it)
}
fun main() {
// Make sure cleaner is initialized.
assertNotNull(globalCleaner)
}
@@ -1,179 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
@file:OptIn(ExperimentalStdlibApi::class, FreezingIsDeprecated::class, kotlin.experimental.ExperimentalNativeApi::class, kotlin.native.runtime.NativeRuntimeApi::class, ObsoleteWorkersApi::class)
package runtime.basic.cleaner_workers
import kotlin.test.*
import kotlin.native.internal.*
import kotlin.concurrent.AtomicInt
import kotlin.concurrent.AtomicReference
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)
init {
freeze()
}
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 testCleanerDestroyInChild() {
val worker = Worker.start()
val called = AtomicBoolean(false);
var funBoxWeak: WeakReference<FunBox>? = null
var cleanerWeak: WeakReference<Cleaner>? = null
worker.execute(TransferMode.SAFE, {
val funBox = FunBox { called.value = true }.freeze()
funBoxWeak = WeakReference(funBox)
val cleaner = createCleaner(funBox) { it.call() }
cleanerWeak = WeakReference(cleaner)
Pair(called, cleaner)
}) { (called, cleaner) ->
assertFalse(called.value)
}.result
GC.collect()
worker.execute(TransferMode.SAFE, {}) { GC.collect() }.result
performGCOnCleanerWorker()
assertNull(cleanerWeak!!.value)
assertTrue(called.value)
assertNull(funBoxWeak!!.value)
worker.requestTermination().result
}
@Test
fun testCleanerDestroyWithChild() {
val worker = Worker.start()
val called = AtomicBoolean(false);
var funBoxWeak: WeakReference<FunBox>? = null
var cleanerWeak: WeakReference<Cleaner>? = null
worker.execute(TransferMode.SAFE, {
val funBox = FunBox { called.value = true }.freeze()
funBoxWeak = WeakReference(funBox)
val cleaner = createCleaner(funBox) { it.call() }
cleanerWeak = WeakReference(cleaner)
Pair(called, cleaner)
}) { (called, cleaner) ->
assertFalse(called.value)
}.result
GC.collect()
worker.requestTermination().result
waitWorkerTermination(worker)
performGCOnCleanerWorker() // Collect cleaners stack
assertNull(cleanerWeak!!.value)
assertTrue(called.value)
assertNull(funBoxWeak!!.value)
}
@Test
fun testCleanerDestroyInMain() {
val worker = Worker.start()
val called = AtomicBoolean(false);
var funBoxWeak: WeakReference<FunBox>? = null
var cleanerWeak: WeakReference<Cleaner>? = null
{
val result = worker.execute(TransferMode.SAFE, { called }) { called ->
val funBox = FunBox { called.value = true }.freeze()
val cleaner = createCleaner(funBox) { it.call() }
Triple(cleaner, WeakReference(funBox), WeakReference(cleaner))
}.result
val cleaner = result.first
funBoxWeak = result.second
cleanerWeak = result.third
assertFalse(called.value)
}()
GC.collect()
worker.execute(TransferMode.SAFE, {}) { GC.collect() }.result
performGCOnCleanerWorker()
assertNull(cleanerWeak!!.value)
assertTrue(called.value)
assertNull(funBoxWeak!!.value)
worker.requestTermination().result
}
@Test
fun testCleanerDestroyShared() {
val worker = Worker.start()
val called = AtomicBoolean(false);
var funBoxWeak: WeakReference<FunBox>? = null
var cleanerWeak: WeakReference<Cleaner>? = null
val cleanerHolder: AtomicReference<Cleaner?> = AtomicReference(null);
{
val funBox = FunBox { called.value = true }.freeze()
funBoxWeak = WeakReference(funBox)
val cleaner = createCleaner(funBox) { it.call() }
cleanerWeak = WeakReference(cleaner)
cleanerHolder.value = cleaner
worker.execute(TransferMode.SAFE, { Pair(called, cleanerHolder) }) { (called, cleanerHolder) ->
cleanerHolder.value = null
assertFalse(called.value)
}.result
}()
GC.collect()
worker.execute(TransferMode.SAFE, {}) { GC.collect() }.result
performGCOnCleanerWorker()
assertNull(cleanerWeak!!.value)
assertTrue(called.value)
assertNull(funBoxWeak!!.value)
worker.requestTermination().result
}
@ThreadLocal
var tlsValue = 11
@Test
fun testCleanerWithTLS() {
val worker = Worker.start()
tlsValue = 12
val value = AtomicInt(0)
worker.execute(TransferMode.SAFE, {value}) {
tlsValue = 13
createCleaner(it) {
it.value = tlsValue
}
Unit
}.result
worker.execute(TransferMode.SAFE, {}) { GC.collect() }.result
performGCOnCleanerWorker()
assertEquals(11, value.value)
worker.requestTermination().result
}
@@ -1,4 +0,0 @@
val x = initRuntimeIfNeeded()
fun main() {
}
@@ -1,76 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
@file:OptIn(ObsoleteWorkersApi::class)
package runtime.basic.initializers6
import kotlin.test.*
import kotlin.concurrent.AtomicInt
import kotlin.concurrent.*
import kotlin.native.concurrent.*
val aWorkerId = AtomicInt(0)
val bWorkersCount = 3
val aWorkerUnlocker = AtomicInt(0)
val bWorkerUnlocker = AtomicInt(0)
object A {
init {
// Must be called by aWorker only.
assertEquals(aWorkerId.value, Worker.current.id)
// Only allow b workers to run, when a worker has started initialization.
bWorkerUnlocker.incrementAndGet()
// Only proceed with initialization, when all b workers have started executing.
while (aWorkerUnlocker.value < bWorkersCount) {}
// And now wait a bit, to increase probability of races.
Worker.current.park(100 * 1000L)
}
val a = produceA()
val b = produceB()
}
fun produceA(): String {
// Must've been called by aWorker only.
assertEquals(aWorkerId.value, Worker.current.id)
return "A"
}
fun produceB(): String {
// Must've been called by aWorker only.
assertEquals(aWorkerId.value, Worker.current.id)
// Also check that it's ok to get A.a while initializing A.b.
return "B+${A.a}"
}
@Test fun runTest() {
val aWorker = Worker.start()
aWorkerId.value = aWorker.id
val bWorkers = Array(bWorkersCount, { _ -> Worker.start() })
val aFuture = aWorker.execute(TransferMode.SAFE, {}, {
A.b
})
val bFutures = Array(bWorkers.size, {
bWorkers[it].execute(TransferMode.SAFE, {}, {
// Wait until A has started to initialize.
while (bWorkerUnlocker.value < 1) {}
// Now allow A initialization to continue.
aWorkerUnlocker.incrementAndGet()
// And this should not've tried to init A itself.
A.a + A.b
})
})
for (future in bFutures) {
assertEquals("AB+A", future.result)
}
assertEquals("B+A", aFuture.result)
for (worker in bWorkers) {
worker.requestTermination().result
}
aWorker.requestTermination().result
}
@@ -1,6 +0,0 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
fun main() {}
@@ -1,847 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
@file:OptIn(kotlin.experimental.ExperimentalNativeApi::class, FreezingIsDeprecated::class, kotlin.native.runtime.NativeRuntimeApi::class, ObsoleteWorkersApi::class)
package runtime.concurrent.worker_bound_reference0
import kotlin.test.*
import kotlin.concurrent.AtomicInt
import kotlin.concurrent.AtomicReference
import kotlin.native.concurrent.*
import kotlin.native.*
import kotlin.native.ref.WeakReference
import kotlin.native.runtime.GC
import kotlin.text.Regex
class A(var a: Int)
val global1: WorkerBoundReference<A> = WorkerBoundReference(A(3))
@Test
fun testGlobal() {
assertEquals(3, global1.value.a)
assertEquals(3, global1.valueOrNull?.a)
val worker = Worker.start()
val future = worker.execute(TransferMode.SAFE, {}) {
global1
}
val value = future.result
assertEquals(3, value.value.a)
assertEquals(3, value.valueOrNull?.a)
worker.requestTermination().result
}
val global2: WorkerBoundReference<A> = WorkerBoundReference(A(3))
@Test
fun testGlobalAccessOnWorker() {
assertEquals(3, global2.value.a)
val worker = Worker.start()
val future = worker.execute(TransferMode.SAFE, {}) {
if (Platform.memoryModel == MemoryModel.EXPERIMENTAL) {
assertEquals(global2.value, global2.valueOrNull)
global2.value.a
} else {
val local = global2
assertFailsWith<IncorrectDereferenceException> {
local.value
}
assertEquals(null, local.valueOrNull)
null
}
}
val value = future.result
if (Platform.memoryModel == MemoryModel.EXPERIMENTAL) {
assertEquals(3, value)
} else {
assertEquals(null, value)
}
worker.requestTermination().result
}
val global3: WorkerBoundReference<A> = WorkerBoundReference(A(3).freeze())
@Test
fun testGlobalAccessOnWorkerFrozenInitially() {
assertEquals(3, global3.value.a)
assertEquals(3, global3.valueOrNull?.a)
val worker = Worker.start()
val future = worker.execute(TransferMode.SAFE, {}) {
global3.value.a
}
val value = future.result
assertEquals(3, value)
worker.requestTermination().result
}
val global4: WorkerBoundReference<A> = WorkerBoundReference(A(3))
@Test
fun testGlobalAccessOnWorkerFrozenBeforePassing() {
assertEquals(3, global4.value.a)
global4.value.freeze()
val worker = Worker.start()
val future = worker.execute(TransferMode.SAFE, {}) {
global4.value.a
}
val value = future.result
assertEquals(3, value)
worker.requestTermination().result
}
val global5: WorkerBoundReference<A> = WorkerBoundReference(A(3))
@Test
fun testGlobalAccessOnWorkerFrozenBeforeAccess() {
val semaphore: AtomicInt = AtomicInt(0)
assertEquals(3, global5.value.a)
val worker = Worker.start()
val future = worker.execute(TransferMode.SAFE, { semaphore }) { semaphore ->
semaphore.incrementAndGet()
while (semaphore.value < 2) {
}
global5.value.a
}
while (semaphore.value < 1) {
}
global5.value.freeze()
semaphore.incrementAndGet()
val value = future.result
assertEquals(3, value)
worker.requestTermination().result
}
val global6: WorkerBoundReference<A> = WorkerBoundReference(A(3))
@Test
fun testGlobalModification() {
val semaphore: AtomicInt = AtomicInt(0)
assertEquals(3, global6.value.a)
val worker = Worker.start()
val future = worker.execute(TransferMode.SAFE, { semaphore }) { semaphore ->
semaphore.incrementAndGet()
while (semaphore.value < 2) {
}
global6
}
while (semaphore.value < 1) {
}
global6.value.a = 4
semaphore.incrementAndGet()
val value = future.result
assertEquals(4, value.value.a)
assertEquals(4, value.valueOrNull?.a)
worker.requestTermination().result
}
val global7: WorkerBoundReference<A> = WorkerBoundReference(A(3))
@Test
fun testGlobalGetWorker() {
val ownerId = Worker.current.id
assertEquals(ownerId, global7.worker.id)
val worker = Worker.start()
val future = worker.execute(TransferMode.SAFE, { ownerId }) { ownerId ->
assertEquals(ownerId, global7.worker.id)
Unit
}
future.result
worker.requestTermination().result
}
@Test
fun testLocal() {
val local = WorkerBoundReference(A(3))
assertEquals(3, local.value.a)
assertEquals(3, local.valueOrNull?.a)
}
@Test
fun testLocalFrozen() {
val local = WorkerBoundReference(A(3)).freeze()
assertEquals(3, local.value.a)
assertEquals(3, local.valueOrNull?.a)
val worker = Worker.start()
val future = worker.execute(TransferMode.SAFE, { local }) { local ->
local
}
val value = future.result
assertEquals(3, value.value.a)
assertEquals(3, value.valueOrNull?.a)
worker.requestTermination().result
}
@Test
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 ->
if (Platform.memoryModel == MemoryModel.EXPERIMENTAL) {
assertEquals(local.value, local.valueOrNull)
local.value.a
} else {
assertFailsWith<IncorrectDereferenceException> {
local.value
}
assertEquals(null, local.valueOrNull)
null
}
}
val value = future.result
if (Platform.memoryModel == MemoryModel.EXPERIMENTAL) {
assertEquals(3, value)
} else {
assertEquals(null, value)
}
worker.requestTermination().result
}
@Test
fun testLocalAccessOnWorkerFrozenInitiallyFrozen() {
val local = WorkerBoundReference(A(3).freeze()).freeze()
assertEquals(3, local.value.a)
assertEquals(3, local.valueOrNull?.a)
val worker = Worker.start()
val future = worker.execute(TransferMode.SAFE, { local }) { local ->
local.value.a
}
val value = future.result
assertEquals(3, value)
worker.requestTermination().result
}
@Test
fun testLocalAccessOnWorkerFrozenBeforePassingFrozen() {
val local = WorkerBoundReference(A(3)).freeze()
assertEquals(3, local.value.a)
local.value.freeze()
val worker = Worker.start()
val future = worker.execute(TransferMode.SAFE, { local }) { local ->
local.value.a
}
val value = future.result
assertEquals(3, value)
worker.requestTermination().result
}
@Test
fun testLocalAccessOnWorkerFrozenBeforeAccessFrozen() {
val semaphore: AtomicInt = AtomicInt(0)
val local = WorkerBoundReference(A(3)).freeze()
assertEquals(3, local.value.a)
val worker = Worker.start()
val future = worker.execute(TransferMode.SAFE, { Pair(local, semaphore) }) { (local, semaphore) ->
semaphore.incrementAndGet()
while (semaphore.value < 2) {
}
local.value.a
}
while (semaphore.value < 1) {
}
local.value.freeze()
semaphore.incrementAndGet()
val value = future.result
assertEquals(3, value)
worker.requestTermination().result
}
@Test
fun testLocalAccessOnMainThread() {
val worker = Worker.start()
val future = worker.execute(TransferMode.SAFE, {}) {
WorkerBoundReference(A(3))
}
assertEquals(3, future.result.value.a)
worker.requestTermination().result
}
@Test
fun testLocalAccessOnMainThreadFrozen() {
val worker = Worker.start()
val future = worker.execute(TransferMode.SAFE, {}) {
WorkerBoundReference(A(3)).freeze()
}
val value = future.result
if (Platform.memoryModel == MemoryModel.EXPERIMENTAL) {
assertEquals(3, value.value.a)
assertEquals(value.value, value.valueOrNull)
} else {
assertFailsWith<IncorrectDereferenceException> {
value.value
}
assertEquals(null, value.valueOrNull)
}
worker.requestTermination().result
}
@Test
fun testLocalModificationFrozen() {
val semaphore: AtomicInt = AtomicInt(0)
val local = WorkerBoundReference(A(3)).freeze()
assertEquals(3, local.value.a)
val worker = Worker.start()
val future = worker.execute(TransferMode.SAFE, { Pair(local, semaphore) }) { (local, semaphore) ->
semaphore.incrementAndGet()
while (semaphore.value < 2) {
}
local
}
while (semaphore.value < 1) {
}
local.value.a = 4
semaphore.incrementAndGet()
val value = future.result
assertEquals(4, value.value.a)
assertEquals(4, value.valueOrNull?.a)
worker.requestTermination().result
}
@Test
fun testLocalGetWorkerFrozen() {
val local = WorkerBoundReference(A(3)).freeze()
val ownerId = Worker.current.id
assertEquals(ownerId, local.worker.id)
val worker = Worker.start()
val future = worker.execute(TransferMode.SAFE, { Pair(local, ownerId) }) { (local, ownerId) ->
assertEquals(ownerId, local.worker.id)
Unit
}
future.result
worker.requestTermination().result
}
@Test
fun testLocalForeignGetWorker() {
val worker = Worker.start()
val ownerId = worker.id
val future = worker.execute(TransferMode.SAFE, { ownerId }) { ownerId ->
val local = WorkerBoundReference(A(3))
assertEquals(ownerId, local.worker.id)
local
}
val value = future.result
assertEquals(ownerId, value.worker.id)
worker.requestTermination().result
}
@Test
fun testLocalForeignGetWorkerFrozen() {
val worker = Worker.start()
val ownerId = worker.id
val future = worker.execute(TransferMode.SAFE, { ownerId }) { ownerId ->
val local = WorkerBoundReference(A(3)).freeze()
assertEquals(ownerId, local.worker.id)
local
}
val value = future.result
assertEquals(ownerId, value.worker.id)
worker.requestTermination().result
}
class Wrapper(val ref: WorkerBoundReference<A>)
@Test
fun testLocalWithWrapperFrozen() {
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 ->
local
}
val value = future.result
assertEquals(3, value.ref.value.a)
worker.requestTermination().result
}
@Test
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 ->
if (Platform.memoryModel == MemoryModel.EXPERIMENTAL) {
assertEquals(local.ref.value, local.ref.valueOrNull)
local.ref.value.a
} else {
assertFailsWith<IncorrectDereferenceException> {
local.ref.value
}
assertEquals(null, local.ref.valueOrNull)
null
}
}
val value = future.result
if (Platform.memoryModel == MemoryModel.EXPERIMENTAL) {
assertEquals(3, value)
} else {
assertEquals(null, value)
}
worker.requestTermination().result
}
fun getOwnerAndWeaks(initial: Int): Triple<AtomicReference<WorkerBoundReference<A>?>, WeakReference<WorkerBoundReference<A>>, WeakReference<A>> {
val ref = WorkerBoundReference(A(initial))
val refOwner: AtomicReference<WorkerBoundReference<A>?> = AtomicReference(ref)
val refWeak = WeakReference(ref)
val refValueWeak = WeakReference(ref.value)
return Triple(refOwner, refWeak, refValueWeak)
}
@Test
fun testCollect() {
val (refOwner, refWeak, refValueWeak) = getOwnerAndWeaks(3)
refOwner.value = null
GC.collect()
// Last reference to WorkerBoundReference is gone, so it and it's referent are destroyed.
assertNull(refWeak.value)
assertNull(refValueWeak.value)
}
fun getOwnerAndWeaksFrozen(initial: Int): Triple<AtomicReference<WorkerBoundReference<A>?>, WeakReference<WorkerBoundReference<A>>, WeakReference<A>> {
val ref = WorkerBoundReference(A(initial)).freeze()
val refOwner: AtomicReference<WorkerBoundReference<A>?> = AtomicReference(ref)
val refWeak = WeakReference(ref)
val refValueWeak = WeakReference(ref.value)
return Triple(refOwner, refWeak, refValueWeak)
}
@Test
fun testCollectFrozen() {
val (refOwner, refWeak, refValueWeak) = getOwnerAndWeaksFrozen(3)
refOwner.value = null
if (Platform.memoryModel == MemoryModel.EXPERIMENTAL) {
// This runs the finalizer on the WorkerBoundReference<A>, 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)
assertNull(refValueWeak.value)
}
fun collectInWorkerFrozen(worker: Worker, semaphore: AtomicInt): Pair<WeakReference<A>, Future<Unit>> {
val (refOwner, _, refValueWeak) = getOwnerAndWeaksFrozen(3)
val future = worker.execute(TransferMode.SAFE, { Pair(refOwner, semaphore) }) { (refOwner, semaphore) ->
semaphore.incrementAndGet()
while (semaphore.value < 2) {
}
refOwner.value = null
GC.collect()
}
while (semaphore.value < 1) {
}
// At this point worker is spinning on semaphore. refOwner still contains reference to
// WorkerBoundReference, so referent is kept alive.
GC.collect()
assertNotNull(refValueWeak.value)
return Pair(refValueWeak, future)
}
@Test
fun testCollectInWorkerFrozen() {
val semaphore: AtomicInt = AtomicInt(0)
val worker = Worker.start()
val (refValueWeak, future) = collectInWorkerFrozen(worker, semaphore)
semaphore.incrementAndGet()
future.result
// At this point WorkerBoundReference no longer has a reference, so it's referent is destroyed.
GC.collect()
assertNull(refValueWeak.value)
worker.requestTermination().result
}
fun doNotCollectInWorkerFrozen(worker: Worker, semaphore: AtomicInt): Future<WorkerBoundReference<A>> {
val ref = WorkerBoundReference(A(3)).freeze()
return worker.execute(TransferMode.SAFE, { Pair(ref, semaphore) }) { (ref, semaphore) ->
semaphore.incrementAndGet()
while (semaphore.value < 2) {
}
GC.collect()
ref
}
}
@Test
fun testDoNotCollectInWorkerFrozen() {
val semaphore: AtomicInt = AtomicInt(0)
val worker = Worker.start()
val future = doNotCollectInWorkerFrozen(worker, semaphore)
while (semaphore.value < 1) {
}
GC.collect()
semaphore.incrementAndGet()
val value = future.result
assertEquals(3, value.value.a)
worker.requestTermination().result
}
class B1 {
lateinit var b2: WorkerBoundReference<B2>
}
data class B2(val b1: WorkerBoundReference<B1>)
fun createCyclicGarbage(): Triple<AtomicReference<WorkerBoundReference<B1>?>, WeakReference<B1>, WeakReference<B2>> {
val ref1 = WorkerBoundReference(B1())
val ref1Owner: AtomicReference<WorkerBoundReference<B1>?> = AtomicReference(ref1)
val ref1Weak = WeakReference(ref1.value)
val ref2 = WorkerBoundReference(B2(ref1))
val ref2Weak = WeakReference(ref2.value)
ref1.value.b2 = ref2
return Triple(ref1Owner, ref1Weak, ref2Weak)
}
@Test
fun collectCyclicGarbage() {
val (ref1Owner, ref1Weak, ref2Weak) = createCyclicGarbage()
ref1Owner.value = null
GC.collect()
assertNull(ref1Weak.value)
assertNull(ref2Weak.value)
}
fun createCyclicGarbageFrozen(): Triple<AtomicReference<WorkerBoundReference<B1>?>, WeakReference<B1>, WeakReference<B2>> {
val ref1 = WorkerBoundReference(B1()).freeze()
val ref1Owner: AtomicReference<WorkerBoundReference<B1>?> = AtomicReference(ref1)
val ref1Weak = WeakReference(ref1.value)
val ref2 = WorkerBoundReference(B2(ref1)).freeze()
val ref2Weak = WeakReference(ref2.value)
ref1.value.b2 = ref2
return Triple(ref1Owner, ref1Weak, ref2Weak)
}
@Test
fun doesNotCollectCyclicGarbageFrozen() {
if (!Platform.isFreezingEnabled) return
val (ref1Owner, ref1Weak, ref2Weak) = createCyclicGarbageFrozen()
ref1Owner.value = null
GC.collect()
// If these asserts fail, that means WorkerBoundReference managed to clean up cyclic garbage all by itself.
assertNotNull(ref1Weak.value)
assertNotNull(ref2Weak.value)
}
fun createCrossThreadCyclicGarbageFrozen(
worker: Worker
): Triple<AtomicReference<WorkerBoundReference<B1>?>, WeakReference<B1>, WeakReference<B2>> {
val ref1 = WorkerBoundReference(B1()).freeze()
val ref1Owner: AtomicReference<WorkerBoundReference<B1>?> = AtomicReference(ref1)
val ref1Weak = WeakReference(ref1.value)
val future = worker.execute(TransferMode.SAFE, { ref1 }) { ref1 ->
val ref2 = WorkerBoundReference(B2(ref1)).freeze()
Pair(ref2, WeakReference(ref2.value))
}
val (ref2, ref2Weak) = future.result
ref1.value.b2 = ref2
return Triple(ref1Owner, ref1Weak, ref2Weak)
}
@Test
fun doesNotCollectCrossThreadCyclicGarbageFrozen() {
if (!Platform.isFreezingEnabled) return
val worker = Worker.start()
val (ref1Owner, ref1Weak, ref2Weak) = createCrossThreadCyclicGarbageFrozen(worker)
ref1Owner.value = null
GC.collect()
worker.execute(TransferMode.SAFE, {}) { GC.collect() }.result
// If these asserts fail, that means WorkerBoundReference managed to clean up cyclic garbage all by itself.
assertNotNull(ref1Weak.value)
assertNotNull(ref2Weak.value)
worker.requestTermination().result
}
class C1 {
lateinit var c2: AtomicReference<WorkerBoundReference<C2>?>
fun dispose() {
c2.value = null
}
}
data class C2(val c1: AtomicReference<WorkerBoundReference<C1>>)
fun createCyclicGarbageWithAtomicsFrozen(): Triple<AtomicReference<WorkerBoundReference<C1>?>, WeakReference<C1>, WeakReference<C2>> {
val ref1 = WorkerBoundReference(C1()).freeze()
val ref1Weak = WeakReference(ref1.value)
val ref2 = WorkerBoundReference(C2(AtomicReference(ref1))).freeze()
val ref2Weak = WeakReference(ref2.value)
ref1.value.c2 = AtomicReference(ref2)
return Triple(AtomicReference(ref1), ref1Weak, ref2Weak)
}
fun dispose(refOwner: AtomicReference<WorkerBoundReference<C1>?>) {
refOwner.value!!.value.dispose()
refOwner.value = null
}
@Test
fun doesNotCollectCyclicGarbageWithAtomicsFrozen() {
if (!Platform.isFreezingEnabled) return
val (ref1Owner, ref1Weak, ref2Weak) = createCyclicGarbageWithAtomicsFrozen()
ref1Owner.value = null
GC.collect()
// If these asserts fail, that means AtomicReference<WorkerBoundReference> managed to clean up cyclic garbage all by itself.
assertNotNull(ref1Weak.value)
assertNotNull(ref2Weak.value)
}
@Test
fun collectCyclicGarbageWithAtomicsFrozen() {
val (ref1Owner, ref1Weak, ref2Weak) = createCyclicGarbageWithAtomicsFrozen()
dispose(ref1Owner)
if (Platform.memoryModel == MemoryModel.EXPERIMENTAL) {
// Finalizes WorkerBoundReference<C2> and schedules C2 removal from the root set
GC.collect()
// Frees C2, finalizes WorkerBoundReference<C1> and schedules C1 removal from the root set
GC.collect()
// Frees C1
GC.collect()
} else {
GC.collect()
}
assertNull(ref1Weak.value)
assertNull(ref2Weak.value)
}
fun createCrossThreadCyclicGarbageWithAtomicsFrozen(
worker: Worker
): Triple<AtomicReference<WorkerBoundReference<C1>?>, WeakReference<C1>, WeakReference<C2>> {
val ref1 = WorkerBoundReference(C1()).freeze()
val ref1Weak = WeakReference(ref1.value)
val future = worker.execute(TransferMode.SAFE, { ref1 }) { ref1 ->
val ref2 = WorkerBoundReference(C2(AtomicReference(ref1))).freeze()
Pair(ref2, WeakReference(ref2.value))
}
val (ref2, ref2Weak) = future.result
ref1.value.c2 = AtomicReference(ref2)
return Triple(AtomicReference(ref1), ref1Weak, ref2Weak)
}
@Test
fun doesNotCollectCrossThreadCyclicGarbageWithAtomicsFrozen() {
if (!Platform.isFreezingEnabled) return
val worker = Worker.start()
val (ref1Owner, ref1Weak, ref2Weak) = createCrossThreadCyclicGarbageWithAtomicsFrozen(worker)
ref1Owner.value = null
GC.collect()
worker.execute(TransferMode.SAFE, {}) { GC.collect() }.result
// If these asserts fail, that means AtomicReference<WorkerBoundReference> managed to clean up cyclic garbage all by itself.
assertNotNull(ref1Weak.value)
assertNotNull(ref2Weak.value)
worker.requestTermination().result
}
@Test
fun collectCrossThreadCyclicGarbageWithAtomicsFrozen() {
val worker = Worker.start()
val (ref1Owner, ref1Weak, ref2Weak) = createCrossThreadCyclicGarbageWithAtomicsFrozen(worker)
dispose(ref1Owner)
// This marks C2 as gone on the main thread
GC.collect()
// This cleans up all the references from the worker thread and destroys C2, but C1 is still alive.
worker.execute(TransferMode.SAFE, {}) { GC.collect() }.result
// And this finally destroys C1
GC.collect()
assertNull(ref1Weak.value)
assertNull(ref2Weak.value)
worker.requestTermination().result
}
@Test
fun concurrentAccessFrozen() {
val workerCount = 10
val workerUnlocker = AtomicInt(0)
val ref = WorkerBoundReference(A(3)).freeze()
assertEquals(3, ref.value.a)
val workers = Array(workerCount) {
Worker.start()
}
val futures = Array(workers.size) {
workers[it].execute(TransferMode.SAFE, { Pair(ref, workerUnlocker) }) { (ref, workerUnlocker) ->
while (workerUnlocker.value < 1) {
}
if (Platform.memoryModel == MemoryModel.EXPERIMENTAL) {
ref.value.a
} else {
assertFailsWith<IncorrectDereferenceException> {
ref.value
}
null
}
}
}
workerUnlocker.incrementAndGet()
for (future in futures) {
val value = future.result
if (Platform.memoryModel == MemoryModel.EXPERIMENTAL) {
assertEquals(3, value)
} else {
assertEquals(null, value)
}
}
for (worker in workers) {
worker.requestTermination().result
}
}
@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()
}
val value = future.result
val ownerName = worker.name
val messagePattern = Regex("illegal attempt to access non-shared runtime\\.concurrent\\.worker_bound_reference0\\.A@[a-f0-9]+ bound to `$ownerName` from `${Worker.current.name}`")
val exception = assertFailsWith<IncorrectDereferenceException> {
value.value
}
assertTrue(messagePattern matches exception.message!!)
worker.requestTermination().result
}
@Test
fun testDoubleFreeze() {
val ref = WorkerBoundReference(A(3))
val wrapper = Wrapper(ref)
ref.freeze()
ref.freeze()
wrapper.freeze()
}
@Test
fun testDoubleFreezeWithFreezeBlocker() {
if (!Platform.isFreezingEnabled) return
val ref = WorkerBoundReference(A(3))
val wrapper = Wrapper(ref)
wrapper.ensureNeverFrozen()
assertFailsWith<FreezingException> {
wrapper.freeze()
}
ref.freeze()
}
@@ -1,26 +0,0 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
import kotlin.test.*
fun testArrayAllocation(size: Int) {
val arr = IntArray(size)
// Force a write into the memory.
// TODO: How to make sure the optimizer never deletes this write?
arr[size - 1] = 42
assertEquals(42, arr[size - 1])
}
@Test
fun sanity() {
// Should always succeed everywhere
testArrayAllocation(1 shl 10)
}
@Test
fun test() {
// Will fail on 32 bits.
testArrayAllocation(1 shl 30)
}
@@ -1,19 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package runtime.memory.basic0
import kotlin.test.*
class A {
var field: B? = null
}
class B(var field: Int)
@Test fun runTest() {
val a = A()
a.field = B(2)
}
@@ -1,55 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package runtime.memory.cycles0
import kotlin.test.*
data class Node(val data: Int, var next: Node?, var prev: Node?, val outer: Node?)
fun makeCycle(len: Int, outer: Node?): Node {
val start = Node(0, null, null, outer)
var prev = start
for (i in 1 .. len - 1) {
prev = Node(i, prev, null, outer)
}
start.next = prev
return start
}
fun makeDoubleCycle(len: Int): Node {
val start = makeCycle(len, null)
var prev = start
var cur = prev.next
while (cur != start) {
cur!!.prev = prev
prev = cur
cur = cur.next
}
start.prev = prev
return start
}
fun createCycles(junk: Node) {
val cycle1 = makeCycle(1, junk)
val cycle2 = makeCycle(2, junk)
val cycle10 = makeCycle(10, junk)
val cycle100 = makeCycle(100, junk)
val dcycle1 = makeDoubleCycle(1)
val dcycle2 = makeDoubleCycle(2)
val dcycle10 = makeDoubleCycle(10)
val dcycle100 = makeDoubleCycle(100)
}
@OptIn(kotlin.native.runtime.NativeRuntimeApi::class)
@Test fun runTest() {
// Create outer link from cyclic garbage.
val outer = Node(42, null, null, null)
createCycles(outer)
kotlin.native.runtime.GC.collect()
// Ensure outer is not collected.
println(outer.data)
}
@@ -1 +0,0 @@
42
@@ -1,27 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
@file:OptIn(kotlin.experimental.ExperimentalNativeApi::class)
package runtime.memory.cycles1
import kotlin.test.*
import kotlin.native.ref.*
@OptIn(kotlin.native.runtime.NativeRuntimeApi::class)
@Test fun runTest() {
// TODO: make it work in relaxed model as well.
if (Platform.memoryModel == MemoryModel.RELAXED) return
val weakRefToTrashCycle = createLoop()
kotlin.native.runtime.GC.collect()
assertNull(weakRefToTrashCycle.get())
}
private fun createLoop(): WeakReference<Any> {
val loop = Array<Any?>(1, { null })
loop[0] = loop
return WeakReference(loop)
}
@@ -1,62 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package runtime.memory.escape0
import kotlin.test.*
//fun foo1(arg: String) : String = foo0(arg)
fun foo1(arg: Any) : Any = foo0(arg)
fun foo0(arg: Any) : Any = Any()
var global : Any = Any()
fun foo0_escape(arg: Any) : Any{
global = arg
return Any()
}
class Node(var previous: Node?)
fun zoo3() : Node {
var current = Node(null)
for (i in 1 .. 5) {
current = Node(current)
}
return current
}
fun zoo4(arg: Int) : Any {
var a = Any()
var b = Any()
var c = Any()
a = b
val x = 3
a = when {
x < arg -> b
else -> c
}
return a
}
fun zoo5(arg: Any) : Any{
foo1(arg)
return arg
}
fun zoo6(arg: Any) : Any {
return zoo7(arg, "foo", 11)
}
fun zoo7(arg1: Any, arg2: Any, selector: Int) : Any {
return if (selector < 2) arg1 else arg2;
}
@Test fun runTest() {
//val z = zoo7(Any(), Any(), 1)
val x = zoo5(Any())
//println(bar(foo1(foo2("")), foo2(foo1(""))))
}
@@ -1,23 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package runtime.memory.escape1
import kotlin.test.*
class B(val s: String)
class A {
val b = B("zzz")
}
fun foo(): B {
val a = A()
return a.b
}
@Test fun runTest() {
println(foo().s)
}
@@ -1 +0,0 @@
zzz
@@ -1,33 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package runtime.memory.escape2
import kotlin.test.*
class A(val s: String)
class B {
var a: A? = null
}
class C(val b: B)
fun foo(c: C) {
c.b.a = A("zzz")
}
fun bar(b: B) {
val c = C(b)
foo(c)
}
@ThreadLocal
val global = B()
@Test fun runTest() {
bar(global)
println(global.a!!.s)
}
@@ -1 +0,0 @@
zzz
@@ -1,61 +0,0 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@file:OptIn(kotlin.ExperimentalStdlibApi::class, kotlin.native.runtime.NativeRuntimeApi::class, kotlinx.cinterop.ExperimentalForeignApi::class)
import kotlin.native.runtime.GC
import kotlin.test.*
@Test
fun `nothing new collected`() {
GC.collect()
GC.collect()
val stat = GC.lastGCInfo
assertNotNull(stat)
for (key in stat.sweepStatistics.keys) {
assertEquals(stat.sweepStatistics[key]!!.sweptCount, 0L)
}
}
object Global {
val x = listOf(1, 2, 3)
}
@Test
fun `stable refs in root set`() {
GC.collect()
val stat0 = GC.lastGCInfo
assertNotNull(stat0)
val rootSet0 = stat0.rootSet
assertNotNull(rootSet0)
val x = listOf(1, 2, 3)
val stable = kotlinx.cinterop.StableRef.create(x)
GC.collect();
val stat1 = GC.lastGCInfo
assertNotNull(stat1)
val rootSet1 = stat1.rootSet
assertNotNull(rootSet1)
assertEquals(rootSet0.stableReferences + 1, rootSet1.stableReferences)
stable.dispose()
Global.x // to initialize and register global object
GC.collect();
val stat2 = GC.lastGCInfo
assertNotNull(stat2)
val rootSet2 = stat2.rootSet
assertNotNull(rootSet2)
assertEquals(rootSet0.stableReferences, rootSet2.stableReferences)
assertEquals(rootSet1.globalReferences + 1, rootSet2.globalReferences)
}
@Test
fun `check everything is filled at the end`() {
GC.collect()
val stat = GC.lastGCInfo
assertNotNull(stat)
// GC.collect is waiting for finalizers, so it should bet not null
assertNotNull(stat.postGcCleanupTimeNs)
}
@@ -1,9 +0,0 @@
@file:OptIn(kotlin.experimental.ExperimentalNativeApi::class)
import kotlinx.cinterop.*
import kotlin.native.Platform
fun main() {
Platform.isMemoryLeakCheckerActive = true
StableRef.create(Any())
}
@@ -1,16 +0,0 @@
@file:OptIn(kotlin.experimental.ExperimentalNativeApi::class)
import kotlin.test.*
import kotlinx.cinterop.*
import kotlin.native.Platform
@BeforeTest
fun enableMemoryChecker() {
Platform.isMemoryLeakCheckerActive = true
}
@Test
fun test() {
StableRef.create(Any())
}
@@ -1,9 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
@OptIn(kotlin.native.runtime.NativeRuntimeApi::class)
fun main(args: Array<String>) {
kotlin.native.runtime.GC.collect()
}
@@ -1,128 +0,0 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
@file:OptIn(kotlin.experimental.ExperimentalNativeApi::class, kotlin.native.runtime.NativeRuntimeApi::class, kotlin.native.concurrent.ObsoleteWorkersApi::class)
import kotlin.concurrent.AtomicInt
import kotlin.concurrent.Volatile
import kotlin.native.concurrent.*
import kotlin.native.identityHashCode
import kotlin.native.internal.MemoryUsageInfo
import kotlin.native.ref.createCleaner
import kotlin.random.Random
// Copying what's done in kotlinx.benchmark
// TODO: Could we benefit, if this was in stdlib, and the compiler just new about it?
object Blackhole {
@Volatile
var i0: Int = Random.nextInt()
var i1 = i0 + 1
fun consume(value: Any?) {
consume(value.identityHashCode())
}
fun consume(i: Int) {
if ((i0 == i) && (i1 == i)) {
i0 = i
}
}
}
class ArrayOfBytes(bytes: Int) {
val data = ByteArray(bytes)
init {
// Write into every OS page.
for (i in 0 until data.size step 4096) {
data[i] = 42
}
Blackhole.consume(data)
}
}
class ArrayOfBytesWithFinalizer(bytes: Int) {
val impl = ArrayOfBytes(bytes)
val cleaner = createCleaner(impl) {
Blackhole.consume(it)
}
}
fun allocateGarbage() {
// Total amount of objects here:
// - 1 big object with finalizer
// - 9 big objects
// - 2490 small objects with finalizers
// - 97500 small objects without finalizers
// And total size is ~50MiB
for (i in 0..100_000) {
val obj: Any = when {
i == 50_000 -> ArrayOfBytesWithFinalizer(1_000_000) // ~1MiB
i % 10_000 == 0 -> ArrayOfBytes(1_000_000) // ~1MiB
i % 40 == 0 -> ArrayOfBytesWithFinalizer(((i / 100) % 10) * 80) // ~1-100 pointers
else -> ArrayOfBytes(((i / 100) % 10) * 80) // ~1-100 pointers
}
Blackhole.consume(obj)
}
}
class PeakRSSChecker(private val rssDiffLimitBytes: Long) {
// On Linux, the child process might immediately commit the same amount of memory as the parent.
// So, measure difference between peak RSS measurements.
private val initialBytes = MemoryUsageInfo.peakResidentSetSizeBytes.also {
check(it != 0L) { "Error trying to obtain peak RSS. Check if current platform is supported" }
}
fun check(): Long {
val diffBytes = MemoryUsageInfo.peakResidentSetSizeBytes - initialBytes
check(diffBytes <= rssDiffLimitBytes) { "Increased peak RSS by $diffBytes bytes which is more than $rssDiffLimitBytes" }
return diffBytes
}
}
fun main() {
// allocateGarbage allocates ~50MiB. Make total amount per mutator ~5GiB.
val count = 100
// Total amount overall is ~20GiB
val threadCount = 4
val progressReportsCount = 10
// Setting the initial boundary to ~50MiB. The scheduler will adapt this value
// dynamically with no upper limit.
kotlin.native.runtime.GC.targetHeapBytes = 50_000_000
kotlin.native.runtime.GC.minHeapBytes = 50_000_000
// Limit memory usage at ~200MiB. 4 times the initial boundary yet still
// way less than total expected allocated amount.
val peakRSSChecker = PeakRSSChecker(200_000_000L)
val workers = Array(threadCount) { Worker.start() }
val globalCount = AtomicInt(0)
val finalGlobalCount = count * workers.size
workers.forEach {
it.executeAfter(0L) {
for (i in 0 until count) {
allocateGarbage()
peakRSSChecker.check()
globalCount.getAndAdd(1)
}
}
}
val reportStep = finalGlobalCount / progressReportsCount
var lastReportCount = -reportStep
while (true) {
val diffPeakRss = peakRSSChecker.check()
val currentCount = globalCount.value
if (currentCount >= finalGlobalCount) {
break
}
if (lastReportCount + reportStep <= currentCount) {
println("Allocating iteration $currentCount of $finalGlobalCount with peak RSS increase: $diffPeakRss bytes")
lastReportCount = currentCount
}
}
workers.forEach {
it.requestTermination().result
}
peakRSSChecker.check()
}
@@ -1,25 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package runtime.memory.throw_cleanup
import kotlin.test.*
@Test fun runTest() {
foo(false)
try {
foo(true)
} catch (e: Error) {
println("Ok")
}
}
fun foo(b: Boolean): Any {
var result = Any()
if (b) {
throw Error()
}
return result
}
@@ -1,63 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package runtime.memory.var1
import kotlin.test.*
class Integer(val value: Int) {
operator fun inc() = Integer(value + 1)
}
fun foo(x: Any, y: Any) {
x.use()
y.use()
}
@Test fun runTest1() {
var x = Integer(0)
for (i in 0..1) {
val c = Integer(0)
if (i == 0) x = c
}
// x refcount is 1.
foo(x, ++x)
}
fun Any?.use() {
var x = this
}
@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
@kotlin.native.internal.CanBePrecreated
object CompileTime {
const val int = Int.MIN_VALUE
const val byte = Byte.MIN_VALUE
const val short = Short.MIN_VALUE
const val long = Long.MIN_VALUE
const val boolean = true
const val float = 1.0f
const val double = 1.0
const val char = Char.MIN_VALUE
}
class AClass {
companion object {}
}
@Test fun runTest2() {
assertEquals(Int.MIN_VALUE, CompileTime.int)
assertEquals(Byte.MIN_VALUE, CompileTime.byte)
assertEquals(Short.MIN_VALUE, CompileTime.short)
assertEquals(Long.MIN_VALUE, CompileTime.long)
assertEquals(true, CompileTime.boolean)
assertEquals(1.0f, CompileTime.float)
assertEquals(1.0, CompileTime.double)
assertEquals(Char.MIN_VALUE, CompileTime.char)
}
@@ -1,31 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package runtime.memory.var2
import kotlin.test.*
@Test fun runTest() {
var x = Any()
for (i in 0..1) {
val c = Any()
if (i == 0) x = c
}
// x refcount is 1.
val y = try {
x
} finally {
x = Any()
}
y.use()
}
fun Any?.use() {
var x = this
}
@@ -1,33 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package runtime.memory.var3
import kotlin.test.*
@Test fun runTest() {
foo().use()
}
fun foo(): Any {
var x = Any()
for (i in 0..1) {
val c = Any()
if (i == 0) x = c
}
// x refcount is 1.
try {
return x
} finally {
x = Any()
}
}
fun Any?.use() {
var x = this
}
@@ -1,33 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package runtime.memory.var4
import kotlin.test.*
@Test fun runTest() {
var x = Error()
for (i in 0..1) {
val c = Error()
if (i == 0) x = c
}
// x refcount is 1.
try {
try {
throw x
} finally {
x = Error()
}
} catch (e: Error) {
e.use()
}
}
fun Any?.use() {
var x = this
}
@@ -1,46 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
@file:OptIn(kotlin.experimental.ExperimentalNativeApi::class)
package runtime.memory.weak0
import kotlin.test.*
import kotlin.native.ref.*
data class Data(val s: String)
fun localWeak(): WeakReference<Data> {
val x = Data("Hello")
val weak = WeakReference(x)
println(weak.get())
return weak
}
fun multiWeak(): Array<WeakReference<Data>> {
val x = Data("Hello")
val weaks = Array(100, { WeakReference(x) } )
weaks.forEach {
it -> if (it.get()?.s != "Hello") throw Error("bad reference")
}
return weaks
}
@OptIn(kotlin.native.runtime.NativeRuntimeApi::class)
@Test fun runTest() {
val weak = localWeak()
kotlin.native.runtime.GC.collect()
val value = weak.get()
println(value?.toString())
val weaks = multiWeak()
kotlin.native.runtime.GC.collect()
weaks.forEach {
it -> if (it.get()?.s != null) throw Error("not null")
}
println("OK")
}
@@ -1,3 +0,0 @@
Data(s=Hello)
null
OK
@@ -1,22 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
@file:OptIn(kotlin.experimental.ExperimentalNativeApi::class)
package runtime.memory.weak1
import kotlin.test.*
import kotlin.native.ref.*
class Node(var next: Node?)
@Test fun runTest1() {
val node1 = Node(null)
val node2 = Node(node1)
node1.next = node2
kotlin.native.ref.WeakReference(node1)
println("OK")
}
@@ -1 +0,0 @@
OK
@@ -1,25 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
@file:OptIn(ObsoleteWorkersApi::class)
package runtime.workers.enum_identity
import kotlin.test.*
import kotlin.native.concurrent.*
enum class A {
A, B
}
data class Foo(val kind: A)
// Enums are shared between threads so identity should be kept.
@Test
fun runTest() {
val result = Worker.start().execute(TransferMode.SAFE, { Foo(A.B) }, { input ->
input.kind == A.B
}).result
println(result)
}
@@ -1 +0,0 @@
true
@@ -1,30 +0,0 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@file:OptIn(FreezingIsDeprecated::class)
package runtime.workers.freeze_disabled
import kotlin.test.*
import kotlin.native.concurrent.*
// this test is ran with disabled freezing, so no mutability checks are done
class A(var x:Int)
@Test
fun testClassNotFrozen(){
val a = A(1)
a.freeze()
a.x = 2
assertEquals(a.x, 2)
}
@Test
fun testArrayNotFrozen(){
val a = arrayOf(1, 2, 3, 4, 5)
a.freeze()
a[0] = 6
assertEquals(a[0], 6)
}
@@ -1,32 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
import kotlin.test.*
object Foo {
val bar = Bar()
}
class Bar {
val f by lazy {
foo()
}
fun foo() = 123
}
fun printAll() {
println(Foo.bar.f)
}
// This test is extracted from the real problem found in kotlinx.serialization, where zeroing out
// initializer field in frozen lazy object led to the crash, induced by breaking frozen objects'
// invariant (initializer end up in the same container as the lazy object itself, so it was destroyed
// earlier than it should when reference counter was decremented).
@OptIn(kotlin.native.runtime.NativeRuntimeApi::class)
fun main(args: Array<String>) {
printAll()
kotlin.native.runtime.GC.collect()
println("OK")
}
@@ -1,2 +0,0 @@
123
OK
@@ -1,56 +0,0 @@
@file:OptIn(kotlin.experimental.ExperimentalNativeApi::class, FreezingIsDeprecated::class, kotlin.native.runtime.NativeRuntimeApi::class)
import kotlin.native.concurrent.*
import kotlin.native.ref.*
import kotlin.test.*
fun main() {
test1()
test2()
}
fun test1() {
ensureGetsCollectedFrozenAndNotFrozen { LazyCapturesThis() }
ensureGetsCollectedFrozenAndNotFrozen {
val l = LazyCapturesThis()
l.bar
l
}
ensureGetsCollected {
val l = LazyCapturesThis().freeze()
l.bar
l
}
}
class LazyCapturesThis {
fun foo() = 42
val bar by lazy { foo() }
}
fun test2() {
ensureGetsCollectedFrozenAndNotFrozen { Throwable() }
ensureGetsCollectedFrozenAndNotFrozen {
val throwable = Throwable()
throwable.getStackTrace()
throwable
}
ensureGetsCollected {
val throwable = Throwable().freeze()
throwable.getStackTrace()
throwable
}
}
fun ensureGetsCollectedFrozenAndNotFrozen(create: () -> Any) {
ensureGetsCollected { create().freeze() }
ensureGetsCollected(create)
}
fun ensureGetsCollected(create: () -> Any) {
val ref = makeWeakRef(create)
kotlin.native.runtime.GC.collect()
assertNull(ref.get())
}
fun makeWeakRef(create: () -> Any) = WeakReference(create())
@@ -1,13 +0,0 @@
@file:OptIn(kotlin.experimental.ExperimentalNativeApi::class, ObsoleteWorkersApi::class)
import kotlin.native.concurrent.*
import kotlin.native.Platform
import kotlinx.cinterop.*
fun main() {
Platform.isMemoryLeakCheckerActive = true
val worker = Worker.start()
// Make sure worker is initialized.
worker.execute(TransferMode.SAFE, {}, {}).result;
StableRef.create(Any())
worker.requestTermination().result
}
@@ -1,12 +0,0 @@
@file:OptIn(kotlin.experimental.ExperimentalNativeApi::class, ObsoleteWorkersApi::class)
import kotlin.native.concurrent.*
import kotlin.native.Platform
import kotlinx.cinterop.*
fun main() {
Platform.isMemoryLeakCheckerActive = true
val worker = Worker.start()
// Make sure worker is initialized.
worker.execute(TransferMode.SAFE, {}, {}).result;
StableRef.create(Any())
}
@@ -1,48 +0,0 @@
@file:OptIn(FreezingIsDeprecated::class, ObsoleteWorkersApi::class)
package runtime.workers.mutableData1
import kotlin.native.concurrent.*
import kotlin.test.*
// See https://youtrack.jetbrains.com/issue/KT-39145
@Test
fun kt39145_1() = withWorker {
execute(TransferMode.SAFE, { "test" }) {
val data = MutableData(1_000)
val bytes = byteArrayOf(0, 10, 20, 30)
data.append(bytes, 0, 4)
assertEquals(4, data.size)
assertContentsEquals(bytes, data)
}.result
}
@Test
fun kt39145_2() = withWorker {
val externalData = MutableData(1_000)
execute(TransferMode.SAFE, { externalData }) {
val bytes = byteArrayOf(0, 10, 20, 30)
it.append(bytes, 0, 4)
assertEquals(4, it.size)
assertContentsEquals(bytes, it)
}.result
}
@Test
fun kt39145_3() {
val mainThreadData = MutableData(1_000)
val bytes = byteArrayOf(0, 10, 20, 30)
mainThreadData.append(bytes, 0, 4)
assertEquals(4, mainThreadData.size)
assertContentsEquals(bytes, mainThreadData)
}
private fun assertContentsEquals(expected: ByteArray, actual: MutableData) {
assertEquals(expected.size, actual.size)
expected.forEachIndexed { index, byte ->
assertEquals(byte, actual.get(index))
}
}
@@ -1,186 +0,0 @@
@file:OptIn(FreezingIsDeprecated::class, kotlin.experimental.ExperimentalNativeApi::class, kotlin.native.runtime.NativeRuntimeApi::class, ObsoleteWorkersApi::class, kotlinx.cinterop.ExperimentalForeignApi::class)
package runtime.workers.worker10
import kotlin.test.*
import kotlin.concurrent.AtomicReference
import kotlin.concurrent.AtomicInt
import kotlin.concurrent.*
import kotlin.native.concurrent.*
import kotlin.native.ref.WeakReference
import kotlinx.cinterop.StableRef
class Data(val x: Int)
val topInt = 1
val topString = "string"
var topStringVar = "string"
val topSharedStringWithGetter: String
get() = "top"
val topData = Data(42)
val topSharedData = Data(43)
@Test fun runTest1() {
val worker = Worker.start()
assertEquals(1, topInt)
assertEquals("string", topString)
assertEquals(42, topData.x)
assertEquals(43, topSharedData.x)
assertEquals("top", topSharedStringWithGetter)
worker.execute(TransferMode.SAFE, { -> }, {
it -> topInt == 1
}).consume {
result -> assertEquals(true, result)
}
worker.execute(TransferMode.SAFE, { -> }, {
it -> topString == "string"
}).consume {
result -> assertEquals(true, result)
}
worker.execute(TransferMode.SAFE, { -> }, {
it -> try {
topStringVar == "string"
} catch (e: IncorrectDereferenceException) {
false
}
}).consume {
result -> assertEquals(Platform.memoryModel != MemoryModel.STRICT, result)
}
worker.execute(TransferMode.SAFE, { -> }, {
it -> try {
topSharedStringWithGetter == "top"
} catch (e: IncorrectDereferenceException) {
false
}
}).consume {
result -> assertEquals(true, result)
}
worker.execute(TransferMode.SAFE, { -> }, {
it -> try {
topData.x == 42
} catch (e: IncorrectDereferenceException) {
false
}
}).consume {
result -> assertEquals(Platform.memoryModel != MemoryModel.STRICT, result)
}
worker.execute(TransferMode.SAFE, { -> }, {
it -> try {
topSharedData.x == 43
} catch (e: Throwable) {
false
}
}).consume {
result -> assertEquals(true, result)
}
worker.requestTermination().result
println("OK")
}
val atomicRef = AtomicReference<Any?>(Any().freeze())
val stableRef = StableRef.create(Any().freeze())
val semaphore = AtomicInt(0)
@Test fun runTest2() {
semaphore.value = 0
val worker = Worker.start()
val future = worker.execute(TransferMode.SAFE, { null }) {
val value = atomicRef.value
semaphore.incrementAndGet()
while (semaphore.value != 2) {}
println(value.toString() != "")
}
while (semaphore.value != 1) {}
atomicRef.value = null
kotlin.native.runtime.GC.collect()
semaphore.incrementAndGet()
future.result
worker.requestTermination().result
}
@Test fun runTest3() {
semaphore.value = 0
val worker = Worker.start()
val future = worker.execute(TransferMode.SAFE, { null }) {
val value = stableRef.get()
semaphore.incrementAndGet()
while (semaphore.value != 2) {}
println(value.toString() != "")
}
while (semaphore.value != 1) {}
stableRef.dispose()
kotlin.native.runtime.GC.collect()
semaphore.incrementAndGet()
future.result
worker.requestTermination().result
}
fun <T: Any> ensureWeakIs(weak: WeakReference<T>, expected: T?) {
assertEquals(expected, weak.get())
}
val stableHolder1 = StableRef.create(("hello" to "world").freeze())
@Test fun runTest4() {
val worker = Worker.start()
semaphore.value = 0
val future = worker.execute(TransferMode.SAFE, { WeakReference(stableHolder1.get()) }) {
ensureWeakIs(it, "hello" to "world")
semaphore.incrementAndGet()
while (semaphore.value != 2) {}
kotlin.native.runtime.GC.collect()
ensureWeakIs(it, null)
}
while (semaphore.value != 1) {}
stableHolder1.dispose()
kotlin.native.runtime.GC.collect()
semaphore.incrementAndGet()
future.result
worker.requestTermination().result
}
val stableHolder2 = StableRef.create(("hello" to "world").freeze())
@Test fun runTest5() {
val worker = Worker.start()
semaphore.value = 0
val future = worker.execute(TransferMode.SAFE, { WeakReference(stableHolder2.get()) }) {
val value = it.get()
semaphore.incrementAndGet()
while (semaphore.value != 2) {}
kotlin.native.runtime.GC.collect()
assertEquals("hello" to "world", value)
}
while (semaphore.value != 1) {}
stableHolder2.dispose()
kotlin.native.runtime.GC.collect()
semaphore.incrementAndGet()
future.result
worker.requestTermination().result
}
val atomicRef2 = AtomicReference<Any?>(Any().freeze())
@Test fun runTest6() {
semaphore.value = 0
val worker = Worker.start()
val future = worker.execute(TransferMode.SAFE, { null }) {
val value = atomicRef2.compareAndExchange(null, null)
semaphore.incrementAndGet()
while (semaphore.value != 2) {}
assertEquals(true, value.toString() != "")
}
while (semaphore.value != 1) {}
atomicRef2.value = null
kotlin.native.runtime.GC.collect()
semaphore.incrementAndGet()
future.result
worker.requestTermination().result
}
@@ -1,3 +0,0 @@
OK
true
true
@@ -1,22 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
@file:OptIn(FreezingIsDeprecated::class, ObsoleteWorkersApi::class)
import kotlin.test.*
import kotlin.native.concurrent.*
// Ensure that termination of current worker on main thread doesn't lead to problems.
fun main() {
val worker = Worker.current
val future = worker.requestTermination(false)
worker.processQueue()
assertEquals(future.state, FutureState.COMPUTED)
future.consume {}
// After termination request this worker is no longer addressable.
assertFailsWith<IllegalStateException> { worker.executeAfter(0, {
println("BUG!")
})}
}
@@ -1,40 +0,0 @@
@file:OptIn(FreezingIsDeprecated::class, ObsoleteWorkersApi::class)
package runtime.workers.worker_exceptions
import kotlin.test.*
import kotlin.native.concurrent.*
@Test
fun testExecuteAfterStartQuiet() {
val worker = Worker.start(errorReporting = false)
worker.executeAfter(0L, {
throw Error("testExecuteAfterStartQuiet error")
}.freeze())
worker.requestTermination().result
}
@Test
fun testExecuteStart() {
val worker = Worker.start()
val future = worker.execute(TransferMode.SAFE, {}) {
throw Error("testExecuteStart error")
}
assertFailsWith<Throwable> {
future.result
}
worker.requestTermination().result
}
@Test
fun testExecuteStartQuiet() {
val worker = Worker.start(errorReporting = false)
val future = worker.execute(TransferMode.SAFE, {}) {
throw Error("testExecuteStartQuiet error")
}
assertFailsWith<Throwable> {
future.result
}
worker.requestTermination().result
}
@@ -1,12 +0,0 @@
@file:OptIn(FreezingIsDeprecated::class, ObsoleteWorkersApi::class)
import kotlin.native.concurrent.*
fun main() {
val worker = Worker.start()
worker.executeAfter(0L, {
throw Error("some error")
}.freeze())
worker.requestTermination().result
println("Will not happen")
}
@@ -1,11 +0,0 @@
@file:OptIn(FreezingIsDeprecated::class, ObsoleteWorkersApi::class)
import kotlin.native.concurrent.*
fun main() {
Worker.current.executeAfter(0L, {
throw Error("some error")
}.freeze())
Worker.current.processQueue()
println("Will not happen")
}
@@ -1,17 +0,0 @@
@file:OptIn(FreezingIsDeprecated::class, kotlin.experimental.ExperimentalNativeApi::class, ObsoleteWorkersApi::class)
import kotlin.native.concurrent.*
fun main() {
setUnhandledExceptionHook({ _: Throwable ->
println("hook called")
}.freeze())
val worker = Worker.start()
worker.executeAfter(0L, {
throw Error("some error")
}.freeze())
worker.requestTermination().result
println("Will happen")
}
@@ -1,15 +0,0 @@
@file:OptIn(FreezingIsDeprecated::class, kotlin.experimental.ExperimentalNativeApi::class, ObsoleteWorkersApi::class)
import kotlin.native.concurrent.*
fun main() {
setUnhandledExceptionHook({ _: Throwable ->
println("hook called")
}.freeze())
Worker.current.executeAfter(0L, {
throw Error("some error")
}.freeze())
Worker.current.processQueue()
println("Will happen")
}
@@ -1,22 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
@file:OptIn(kotlin.experimental.ExperimentalNativeApi::class, ObsoleteWorkersApi::class)
import kotlin.native.concurrent.*
import kotlin.native.Platform
@ThreadLocal
var x = Any()
fun main() {
Platform.isMemoryLeakCheckerActive = true
val worker = Worker.start()
worker.execute(TransferMode.SAFE, {}) {
println(x) // Make sure x is initialized
}.result
worker.requestTermination().result
}