File tree 2 files changed +50
-17
lines changed
core/kotlinx-coroutines-core/src
main/kotlin/kotlinx/coroutines/experimental
test/kotlin/kotlinx/coroutines/experimental
2 files changed +50
-17
lines changed Original file line number Diff line number Diff line change @@ -20,7 +20,8 @@ public suspend fun awaitAll(vararg jobs: Job): Unit = jobs.asList().awaitAll()
20
20
*/
21
21
public suspend fun Collection<Job>.awaitAll () {
22
22
if (isEmpty()) return
23
- AwaitAll (this ).await()
23
+ val snapshot = ArrayList (this )
24
+ AwaitAll (snapshot).await()
24
25
}
25
26
26
27
/* *
@@ -43,21 +44,23 @@ private class AwaitAll(private val jobs: Collection<Job>) {
43
44
44
45
suspend fun await () {
45
46
suspendCancellableCoroutine<Unit > { cont ->
46
- // todo: create a separate named class instance of JobNode to avoid extra object
47
- val handler: (Throwable ? ) -> Unit = {
48
- if (it != null ) {
49
- val token = cont.tryResumeWithException(it)
50
- if (token != null ) {
51
- cont.completeResume(token)
52
- }
53
- } else if (notCompletedCount.decrementAndGet() == 0 ) {
54
- cont.resume(Unit )
55
- }
56
- }
57
-
58
47
jobs.forEach {
59
48
it.start() // To properly await lazily started jobs
60
- cont.disposeOnCompletion(it.invokeOnCompletion(handler))
49
+ cont.disposeOnCompletion(it.invokeOnCompletion(AwaitAllNode (cont, it)))
50
+ }
51
+ }
52
+ }
53
+
54
+ inner class AwaitAllNode (private val continuation : CancellableContinuation <Unit >, job : Job ) : JobNode<Job>(job), CompletionHandler {
55
+
56
+ override fun invoke (cause : Throwable ? ) {
57
+ if (cause != null ) {
58
+ val token = continuation.tryResumeWithException(cause)
59
+ if (token != null ) {
60
+ continuation.completeResume(token)
61
+ }
62
+ } else if (notCompletedCount.decrementAndGet() == 0 ) {
63
+ continuation.resume(Unit )
61
64
}
62
65
}
63
66
}
Original file line number Diff line number Diff line change 1
1
package kotlinx.coroutines.experimental
2
2
3
- import org.junit.After
4
- import java.util.concurrent.CyclicBarrier
5
- import kotlin.test.Test
3
+ import org.junit.*
4
+ import org.junit.Test
5
+ import java.util.concurrent.*
6
6
7
7
class AwaitStressTest : TestBase () {
8
8
@@ -100,4 +100,34 @@ class AwaitStressTest : TestBase() {
100
100
101
101
require(cancelledOnce) { " Cancellation exception wasn't properly caught" }
102
102
}
103
+
104
+ @Test
105
+ fun testMutatingCollection () = runTest {
106
+ val barrier = CyclicBarrier (4 )
107
+
108
+ repeat(iterations) {
109
+ val jobs = mutableListOf<Job >()
110
+
111
+ jobs + = async(pool) {
112
+ barrier.await()
113
+ 1L
114
+ }
115
+
116
+ jobs + = async(pool) {
117
+ barrier.await()
118
+ 2L
119
+ }
120
+
121
+ jobs + = async(pool) {
122
+ barrier.await()
123
+ jobs.removeAt(2 )
124
+ }
125
+
126
+ val allJobs = ArrayList (jobs)
127
+ barrier.await()
128
+ jobs.awaitAll() // shouldn't hang
129
+ allJobs.awaitAll()
130
+ barrier.reset()
131
+ }
132
+ }
103
133
}
You can’t perform that action at this time.
0 commit comments