1 /* 2 * Copyright 2016-2021 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.coroutines.selects.* 8 import kotlinx.coroutines.sync.* 9 import kotlin.test.* 10 11 class AtomicCancellationCommonTest : TestBase() { 12 @Test <lambda>null13 fun testCancellableLaunch() = runTest { 14 expect(1) 15 val job = launch { 16 expectUnreached() // will get cancelled before start 17 } 18 expect(2) 19 job.cancel() 20 finish(3) 21 } 22 23 @Test <lambda>null24 fun testAtomicLaunch() = runTest { 25 expect(1) 26 val job = launch(start = CoroutineStart.ATOMIC) { 27 finish(4) // will execute even after it was cancelled 28 } 29 expect(2) 30 job.cancel() 31 expect(3) 32 } 33 34 @Test <lambda>null35 fun testUndispatchedLaunch() = runTest { 36 expect(1) 37 assertFailsWith<CancellationException> { 38 withContext(Job()) { 39 cancel() 40 launch(start = CoroutineStart.UNDISPATCHED) { 41 expect(2) 42 yield() 43 expectUnreached() 44 } 45 } 46 } 47 finish(3) 48 } 49 50 @Test <lambda>null51 fun testUndispatchedLaunchWithUnconfinedContext() = runTest { 52 expect(1) 53 assertFailsWith<CancellationException> { 54 withContext(Dispatchers.Unconfined + Job()) { 55 cancel() 56 launch(start = CoroutineStart.UNDISPATCHED) { 57 expect(2) 58 yield() 59 expectUnreached() 60 } 61 } 62 } 63 finish(3) 64 } 65 66 @Test <lambda>null67 fun testDeferredAwaitCancellable() = runTest { 68 expect(1) 69 val deferred = async { // deferred, not yet complete 70 expect(4) 71 "OK" 72 } 73 assertEquals(false, deferred.isCompleted) 74 var job: Job? = null 75 launch { // will cancel job as soon as deferred completes 76 expect(5) 77 assertEquals(true, deferred.isCompleted) 78 job!!.cancel() 79 } 80 job = launch(start = CoroutineStart.UNDISPATCHED) { 81 expect(2) 82 try { 83 deferred.await() // suspends 84 expectUnreached() // will not execute -- cancelled while dispatched 85 } finally { 86 finish(7) // but will execute finally blocks 87 } 88 } 89 expect(3) // continues to execute when the job suspends 90 yield() // to deferred & canceller 91 expect(6) 92 } 93 94 @Test testJobJoinCancellablenull95 fun testJobJoinCancellable() = runTest { 96 expect(1) 97 val jobToJoin = launch { // not yet complete 98 expect(4) 99 } 100 assertEquals(false, jobToJoin.isCompleted) 101 var job: Job? = null 102 launch { // will cancel job as soon as jobToJoin completes 103 expect(5) 104 assertEquals(true, jobToJoin.isCompleted) 105 job!!.cancel() 106 } 107 job = launch(start = CoroutineStart.UNDISPATCHED) { 108 expect(2) 109 try { 110 jobToJoin.join() // suspends 111 expectUnreached() // will not execute -- cancelled while dispatched 112 } finally { 113 finish(7) // but will execute finally blocks 114 } 115 } 116 expect(3) // continues to execute when the job suspends 117 yield() // to jobToJoin & canceller 118 expect(6) 119 } 120 121 @Test <lambda>null122 fun testLockCancellable() = runTest { 123 expect(1) 124 val mutex = Mutex(true) // locked mutex 125 val job = launch(start = CoroutineStart.UNDISPATCHED) { 126 expect(2) 127 mutex.lock() // suspends 128 expectUnreached() // should NOT execute because of cancellation 129 } 130 expect(3) 131 mutex.unlock() // unlock mutex first 132 job.cancel() // cancel the job next 133 yield() // now yield 134 finish(4) 135 } 136 137 @Test <lambda>null138 fun testSelectLockCancellable() = runTest { 139 expect(1) 140 val mutex = Mutex(true) // locked mutex 141 val job = launch(start = CoroutineStart.UNDISPATCHED) { 142 expect(2) 143 select<String> { // suspends 144 mutex.onLock { 145 expect(4) 146 "OK" 147 } 148 } 149 expectUnreached() // should NOT execute because of cancellation 150 } 151 expect(3) 152 mutex.unlock() // unlock mutex first 153 job.cancel() // cancel the job next 154 yield() // now yield 155 finish(4) 156 } 157 } 158