@@ -4,9 +4,14 @@ import rx.Observable
4
4
import rx.Subscriber
5
5
import java.util.concurrent.atomic.AtomicReference
6
6
import kotlin.coroutines.Continuation
7
- import kotlin.coroutines.suspendCoroutine
7
+ import kotlin.coroutines.CoroutineIntrinsics.SUSPENDED
8
+ import kotlin.coroutines.CoroutineIntrinsics.suspendCoroutineOrReturn
8
9
9
- // supports suspending iteration on observables
10
+ /* *
11
+ * Suspending iteration extension. It does not have its own buffer and applies back-pressure to the source.
12
+ * If iterating coroutine does not have a dispatcher with its own thread, then the iterating coroutine
13
+ * is resumed in the thread that invokes [Subscriber.onNext].
14
+ */
10
15
suspend operator fun <V : Any > Observable<V>.iterator (): ObserverIterator <V > {
11
16
val iterator = ObserverIterator <V >()
12
17
subscribe(iterator.subscriber)
@@ -17,101 +22,45 @@ private sealed class Waiter<in T>(val c: Continuation<T>)
17
22
private class HasNextWaiter (c : Continuation <Boolean >) : Waiter<Boolean>(c)
18
23
private class NextWaiter <V >(c : Continuation <V >) : Waiter<V>(c)
19
24
20
- private object Complete
21
- private class Fail (val e : Throwable )
25
+ private object Completed
26
+ private class CompletedWith (val v : Any )
27
+ private class Error (val e : Throwable )
22
28
23
29
class ObserverIterator <V : Any > {
24
30
internal val subscriber = Sub ()
25
- // Contains either null, Complete, Fail (exception), Waiter, or next value
31
+ // Contains either null, Completed, CompletedWith, Error (exception), Waiter, or next value
26
32
private val rendezvous = AtomicReference <Any ?>()
27
33
28
34
@Suppress(" UNCHECKED_CAST" )
29
- private suspend fun awaitHasNext (): Boolean = suspendCoroutine sc@ { c ->
35
+ suspend operator fun hasNext (): Boolean = suspendCoroutineOrReturn sc@ { c ->
30
36
while (true ) { // lock-free loop for rendezvous
31
37
val cur = rendezvous.get()
32
38
when (cur) {
33
- null -> {
34
- if (rendezvous.compareAndSet(null , HasNextWaiter (c))) return @sc
35
- }
36
- Complete -> {
37
- c.resume(false )
38
- return @sc
39
- }
40
- is Fail -> {
41
- c.resumeWithException(cur.e)
42
- return @sc
43
- }
44
- is Waiter <* > -> {
45
- c.resumeWithException(IllegalStateException (" Concurrent iteration" ))
46
- return @sc
47
- }
48
- else -> {
49
- c.resume(true )
50
- return @sc
51
- }
52
- }
53
- }
54
- }
55
-
56
- private suspend fun awaitNext (): V = suspendCoroutine sc@ { c ->
57
- while (true ) { // lock-free loop for rendezvous
58
- val cur = rendezvous.get()
59
- when (cur) {
60
- null -> {
61
- if (rendezvous.compareAndSet(null , NextWaiter (c))) return @sc
62
- }
63
- Complete -> {
64
- c.resumeWithException(NoSuchElementException ())
65
- return @sc
66
- }
67
- is Fail -> {
68
- c.resumeWithException(cur.e)
69
- return @sc
70
- }
71
- is Waiter <* > -> {
72
- c.resumeWithException(IllegalStateException (" Concurrent iteration" ))
73
- return @sc
74
- }
75
- else -> {
76
- if (rendezvous.compareAndSet(cur, null )) {
77
- c.resume(consumeValue(cur))
78
- return @sc
79
- }
80
- }
39
+ null -> if (rendezvous.compareAndSet(null , HasNextWaiter (c))) return @sc SUSPENDED
40
+ Completed -> return @sc false
41
+ is CompletedWith -> return @sc true
42
+ is Error -> throw cur.e
43
+ is Waiter <* > -> throw IllegalStateException (" Concurrent iteration" )
44
+ else -> return @sc true
81
45
}
82
46
}
83
47
}
84
48
85
- suspend operator fun hasNext (): Boolean {
86
- val cur = rendezvous.get()
87
- return when (cur) {
88
- null -> awaitHasNext()
89
- Complete -> false
90
- is Fail -> throw cur.e
91
- is Waiter <* > -> throw IllegalStateException (" Concurrent iteration" )
92
- else -> true
93
- }
94
- }
95
-
96
- suspend operator fun next (): V {
49
+ @Suppress(" UNCHECKED_CAST" )
50
+ suspend operator fun next (): V = suspendCoroutineOrReturn sc@ { c ->
97
51
while (true ) { // lock-free loop for rendezvous
98
52
val cur = rendezvous.get()
99
53
when (cur) {
100
- null -> return awaitNext()
101
- Complete -> throw NoSuchElementException ()
102
- is Fail -> throw cur.e
54
+ null -> if (rendezvous.compareAndSet(null , NextWaiter (c))) return @sc SUSPENDED
55
+ Completed -> throw NoSuchElementException ()
56
+ is CompletedWith -> if (rendezvous.compareAndSet(cur, Completed )) return @sc cur.v as V
57
+ is Error -> throw cur.e
103
58
is Waiter <* > -> throw IllegalStateException (" Concurrent iteration" )
104
- else -> if (rendezvous.compareAndSet(cur, null )) return consumeValue (cur)
59
+ else -> if (rendezvous.compareAndSet(cur, null )) return (cur as V ). apply { subscriber.requestOne() }
105
60
}
106
61
}
107
62
}
108
63
109
- @Suppress(" UNCHECKED_CAST" )
110
- private fun consumeValue (cur : Any? ): V {
111
- subscriber.requestOne()
112
- return cur as V
113
- }
114
-
115
64
internal inner class Sub : Subscriber <V >() {
116
65
fun requestOne () {
117
66
request(1 )
@@ -125,9 +74,10 @@ class ObserverIterator<V : Any> {
125
74
while (true ) { // lock-free loop for rendezvous
126
75
val cur = rendezvous.get()
127
76
when (cur) {
128
- null -> if (rendezvous.compareAndSet(null , Fail (e))) return
129
- Complete -> throw IllegalStateException (" onError after onComplete" )
130
- is Fail -> throw IllegalStateException (" onError after onError" )
77
+ null -> if (rendezvous.compareAndSet(null , Error (e))) return
78
+ Completed -> throw IllegalStateException (" onError after onCompleted" )
79
+ is CompletedWith -> throw IllegalStateException (" onError after onCompleted" )
80
+ is Error -> throw IllegalStateException (" onError after onError" )
131
81
is Waiter <* > -> if (rendezvous.compareAndSet(cur, null )) {
132
82
cur.c.resumeWithException(e)
133
83
return
@@ -143,8 +93,9 @@ class ObserverIterator<V : Any> {
143
93
val cur = rendezvous.get()
144
94
when (cur) {
145
95
null -> if (rendezvous.compareAndSet(null , v)) return
146
- Complete -> throw IllegalStateException (" onNext after onComplete" )
147
- is Fail -> throw IllegalStateException (" onNext after onError" )
96
+ Completed -> throw IllegalStateException (" onNext after onCompleted" )
97
+ is CompletedWith -> throw IllegalStateException (" onNext after onCompleted" )
98
+ is Error -> throw IllegalStateException (" onNext after onError" )
148
99
is HasNextWaiter -> if (rendezvous.compareAndSet(cur, v)) {
149
100
cur.c.resume(true )
150
101
return
@@ -162,18 +113,19 @@ class ObserverIterator<V : Any> {
162
113
while (true ) { // lock-free loop for rendezvous
163
114
val cur = rendezvous.get()
164
115
when (cur) {
165
- null -> if (rendezvous.compareAndSet(null , Complete )) return
166
- Complete -> throw IllegalStateException (" onComplete after onComplete" )
167
- is Fail -> throw IllegalStateException (" onComplete after onError" )
168
- is HasNextWaiter -> if (rendezvous.compareAndSet(cur, Complete )) {
116
+ null -> if (rendezvous.compareAndSet(null , Completed )) return
117
+ Completed -> throw IllegalStateException (" onCompleted after onCompleted" )
118
+ is CompletedWith -> throw IllegalStateException (" onCompleted after onCompleted" )
119
+ is Error -> throw IllegalStateException (" onCompleted after onError" )
120
+ is HasNextWaiter -> if (rendezvous.compareAndSet(cur, Completed )) {
169
121
cur.c.resume(false )
170
122
return
171
123
}
172
- is NextWaiter <* > -> if (rendezvous.compareAndSet(cur, Complete )) {
124
+ is NextWaiter <* > -> if (rendezvous.compareAndSet(cur, Completed )) {
173
125
cur.c.resumeWithException(NoSuchElementException ())
174
126
return
175
127
}
176
- else -> throw IllegalStateException ( " onComplete after onNext before request(1) was called " )
128
+ else -> if (rendezvous.compareAndSet(cur, CompletedWith (cur))) return
177
129
}
178
130
}
179
131
}
0 commit comments