1
+ package kotlinx.coroutines.experimental.io.internal
2
+
3
+ import kotlinx.atomicfu.*
4
+ import kotlinx.coroutines.experimental.*
5
+ import kotlin.coroutines.experimental.*
6
+ import kotlin.coroutines.experimental.intrinsics.*
7
+
8
+ /* *
9
+ * Semi-cancellable reusable continuation. Unlike regular continuation this implementation has limitations:
10
+ * - could be resumed only once per swap, undefined behaviour otherwise
11
+ * - [T] should be neither [Throwable] nor [Continuation]
12
+ * - value shouldn't be null
13
+ */
14
+ internal class MutableDelegateContinuation <T : Any > : Continuation <T > {
15
+ private val state = atomic<Any ?>(null )
16
+ private val handler = atomic<JobRelation ?>(null )
17
+
18
+ fun swap (actual : Continuation <T >): Any {
19
+ loop@while (true ) {
20
+ val before = state.value
21
+
22
+ when (before) {
23
+ null -> {
24
+ if (! state.compareAndSet(null , actual)) continue @loop
25
+ parent(actual.context)
26
+ return COROUTINE_SUSPENDED
27
+ }
28
+ else -> {
29
+ if (! state.compareAndSet(before, null )) continue @loop
30
+ if (before is Throwable ) throw before
31
+ @Suppress(" UNCHECKED_CAST" )
32
+ return before as T
33
+ }
34
+ }
35
+ }
36
+ }
37
+
38
+ fun close () {
39
+ resumeWithException(Cancellation )
40
+ handler.getAndSet(null )?.dispose()
41
+ }
42
+
43
+ private fun parent (context : CoroutineContext ) {
44
+ val job = context[Job ]
45
+ if (handler.value?.job == = job) return
46
+
47
+ if (job == null ) {
48
+ handler.getAndSet(null )?.dispose()
49
+ } else {
50
+ val handler = JobRelation (job)
51
+ val old = this .handler.getAndUpdate { j ->
52
+ when {
53
+ j == null -> handler
54
+ j.job == = job -> return
55
+ else -> handler
56
+ }
57
+ }
58
+ old?.dispose()
59
+ }
60
+ }
61
+
62
+ override val context: CoroutineContext
63
+ get() = (state.value as ? Continuation <* >)?.context ? : EmptyCoroutineContext
64
+
65
+ override fun resume (value : T ) {
66
+ loop@while(true ) {
67
+ val before = state.value
68
+
69
+ when (before) {
70
+ null -> {
71
+ if (! state.compareAndSet(null , value)) continue @loop
72
+ return
73
+ }
74
+ is Continuation <* > -> {
75
+ if (! state.compareAndSet(before, null )) continue @loop
76
+ @Suppress(" UNCHECKED_CAST" )
77
+ val cont = before as Continuation <T >
78
+ return cont.resume(value)
79
+ }
80
+ else -> return
81
+ }
82
+ }
83
+ }
84
+
85
+ override fun resumeWithException (exception : Throwable ) {
86
+ loop@while(true ) {
87
+ val before = state.value
88
+
89
+ when (before) {
90
+ null -> {
91
+ if (! state.compareAndSet(null , exception)) continue @loop
92
+ return
93
+ }
94
+ is Continuation <* > -> {
95
+ if (! state.compareAndSet(before, null )) continue @loop
96
+ @Suppress(" UNCHECKED_CAST" )
97
+ val cont = before as Continuation <T >
98
+ return cont.resumeWithException(exception)
99
+ }
100
+ else -> return
101
+ }
102
+ }
103
+ }
104
+
105
+ private fun resumeWithExceptionContinuationOnly (job : Job , exception : Throwable ) {
106
+ var c: Continuation <* >? = null
107
+
108
+ state.update {
109
+ if (it !is Continuation <* >) return
110
+ if (it.context[Job ] != = job) return
111
+ c = it
112
+ null
113
+ }
114
+
115
+ c!! .resumeWithException(exception)
116
+ }
117
+
118
+ private inner class JobRelation (val job : Job ) : CompletionHandler, DisposableHandle {
119
+ private var handler: DisposableHandle = NonDisposableHandle
120
+
121
+ init {
122
+ val h = job.invokeOnCompletion(onCancelling = true , handler = this )
123
+ if (job.isActive) {
124
+ handler = h
125
+ }
126
+ }
127
+
128
+ override fun invoke (cause : Throwable ? ) {
129
+ this @MutableDelegateContinuation.handler.compareAndSet(this , null )
130
+ dispose()
131
+
132
+ if (cause != null ) {
133
+ resumeWithExceptionContinuationOnly(job, cause)
134
+ }
135
+ }
136
+
137
+ override fun dispose () {
138
+ handler.dispose()
139
+ handler = NonDisposableHandle
140
+ }
141
+ }
142
+
143
+ private companion object {
144
+ val Cancellation = CancellationException (" Continuation terminated" )
145
+ }
146
+ }
0 commit comments