All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jvmTest.selects.SelectDeadlockLFStressTest.kt Maven / Gradle / Ivy

There is a newer version: 1.4.2-native-mt
Show newest version
/*
 * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
 */

package kotlinx.coroutines.selects

import kotlinx.atomicfu.*
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
import org.junit.*
import org.junit.Ignore
import org.junit.Test
import kotlin.math.*
import kotlin.test.*

/**
 * A stress-test on lock-freedom of select sending/receiving into opposite channels.
 */
class SelectDeadlockLFStressTest : TestBase() {
    private val env = LockFreedomTestEnvironment("SelectDeadlockLFStressTest", allowSuspendedThreads = 1)
    private val nSeconds = 5 * stressTestMultiplier

    private val c1 = Channel()
    private val c2 = Channel()

    @Test
    fun testLockFreedom() = testScenarios(
        "s1r2",
        "s2r1",
        "r1s2",
        "r2s1"
    )

    private fun testScenarios(vararg scenarios: String) {
        env.onCompletion {
            c1.cancel(TestCompleted())
            c2.cancel(TestCompleted())
        }
        val t = scenarios.mapIndexed { i, scenario ->
            val idx = i + 1L
            TestDef(idx, "$idx [$scenario]", scenario)
        }
        t.forEach { it.test() }
        env.performTest(nSeconds) {
            t.forEach { println(it) }
        }
    }

    private inner class TestDef(
        var sendIndex: Long = 0L,
        val name: String,
        scenario: String
    ) {
        var receiveIndex = 0L

        val clauses: List.() -> Unit> = ArrayList.() -> Unit>().apply {
            require(scenario.length % 2 == 0)
            for (i in scenario.indices step 2) {
                val ch = when (val c = scenario[i + 1]) {
                    '1' -> c1
                    '2' -> c2
                    else -> error("Channel '$c'")
                }
                val clause = when (val op = scenario[i]) {
                    's' -> fun SelectBuilder.() { sendClause(ch) }
                    'r' -> fun SelectBuilder.() { receiveClause(ch) }
                    else -> error("Operation '$op'")
                }
                add(clause)
            }
        }

        fun test() = env.testThread(name) {
            doSendReceive()
        }

        private suspend fun doSendReceive() {
            try {
                select {
                    for (clause in clauses) clause()
                }
            } catch (e: TestCompleted) {
                assertTrue(env.isCompleted)
            }
        }

        private fun SelectBuilder.sendClause(c: Channel) =
            c.onSend(sendIndex) {
                sendIndex += 4L
            }

        private fun SelectBuilder.receiveClause(c: Channel) =
            c.onReceive { i ->
                receiveIndex = max(i, receiveIndex)
            }

        override fun toString(): String = "$name: send=$sendIndex, received=$receiveIndex"
    }

    private class TestCompleted : CancellationException()
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy