55package kotlinx.coroutines.experimental
66
77import kotlinx.atomicfu.*
8+ import kotlinx.coroutines.experimental.NotInitialized.*
89import kotlinx.coroutines.experimental.internal.*
910import kotlinx.coroutines.experimental.internalAnnotations.*
1011import kotlinx.coroutines.experimental.intrinsics.*
@@ -155,6 +156,18 @@ internal open class JobSupport constructor(active: Boolean) : Job, SelectClause0
155156 * If this method succeeds, state of this job will never be changed again
156157 */
157158 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 {
158171 /*
159172 * If job is in 'cancelling' state and we're finalizing job state, we start intricate dance:
160173 * 1) Synchronize on state to avoid races with concurrent
@@ -164,38 +177,31 @@ internal open class JobSupport constructor(active: Boolean) : Job, SelectClause0
164177 * collection
165178 * 4) Pass it upstream
166179 */
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
182183 }
183184
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)
191187 }
192188
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
195203 }
196204
197- val update = coerceProposedUpdate(expect, proposedUpdate)
198- if (! tryFinalizeState(expect, update)) return false
199205 completeStateFinalization(expect, update, mode)
200206 return true
201207 }
@@ -832,31 +838,58 @@ internal open class JobSupport constructor(active: Boolean) : Job, SelectClause0
832838 }
833839
834840 // Cancelling or Completing
841+ @Suppress(" UNCHECKED_CAST" )
835842 private class Finishing (
836843 override val list : NodeList ,
837844 @JvmField val cancelled : Cancelled ? , /* != null when cancelling */
838845 @JvmField val completing : Boolean /* true when completing */
839846 ) : Incomplete {
840847 override val isActive: Boolean get() = cancelled == null
841- val exceptions: List <Throwable > get() = _exceptionsHolder as List <Throwable >
842848
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
845856
846857 fun addException (exception : Throwable ): Boolean {
847858 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)
854874 }
855875 }
856876 }
857877
858878 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+ }
860893 }
861894
862895 /* *
@@ -988,6 +1021,10 @@ private val EmptyNew = Empty(false)
9881021@Suppress(" PrivatePropertyName" )
9891022private val EmptyActive = Empty (true )
9901023
1024+ private enum class NotInitialized {
1025+ NOT_INITIALIZED
1026+ }
1027+
9911028private class Empty (override val isActive : Boolean ) : Incomplete {
9921029 override val list: NodeList ? get() = null
9931030 override fun toString (): String = " Empty{${if (isActive) " Active" else " New" } }"
0 commit comments