5
5
package kotlinx.coroutines.experimental
6
6
7
7
import kotlinx.atomicfu.*
8
+ import kotlinx.coroutines.experimental.NotInitialized.*
8
9
import kotlinx.coroutines.experimental.internal.*
9
10
import kotlinx.coroutines.experimental.internalAnnotations.*
10
11
import kotlinx.coroutines.experimental.intrinsics.*
@@ -155,6 +156,18 @@ internal open class JobSupport constructor(active: Boolean) : Job, SelectClause0
155
156
* If this method succeeds, state of this job will never be changed again
156
157
*/
157
158
private fun tryFinalizeState (expect : Incomplete , proposedUpdate : Any? , mode : Int ): Boolean {
159
+ if (expect is Finishing && expect.cancelled != null ) {
160
+ return tryFinalizeCancellingState(expect, proposedUpdate, mode)
161
+ }
162
+
163
+ val update = coerceProposedUpdate(expect, proposedUpdate)
164
+ if (! tryFinalizeState(expect, update)) return false
165
+ if (update is CompletedExceptionally ) handleJobException(update.cause)
166
+ completeStateFinalization(expect, update, mode)
167
+ return true
168
+ }
169
+
170
+ private fun tryFinalizeCancellingState (expect : Finishing , proposedUpdate : Any? , mode : Int ): Boolean {
158
171
/*
159
172
* If job is in 'cancelling' state and we're finalizing job state, we start intricate dance:
160
173
* 1) Synchronize on state to avoid races with concurrent
@@ -164,38 +177,31 @@ internal open class JobSupport constructor(active: Boolean) : Job, SelectClause0
164
177
* collection
165
178
* 4) Pass it upstream
166
179
*/
167
- if (expect is Finishing && expect.cancelled != null ) {
168
- val finalException = synchronized(expect) {
169
- if (_state .value != = expect) {
170
- return false
171
- }
172
-
173
- if (proposedUpdate is CompletedExceptionally ) {
174
- expect.addLocked(proposedUpdate.cause)
175
- }
176
-
177
- /*
178
- * Note that new exceptions cannot be added concurrently: state is guarded by lock
179
- * and storage is sealed in the end, so all new exceptions will be reported separately
180
- */
181
- buildException(expect).also { expect.seal() }
180
+ val finalException = synchronized(expect) {
181
+ if (_state .value != = expect) {
182
+ return false
182
183
}
183
184
184
- val update = Cancelled (this , finalException ? : expect.cancelled.cause)
185
- handleJobException(update.cause)
186
- // This CAS never fails: we're in the state when no jobs can be attached, because state is already sealed
187
- if (! tryFinalizeState(expect, update)) {
188
- val error = AssertionError (" Unexpected state: ${_state .value} , expected: $expect , update: $update " )
189
- handleOnCompletionException(error)
190
- throw error
185
+ if (proposedUpdate is CompletedExceptionally ) {
186
+ expect.addLocked(proposedUpdate.cause)
191
187
}
192
188
193
- completeStateFinalization(expect, update, mode)
194
- return true
189
+ /*
190
+ * Note that new exceptions cannot be added concurrently: state is guarded by lock
191
+ * and storage is sealed in the end, so all new exceptions will be reported separately
192
+ */
193
+ buildException(expect).also { expect.seal() }
194
+ }
195
+
196
+ val update = Cancelled (this , finalException ? : expect.cancelled!! .cause)
197
+ handleJobException(update.cause)
198
+ // This CAS never fails: we're in the state when no jobs can be attached, because state is already sealed
199
+ if (! tryFinalizeState(expect, update)) {
200
+ val error = AssertionError (" Unexpected state: ${_state .value} , expected: $expect , update: $update " )
201
+ handleOnCompletionException(error)
202
+ throw error
195
203
}
196
204
197
- val update = coerceProposedUpdate(expect, proposedUpdate)
198
- if (! tryFinalizeState(expect, update)) return false
199
205
completeStateFinalization(expect, update, mode)
200
206
return true
201
207
}
@@ -832,31 +838,58 @@ internal open class JobSupport constructor(active: Boolean) : Job, SelectClause0
832
838
}
833
839
834
840
// Cancelling or Completing
841
+ @Suppress(" UNCHECKED_CAST" )
835
842
private class Finishing (
836
843
override val list : NodeList ,
837
844
@JvmField val cancelled : Cancelled ? , /* != null when cancelling */
838
845
@JvmField val completing : Boolean /* true when completing */
839
846
) : Incomplete {
840
847
override val isActive: Boolean get() = cancelled == null
841
- val exceptions: List <Throwable > get() = _exceptionsHolder as List <Throwable >
842
848
843
- // TODO optimize
844
- private var _exceptionsHolder : Any? = if (cancelled == null ) null else ArrayList <Throwable >(2 )
849
+ val exceptions: List <Throwable > get() = when (_exceptionsHolder ) {
850
+ NOT_INITIALIZED -> emptyList()
851
+ is Throwable -> listOf (_exceptionsHolder as Throwable ) // EA should handle this
852
+ else -> (_exceptionsHolder as List <Throwable >)
853
+ }
854
+
855
+ private var _exceptionsHolder : Any? = if (cancelled == null ) null else NOT_INITIALIZED
845
856
846
857
fun addException (exception : Throwable ): Boolean {
847
858
synchronized(this ) {
848
- return if (_exceptionsHolder == null ) {
849
- false
850
- } else {
851
- @Suppress(" UNCHECKED_CAST" )
852
- (_exceptionsHolder as MutableList <Throwable >).add(exception)
853
- true
859
+ return when (_exceptionsHolder ) {
860
+ null -> false
861
+ NOT_INITIALIZED -> {
862
+ _exceptionsHolder = exception
863
+ return true
864
+ }
865
+ is Throwable -> {
866
+ val previous = _exceptionsHolder
867
+ val list = ArrayList <Any ?>(4 )
868
+ list.add(previous)
869
+ list.add(exception)
870
+ _exceptionsHolder = list
871
+ return true
872
+ }
873
+ else -> (_exceptionsHolder as MutableList <Throwable >).add(exception)
854
874
}
855
875
}
856
876
}
857
877
858
878
fun addLocked (exception : Throwable ) {
859
- (_exceptionsHolder as MutableList <Throwable >).add(exception)
879
+ // Cannot be null at this point here
880
+ when (_exceptionsHolder ) {
881
+ NOT_INITIALIZED -> {
882
+ _exceptionsHolder = exception
883
+ }
884
+ is Throwable -> {
885
+ val previous = _exceptionsHolder
886
+ val list = ArrayList <Any ?>(4 )
887
+ list.add(previous)
888
+ list.add(exception)
889
+ _exceptionsHolder = list
890
+ }
891
+ else -> (_exceptionsHolder as MutableList <Throwable >).add(exception)
892
+ }
860
893
}
861
894
862
895
/* *
@@ -988,6 +1021,10 @@ private val EmptyNew = Empty(false)
988
1021
@Suppress(" PrivatePropertyName" )
989
1022
private val EmptyActive = Empty (true )
990
1023
1024
+ private enum class NotInitialized {
1025
+ NOT_INITIALIZED
1026
+ }
1027
+
991
1028
private class Empty (override val isActive : Boolean ) : Incomplete {
992
1029
override val list: NodeList ? get() = null
993
1030
override fun toString (): String = " Empty{${if (isActive) " Active" else " New" } }"
0 commit comments