1
+ /*
2
+ * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3
+ */
4
+
5
+ package kotlinx.coroutines
6
+
7
+ import kotlinx.atomicfu.*
8
+ import org.junit.*
9
+ import java.util.concurrent.*
10
+ import kotlin.test.*
11
+ import kotlin.test.Test
12
+
13
+ class CancellableContinuationResumeCloseStressTest : TestBase () {
14
+ private val dispatcher =
15
+ newFixedThreadPoolContext(2 , " CancellableContinuationResumeCloseStressTest" )
16
+ private val startBarrier = CyclicBarrier (3 )
17
+ private val doneBarrier = CyclicBarrier (2 )
18
+ private val nRepeats = 1_000 * stressTestMultiplier
19
+
20
+ private val closed = atomic(false )
21
+ private var returnedOk = false
22
+
23
+ @After
24
+ fun tearDown () {
25
+ dispatcher.close()
26
+ }
27
+
28
+ @Test
29
+ @Suppress(" BlockingMethodInNonBlockingContext" )
30
+ fun testStress () = runTest {
31
+ repeat(nRepeats) {
32
+ closed.value = false
33
+ returnedOk = false
34
+ val job = testJob()
35
+ startBarrier.await()
36
+ job.cancel() // (1) cancel job
37
+ job.join()
38
+ // check consistency
39
+ doneBarrier.await()
40
+ if (returnedOk) {
41
+ assertFalse(closed.value, " should not have closed resource -- returned Ok" )
42
+ } else {
43
+ assertTrue(closed.value, " should have closed resource -- was cancelled" )
44
+ }
45
+ }
46
+ }
47
+
48
+ private fun CoroutineScope.testJob (): Job = launch(dispatcher, start = CoroutineStart .ATOMIC ) {
49
+ val ok = resumeClose() // might be cancelled
50
+ assertEquals(" OK" , ok)
51
+ returnedOk = true
52
+ }
53
+
54
+ private suspend fun resumeClose () = suspendCancellableCoroutine<String > { cont ->
55
+ dispatcher.executor.execute {
56
+ startBarrier.await() // (2) resume at the same time
57
+ cont.resume(" OK" ) {
58
+ close()
59
+ }
60
+ doneBarrier.await()
61
+ }
62
+ startBarrier.await() // (3) return at the same time
63
+ }
64
+
65
+ fun close () {
66
+ assertFalse(closed.getAndSet(true ))
67
+ }
68
+ }
0 commit comments