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 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
|
/*
* Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
package kotlinx.coroutines.exceptions
import kotlinx.coroutines.*
import org.junit.*
import org.junit.Test
import java.io.*
import java.util.concurrent.*
import kotlin.coroutines.*
import kotlin.test.*
class WithContextCancellationStressTest : TestBase() {
private val iterations = 15_000 * stressTestMultiplier
private val pool = newFixedThreadPoolContext(3, "WithContextCancellationStressTest")
@After
fun tearDown() {
pool.close()
}
@Test
fun testConcurrentCancellation() = runBlocking {
var ioException = 0
var arithmeticException = 0
var aioobException = 0
repeat(iterations) {
val barrier = CyclicBarrier(4)
val ctx = pool + NonCancellable
val jobWithContext = async(ctx) {
withContext(wrapperDispatcher(coroutineContext)) {
barrier.await()
throw IOException()
}
}
val cancellerJob = async(ctx) {
barrier.await()
jobWithContext.cancel(ArithmeticException())
}
val cancellerJob2 = async(ctx) {
barrier.await()
jobWithContext.cancel(ArrayIndexOutOfBoundsException())
}
barrier.await()
val aeCancelled = cancellerJob.await()
val aioobCancelled = cancellerJob2.await()
try {
jobWithContext.await()
} catch (e: Exception) {
when (e) {
is IOException -> {
++ioException
e.checkSuppressed(aeException = aeCancelled, aioobException = aioobCancelled)
}
is ArithmeticException -> {
++arithmeticException
e.checkSuppressed(ioException = true, aioobException = aioobCancelled)
}
is ArrayIndexOutOfBoundsException -> {
++aioobException
e.checkSuppressed(ioException = true, aeException = aeCancelled)
}
else -> error("Unexpected exception $e")
}
}
}
require(ioException > 0) { "At least one IOException expected" }
require(arithmeticException > 0) { "At least one ArithmeticException expected" }
require(aioobException > 0) { "At least one ArrayIndexOutOfBoundsException expected" }
}
private fun wrapperDispatcher(context: CoroutineContext): CoroutineContext {
val dispatcher = context[ContinuationInterceptor] as CoroutineDispatcher
return object : CoroutineDispatcher() {
override fun dispatch(context: CoroutineContext, block: Runnable) {
dispatcher.dispatch(context, block)
}
}
}
private fun Throwable.checkSuppressed(
ioException: Boolean = false,
aeException: Boolean = false,
aioobException: Boolean = false
) {
val suppressed: Array<Throwable> = suppressed
try {
if (ioException) {
assertTrue(suppressed.any { it is IOException }, "IOException should be present: $this")
}
if (aeException) {
assertTrue(suppressed.any { it is ArithmeticException }, "ArithmeticException should be present: $this")
}
if (aioobException) {
assertTrue(
suppressed.any { it is ArrayIndexOutOfBoundsException },
"ArrayIndexOutOfBoundsException should be present: $this"
)
}
} catch (e: Throwable) {
val a =2
}
}
}
|