Skip to content

Commit e3f2884

Browse files
committed
MPP: async, Deferred and CompletableDeferred
1 parent 19f4845 commit e3f2884

File tree

10 files changed

+547
-213
lines changed

10 files changed

+547
-213
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package kotlinx.coroutines.experimental
2+
3+
public expect interface CompletableDeferred<T> : Deferred<T> {
4+
public fun complete(value: T): Boolean
5+
public fun completeExceptionally(exception: Throwable): Boolean
6+
}
7+
8+
@Suppress("FunctionName", "EXPECTED_DECLARATION_WITH_DEFAULT_PARAMETER")
9+
public expect fun <T> CompletableDeferred(parent: Job? = null): CompletableDeferred<T>
10+
11+
@Suppress("FunctionName", "EXPECTED_DECLARATION_WITH_DEFAULT_PARAMETER")
12+
public expect fun <T> CompletableDeferred(value: T): CompletableDeferred<T>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package kotlinx.coroutines.experimental
2+
3+
import kotlin.coroutines.experimental.CoroutineContext
4+
5+
public expect interface Deferred<out T> : Job {
6+
public val isCompletedExceptionally: Boolean
7+
public suspend fun await(): T
8+
public fun getCompleted(): T
9+
public fun getCompletionExceptionOrNull(): Throwable?
10+
}
11+
12+
@Suppress("EXPECTED_DECLARATION_WITH_DEFAULT_PARAMETER")
13+
public expect fun <T> async(
14+
context: CoroutineContext = DefaultDispatcher,
15+
start: CoroutineStart = CoroutineStart.DEFAULT,
16+
parent: Job? = null,
17+
block: suspend CoroutineScope.() -> T
18+
): Deferred<T>

core/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/AsyncLazyTest.kt renamed to common/kotlinx-coroutines-core-common/src/test/kotlin/kotlinx/coroutines/experimental/CommonAsyncLazyTest.kt

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,15 @@
1414
* limitations under the License.
1515
*/
1616

17+
@file:Suppress("NAMED_ARGUMENTS_NOT_ALLOWED") // KT-21913
18+
1719
package kotlinx.coroutines.experimental
1820

19-
import org.junit.Test
20-
import java.io.IOException
21+
import kotlin.test.*
2122

22-
class AsyncLazyTest : TestBase() {
23+
class CommonAsyncLazyTest : TestBase() {
2324
@Test
24-
fun testSimple(): Unit = runBlocking {
25+
fun testSimple() = runTest {
2526
expect(1)
2627
val d = async(coroutineContext, CoroutineStart.LAZY) {
2728
expect(3)
@@ -37,7 +38,7 @@ class AsyncLazyTest : TestBase() {
3738
}
3839

3940
@Test
40-
fun testLazyDeferAndYield(): Unit = runBlocking {
41+
fun testLazyDeferAndYield() = runTest {
4142
expect(1)
4243
val d = async(coroutineContext, CoroutineStart.LAZY) {
4344
expect(3)
@@ -55,7 +56,7 @@ class AsyncLazyTest : TestBase() {
5556
}
5657

5758
@Test
58-
fun testLazyDeferAndYield2(): Unit = runBlocking {
59+
fun testLazyDeferAndYield2() = runTest {
5960
expect(1)
6061
val d = async(coroutineContext, CoroutineStart.LAZY) {
6162
expect(7)
@@ -81,52 +82,56 @@ class AsyncLazyTest : TestBase() {
8182
finish(8)
8283
}
8384

84-
@Test(expected = IOException::class)
85-
fun testSimpleException(): Unit = runBlocking {
85+
@Test
86+
fun testSimpleException() = runTest(
87+
expected = { it is TestException }
88+
) {
8689
expect(1)
8790
val d = async(coroutineContext, CoroutineStart.LAZY) {
8891
finish(3)
89-
throw IOException()
92+
throw TestException()
9093
}
9194
expect(2)
9295
check(!d.isActive && !d.isCompleted)
9396
d.await() // will throw IOException
9497
}
9598

96-
@Test(expected = IOException::class)
97-
fun testLazyDeferAndYieldException(): Unit = runBlocking {
99+
@Test
100+
fun testLazyDeferAndYieldException() = runTest(
101+
expected = { it is TestException }
102+
) {
98103
expect(1)
99104
val d = async(coroutineContext, CoroutineStart.LAZY) {
100105
expect(3)
101106
yield() // this has not effect, because parent coroutine is waiting
102107
finish(4)
103-
throw IOException()
108+
throw TestException()
104109
}
105110
expect(2)
106111
check(!d.isActive && !d.isCompleted)
107112
d.await() // will throw IOException
108113
}
109114

110115
@Test
111-
fun testCatchException(): Unit = runBlocking {
116+
fun testCatchException() = runTest {
112117
expect(1)
113118
val d = async(coroutineContext, CoroutineStart.LAZY) {
114119
expect(3)
115-
throw IOException()
120+
throw TestException()
116121
}
117122
expect(2)
118123
check(!d.isActive && !d.isCompleted)
119124
try {
120125
d.await() // will throw IOException
121-
} catch (e: IOException) {
126+
} catch (e: TestException) {
122127
check(!d.isActive && d.isCompleted && d.isCompletedExceptionally && !d.isCancelled)
123128
expect(4)
124129
}
125130
finish(5)
126131
}
127132

128133
@Test
129-
fun testStart(): Unit = runBlocking {
134+
fun testStart() = runTest {
130135
expect(1)
131136
val d = async(coroutineContext, CoroutineStart.LAZY) {
132137
expect(4)
@@ -145,8 +150,10 @@ class AsyncLazyTest : TestBase() {
145150
finish(6)
146151
}
147152

148-
@Test(expected = CancellationException::class)
149-
fun testCancelBeforeStart(): Unit = runBlocking {
153+
@Test
154+
fun testCancelBeforeStart() = runTest(
155+
expected = { it is JobCancellationException }
156+
) {
150157
expect(1)
151158
val d = async(coroutineContext, CoroutineStart.LAZY) {
152159
expectUnreached()
@@ -163,8 +170,10 @@ class AsyncLazyTest : TestBase() {
163170
expectUnreached()
164171
}
165172

166-
@Test(expected = CancellationException::class)
167-
fun testCancelWhileComputing(): Unit = runBlocking {
173+
@Test
174+
fun testCancelWhileComputing() = runTest(
175+
expected = { it is CancellationException }
176+
) {
168177
expect(1)
169178
val d = async(coroutineContext, CoroutineStart.LAZY) {
170179
expect(4)
@@ -188,4 +197,6 @@ class AsyncLazyTest : TestBase() {
188197
check(d.await() == 42) // await shall throw CancellationException
189198
expectUnreached()
190199
}
200+
201+
private class TestException : Exception()
191202
}
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/*
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+
17+
@file:Suppress("NAMED_ARGUMENTS_NOT_ALLOWED") // KT-21913
18+
19+
package kotlinx.coroutines.experimental
20+
21+
import kotlin.test.*
22+
23+
class CommonAsyncTest : TestBase() {
24+
@Test
25+
fun testSimple() = runTest {
26+
expect(1)
27+
val d = async(coroutineContext) {
28+
expect(3)
29+
42
30+
}
31+
expect(2)
32+
check(d.isActive)
33+
check(d.await() == 42)
34+
check(!d.isActive)
35+
expect(4)
36+
check(d.await() == 42) // second await -- same result
37+
finish(5)
38+
}
39+
40+
@Test
41+
fun testUndispatched() = runTest {
42+
expect(1)
43+
val d = async(coroutineContext, start = CoroutineStart.UNDISPATCHED) {
44+
expect(2)
45+
42
46+
}
47+
expect(3)
48+
check(!d.isActive)
49+
check(d.await() == 42)
50+
finish(4)
51+
}
52+
53+
@Test
54+
fun testSimpleException() = runTest(
55+
expected = { it is TestException }
56+
) {
57+
expect(1)
58+
val d = async(coroutineContext) {
59+
finish(3)
60+
throw TestException()
61+
}
62+
expect(2)
63+
d.await() // will throw IOException
64+
}
65+
66+
@Test
67+
fun testDeferAndYieldException() = runTest(
68+
expected = { it is TestException }
69+
) {
70+
expect(1)
71+
val d = async(coroutineContext) {
72+
expect(3)
73+
yield() // no effect, parent waiting
74+
finish(4)
75+
throw TestException()
76+
}
77+
expect(2)
78+
d.await() // will throw IOException
79+
}
80+
81+
@Test
82+
fun testDeferWithTwoWaiters() = runTest {
83+
expect(1)
84+
val d = async(coroutineContext) {
85+
expect(5)
86+
yield()
87+
expect(9)
88+
42
89+
}
90+
expect(2)
91+
launch(coroutineContext) {
92+
expect(6)
93+
check(d.await() == 42)
94+
expect(11)
95+
}
96+
expect(3)
97+
launch(coroutineContext) {
98+
expect(7)
99+
check(d.await() == 42)
100+
expect(12)
101+
}
102+
expect(4)
103+
yield() // this actually yields control to async, which produces results and resumes both waiters (in order)
104+
expect(8)
105+
yield() // yield again to "d", which completes
106+
expect(10)
107+
yield() // yield to both waiters
108+
finish(13)
109+
}
110+
111+
class BadClass {
112+
override fun equals(other: Any?): Boolean = error("equals")
113+
override fun hashCode(): Int = error("hashCode")
114+
override fun toString(): String = error("toString")
115+
}
116+
117+
@Test
118+
fun testDeferBadClass() = runTest {
119+
val bad = BadClass()
120+
val d = async(coroutineContext) {
121+
expect(1)
122+
bad
123+
}
124+
assertTrue(d.await() === bad)
125+
finish(2)
126+
}
127+
128+
private class TestException : Exception()
129+
}

0 commit comments

Comments
 (0)