blob: bb713b258d1a603490fc3f5abc728849485b59c0 [file] [log] [blame]
/*
* Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
package kotlinx.coroutines.sync
import kotlinx.coroutines.*
import kotlinx.coroutines.selects.*
import kotlin.test.*
class MutexStressTest : TestBase() {
@Test
fun testStress() = runBlocking(Dispatchers.Default) {
val n = 1000 * stressTestMultiplier
val k = 100
var shared = 0
val mutex = Mutex()
val jobs = List(n) {
launch {
repeat(k) {
mutex.lock()
shared++
mutex.unlock()
}
}
}
jobs.forEach { it.join() }
assertEquals(n * k, shared)
}
@Test
fun stressUnlockCancelRace() = runTest {
val n = 10_000 * stressTestMultiplier
val mutex = Mutex(true) // create a locked mutex
newSingleThreadContext("SemaphoreStressTest").use { pool ->
repeat (n) {
// Initially, we hold the lock and no one else can `lock`,
// otherwise it's a bug.
assertTrue(mutex.isLocked)
var job1EnteredCriticalSection = false
val job1 = launch(start = CoroutineStart.UNDISPATCHED) {
mutex.lock()
job1EnteredCriticalSection = true
mutex.unlock()
}
// check that `job1` didn't finish the call to `acquire()`
assertEquals(false, job1EnteredCriticalSection)
val job2 = launch(pool) {
mutex.unlock()
}
// Because `job2` executes in a separate thread, this
// cancellation races with the call to `unlock()`.
job1.cancelAndJoin()
job2.join()
assertFalse(mutex.isLocked)
mutex.lock()
}
}
}
@Test
fun stressUnlockCancelRaceWithSelect() = runTest {
val n = 10_000 * stressTestMultiplier
val mutex = Mutex(true) // create a locked mutex
newSingleThreadContext("SemaphoreStressTest").use { pool ->
repeat (n) {
// Initially, we hold the lock and no one else can `lock`,
// otherwise it's a bug.
assertTrue(mutex.isLocked)
var job1EnteredCriticalSection = false
val job1 = launch(start = CoroutineStart.UNDISPATCHED) {
select<Unit> {
mutex.onLock {
job1EnteredCriticalSection = true
mutex.unlock()
}
}
}
// check that `job1` didn't finish the call to `acquire()`
assertEquals(false, job1EnteredCriticalSection)
val job2 = launch(pool) {
mutex.unlock()
}
// Because `job2` executes in a separate thread, this
// cancellation races with the call to `unlock()`.
job1.cancelAndJoin()
job2.join()
assertFalse(mutex.isLocked)
mutex.lock()
}
}
}
}