blob: 4528a4c085f5dd4799ce79f1f48ce9988935e52f [file] [log] [blame]
Roman Elizarov174c6962017-02-28 17:36:51 +03001/*
2 * Copyright 2016-2017 JetBrains s.r.o.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package kotlinx.coroutines.experimental.selects
18
19import kotlinx.coroutines.experimental.*
Roman Elizarov9fe5f462018-02-21 19:05:52 +030020import kotlinx.coroutines.experimental.sync.*
21import org.junit.*
22import org.junit.Assert.*
23import kotlin.coroutines.experimental.*
Roman Elizarov174c6962017-02-28 17:36:51 +030024
Roman Elizarovebe18b42017-02-28 17:50:55 +030025class SelectPhilosophersStressTest : TestBase() {
26 val TEST_DURATION = 3000L * stressTestMultiplier
Roman Elizarov174c6962017-02-28 17:36:51 +030027
28 val n = 10 // number of philosophers
29 val forks = Array<Mutex>(n) { Mutex() }
30
31 suspend fun eat(id: Int, desc: String) {
32 val left = forks[id]
33 val right = forks[(id + 1) % n]
34 while (true) {
35 val pair = selectUnbiased<Pair<Mutex, Mutex>> {
36 left.onLock(desc) { left to right }
37 right.onLock(desc) { right to left }
38 }
39 if (pair.second.tryLock(desc)) break
40 pair.first.unlock(desc)
41 pair.second.lock(desc)
42 if (pair.first.tryLock(desc)) break
43 pair.second.unlock(desc)
44 }
45 assertTrue(left.isLocked && right.isLocked)
46 // om, nom, nom --> eating!!!
47 right.unlock(desc)
48 left.unlock(desc)
49 }
50
51 @Test
52 fun testPhilosophers() = runBlocking<Unit> {
Roman Elizarovd3d335b2017-10-21 17:43:53 +030053 println("--- SelectPhilosophersStressTest")
Roman Elizarov174c6962017-02-28 17:36:51 +030054 val timeLimit = System.currentTimeMillis() + TEST_DURATION
55 val philosophers = List<Deferred<Int>>(n) { id ->
56 async(CommonPool) {
57 val desc = "Philosopher $id"
58 var eatsCount = 0
59 while (System.currentTimeMillis() < timeLimit) {
60 eat(id, desc)
61 eatsCount++
62 yield()
63 }
64 println("Philosopher $id done, eats $eatsCount times")
65 eatsCount
66 }
67 }
Roman Elizarov43e3af72017-07-21 16:01:31 +030068 val debugJob = launch(coroutineContext) {
Roman Elizarov174c6962017-02-28 17:36:51 +030069 delay(3 * TEST_DURATION)
70 println("Test is failing. Lock states are:")
71 forks.withIndex().forEach { (id, mutex) -> println("$id: $mutex") }
72 }
73 val eats = withTimeout(5 * TEST_DURATION) { philosophers.map { it.await() } }
74 debugJob.cancel()
75 eats.withIndex().forEach { (id, eats) ->
76 assertTrue("$id shall not starve", eats > 0)
77 }
78 }
79}