File: SelectPhilosophersStressTest.kt

package info (click to toggle)
kotlinx-coroutines 1.0.1-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 4,628 kB
  • sloc: xml: 418; sh: 322; javascript: 60; makefile: 17; java: 8
file content (65 lines) | stat: -rw-r--r-- 2,174 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
/*
 * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
 */

package kotlinx.coroutines.selects

import kotlinx.coroutines.*
import kotlinx.coroutines.sync.*
import org.junit.*
import org.junit.Assert.*

class SelectPhilosophersStressTest : TestBase() {
    private val TEST_DURATION = 3000L * stressTestMultiplier

    val n = 10 // number of philosophers
    private val forks = Array(n) { Mutex() }

    private suspend fun eat(id: Int, desc: String) {
        val left = forks[id]
        val right = forks[(id + 1) % n]
        while (true) {
            val pair = selectUnbiased<Pair<Mutex, Mutex>> {
                left.onLock(desc) { left to right }
                right.onLock(desc) { right to left }
            }
            if (pair.second.tryLock(desc)) break
            pair.first.unlock(desc)
            pair.second.lock(desc)
            if (pair.first.tryLock(desc)) break
            pair.second.unlock(desc)
        }
        assertTrue(left.isLocked && right.isLocked)
        // om, nom, nom --> eating!!!
        right.unlock(desc)
        left.unlock(desc)
    }

    @Test
    fun testPhilosophers() = runBlocking<Unit> {
        val timeLimit = System.currentTimeMillis() + TEST_DURATION
        val philosophers = List<Deferred<Int>>(n) { id ->
            async {
                val desc = "Philosopher $id"
                var eatsCount = 0
                while (System.currentTimeMillis() < timeLimit) {
                    eat(id, desc)
                    eatsCount++
                    yield()
                }
                println("Philosopher $id done, eats $eatsCount times")
                eatsCount
            }
        }
        val debugJob = launch {
            delay(3 * TEST_DURATION)
            println("Test is failing. Lock states are:")
            forks.withIndex().forEach { (id, mutex) -> println("$id: $mutex") }
        }
        val eats = withTimeout(5 * TEST_DURATION) { philosophers.map { it.await() } }
        debugJob.cancel()
        eats.withIndex().forEach { (id, eats) ->
            assertTrue("$id shall not starve", eats > 0)
        }
    }
}