Improve coroutines library layout
- Split CoroutinesLibrary into common and JVM parts - Get rid of startCoroutine duplications - Make suspendCoroutine implementation to be platform independent
This commit is contained in:
@@ -34,19 +34,6 @@ public fun <R, T> (suspend R.() -> T).createCoroutine(
|
|||||||
COROUTINE_SUSPENDED
|
COROUTINE_SUSPENDED
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts coroutine with receiver type [R] and result type [T].
|
|
||||||
* This function creates and start a new, fresh instance of suspendable computation every time it is invoked.
|
|
||||||
* The [completion] continuation is invoked when coroutine completes with result of exception.
|
|
||||||
*/
|
|
||||||
@SinceKotlin("1.1")
|
|
||||||
public fun <R, T> (suspend R.() -> T).startCoroutine(
|
|
||||||
receiver: R,
|
|
||||||
completion: Continuation<T>
|
|
||||||
) {
|
|
||||||
createCoroutine(receiver, completion).resume(Unit)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates coroutine without receiver and with result type [T].
|
* Creates coroutine without receiver and with result type [T].
|
||||||
* This function creates a new, fresh instance of suspendable computation every time it is invoked.
|
* This function creates a new, fresh instance of suspendable computation every time it is invoked.
|
||||||
@@ -62,33 +49,6 @@ public fun <T> (suspend () -> T).createCoroutine(
|
|||||||
COROUTINE_SUSPENDED
|
COROUTINE_SUSPENDED
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts coroutine without receiver and with result type [T].
|
|
||||||
* This function creates and start a new, fresh instance of suspendable computation every time it is invoked.
|
|
||||||
* The [completion] continuation is invoked when coroutine completes with result of exception.
|
|
||||||
*/
|
|
||||||
@SinceKotlin("1.1")
|
|
||||||
public fun <T> (suspend () -> T).startCoroutine(
|
|
||||||
completion: Continuation<T>
|
|
||||||
) {
|
|
||||||
createCoroutine(completion).resume(Unit)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Obtains the current continuation instance inside suspend functions and suspends
|
|
||||||
* currently running coroutine.
|
|
||||||
*
|
|
||||||
* In this function both [Continuation.resume] and [Continuation.resumeWithException] can be used either synchronously in
|
|
||||||
* the same stack-frame where suspension function is run or asynchronously later in the same thread or
|
|
||||||
* from a different thread of execution. Repeated invocation of any resume function produces [IllegalStateException].
|
|
||||||
*/
|
|
||||||
@SinceKotlin("1.1")
|
|
||||||
public suspend fun <T> suspendCoroutine(block: (Continuation<T>) -> Unit): T = suspendCoroutineOrReturn { c ->
|
|
||||||
val safe = SafeContinuation(c)
|
|
||||||
block(safe)
|
|
||||||
safe.getResult()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------- internal stuff -------
|
// ------- internal stuff -------
|
||||||
|
|
||||||
@JsName("CoroutineImpl")
|
@JsName("CoroutineImpl")
|
||||||
@@ -135,11 +95,16 @@ private val UNDECIDED: Any? = Any()
|
|||||||
private val RESUMED: Any? = Any()
|
private val RESUMED: Any? = Any()
|
||||||
private class Fail(val exception: Throwable)
|
private class Fail(val exception: Throwable)
|
||||||
|
|
||||||
|
@PublishedApi
|
||||||
internal class SafeContinuation<in T>
|
internal class SafeContinuation<in T>
|
||||||
@PublishedApi internal constructor(
|
internal constructor(
|
||||||
private val delegate: Continuation<T>,
|
private val delegate: Continuation<T>,
|
||||||
initialResult: Any? = UNDECIDED
|
initialResult: Any?
|
||||||
) : Continuation<T> {
|
) : Continuation<T> {
|
||||||
|
|
||||||
|
@PublishedApi
|
||||||
|
internal constructor(delegate: Continuation<T>) : this(delegate, UNDECIDED)
|
||||||
|
|
||||||
public override val context: CoroutineContext
|
public override val context: CoroutineContext
|
||||||
get() = delegate.context
|
get() = delegate.context
|
||||||
|
|
||||||
@@ -175,6 +140,7 @@ internal class SafeContinuation<in T>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PublishedApi
|
||||||
internal fun getResult(): Any? {
|
internal fun getResult(): Any? {
|
||||||
if (result === UNDECIDED) {
|
if (result === UNDECIDED) {
|
||||||
result = COROUTINE_SUSPENDED
|
result = COROUTINE_SUSPENDED
|
||||||
|
|||||||
@@ -14,18 +14,6 @@ public header fun <R, T> (suspend R.() -> T).createCoroutine(
|
|||||||
completion: Continuation<T>
|
completion: Continuation<T>
|
||||||
): Continuation<Unit>
|
): Continuation<Unit>
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts coroutine with receiver type [R] and result type [T].
|
|
||||||
* This function creates and start a new, fresh instance of suspendable computation every time it is invoked.
|
|
||||||
* The [completion] continuation is invoked when coroutine completes with result of exception.
|
|
||||||
* An optional [dispatcher] may be specified to customise dispatch of continuations between suspension points inside the coroutine.
|
|
||||||
*/
|
|
||||||
@SinceKotlin("1.1")
|
|
||||||
public header fun <R, T> (suspend R.() -> T).startCoroutine(
|
|
||||||
receiver: R,
|
|
||||||
completion: Continuation<T>
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates coroutine without receiver and with result type [T].
|
* Creates coroutine without receiver and with result type [T].
|
||||||
* This function creates a new, fresh instance of suspendable computation every time it is invoked.
|
* This function creates a new, fresh instance of suspendable computation every time it is invoked.
|
||||||
@@ -38,25 +26,17 @@ public header fun <T> (suspend () -> T).createCoroutine(
|
|||||||
completion: Continuation<T>
|
completion: Continuation<T>
|
||||||
): Continuation<Unit>
|
): Continuation<Unit>
|
||||||
|
|
||||||
/**
|
@PublishedApi
|
||||||
* Starts coroutine without receiver and with result type [T].
|
internal header class SafeContinuation<in T> : Continuation<T> {
|
||||||
* This function creates and start a new, fresh instance of suspendable computation every time it is invoked.
|
internal constructor(delegate: Continuation<T>, initialResult: Any?)
|
||||||
* The [completion] continuation is invoked when coroutine completes with result of exception.
|
|
||||||
* An optional [dispatcher] may be specified to customise dispatch of continuations between suspension points inside the coroutine.
|
|
||||||
*/
|
|
||||||
@SinceKotlin("1.1")
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
public header fun <T> (suspend () -> T).startCoroutine(
|
|
||||||
completion: Continuation<T>
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
@PublishedApi
|
||||||
* Obtains the current continuation instance inside suspend functions and suspends
|
internal constructor(delegate: Continuation<T>)
|
||||||
* currently running coroutine.
|
|
||||||
*
|
@PublishedApi
|
||||||
* In this function both [Continuation.resume] and [Continuation.resumeWithException] can be used either synchronously in
|
internal fun getResult(): Any?
|
||||||
* the same stack-frame where suspension function is run or asynchronously later in the same thread or
|
|
||||||
* from a different thread of execution. Repeated invocation of any resume function produces [IllegalStateException].
|
override val context: CoroutineContext
|
||||||
*/
|
override fun resume(value: T): Unit
|
||||||
@SinceKotlin("1.1")
|
override fun resumeWithException(exception: Throwable): Unit
|
||||||
public header inline suspend fun <T> suspendCoroutine(crossinline block: (Continuation<T>) -> Unit): T
|
}
|
||||||
|
|||||||
@@ -15,37 +15,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
@file:kotlin.jvm.JvmName("CoroutinesKt")
|
@file:kotlin.jvm.JvmName("CoroutinesKt")
|
||||||
@file:kotlin.jvm.JvmVersion
|
|
||||||
package kotlin.coroutines.experimental
|
package kotlin.coroutines.experimental
|
||||||
|
|
||||||
import java.lang.IllegalStateException
|
|
||||||
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater
|
|
||||||
import kotlin.coroutines.experimental.intrinsics.COROUTINE_SUSPENDED
|
import kotlin.coroutines.experimental.intrinsics.COROUTINE_SUSPENDED
|
||||||
import kotlin.coroutines.experimental.intrinsics.suspendCoroutineOrReturn
|
import kotlin.coroutines.experimental.intrinsics.suspendCoroutineOrReturn
|
||||||
import kotlin.coroutines.experimental.jvm.internal.CoroutineImpl
|
|
||||||
import kotlin.coroutines.experimental.jvm.internal.interceptContinuationIfNeeded
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates coroutine with receiver type [R] and result type [T].
|
|
||||||
* This function creates a new, fresh instance of suspendable computation every time it is invoked.
|
|
||||||
* To start executing the created coroutine, invoke `resume(Unit)` on the returned [Continuation] instance.
|
|
||||||
* The [completion] continuation is invoked when coroutine completes with result or exception.
|
|
||||||
*/
|
|
||||||
@SinceKotlin("1.1")
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
public fun <R, T> (suspend R.() -> T).createCoroutine(
|
|
||||||
receiver: R,
|
|
||||||
completion: Continuation<T>
|
|
||||||
): Continuation<Unit> =
|
|
||||||
SafeContinuation(
|
|
||||||
if (this !is CoroutineImpl)
|
|
||||||
buildContinuationByInvokeCall(completion) {
|
|
||||||
(this as Function2<R, Continuation<T>, Any?>).invoke(receiver, completion)
|
|
||||||
}
|
|
||||||
else
|
|
||||||
((this as CoroutineImpl).create(receiver, completion) as CoroutineImpl).facade,
|
|
||||||
COROUTINE_SUSPENDED
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts coroutine with receiver type [R] and result type [T].
|
* Starts coroutine with receiver type [R] and result type [T].
|
||||||
@@ -61,27 +34,6 @@ public fun <R, T> (suspend R.() -> T).startCoroutine(
|
|||||||
createCoroutine(receiver, completion).resume(Unit)
|
createCoroutine(receiver, completion).resume(Unit)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates coroutine without receiver and with result type [T].
|
|
||||||
* This function creates a new, fresh instance of suspendable computation every time it is invoked.
|
|
||||||
* To start executing the created coroutine, invoke `resume(Unit)` on the returned [Continuation] instance.
|
|
||||||
* The [completion] continuation is invoked when coroutine completes with result or exception.
|
|
||||||
*/
|
|
||||||
@SinceKotlin("1.1")
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
public fun <T> (suspend () -> T).createCoroutine(
|
|
||||||
completion: Continuation<T>
|
|
||||||
): Continuation<Unit> =
|
|
||||||
SafeContinuation(
|
|
||||||
if (this !is CoroutineImpl)
|
|
||||||
buildContinuationByInvokeCall(completion) {
|
|
||||||
(this as Function1<Continuation<T>, Any?>).invoke(completion)
|
|
||||||
}
|
|
||||||
else
|
|
||||||
((this as CoroutineImpl).create(completion) as CoroutineImpl).facade,
|
|
||||||
COROUTINE_SUSPENDED
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts coroutine without receiver and with result type [T].
|
* Starts coroutine without receiver and with result type [T].
|
||||||
* This function creates and start a new, fresh instance of suspendable computation every time it is invoked.
|
* This function creates and start a new, fresh instance of suspendable computation every time it is invoked.
|
||||||
@@ -113,28 +65,7 @@ public inline suspend fun <T> suspendCoroutine(crossinline block: (Continuation<
|
|||||||
|
|
||||||
// INTERNAL DECLARATIONS
|
// INTERNAL DECLARATIONS
|
||||||
|
|
||||||
private inline fun <T> buildContinuationByInvokeCall(
|
internal inline fun processInvokeCallOnCoroutine(completion: Continuation<*>, block: () -> Any?) {
|
||||||
completion: Continuation<T>,
|
|
||||||
crossinline block: () -> Any?
|
|
||||||
): Continuation<Unit> {
|
|
||||||
val continuation =
|
|
||||||
object : Continuation<Unit> {
|
|
||||||
override val context: CoroutineContext
|
|
||||||
get() = completion.context
|
|
||||||
|
|
||||||
override fun resume(value: Unit) {
|
|
||||||
processInvokeCallOnCoroutine(completion, block)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun resumeWithException(exception: Throwable) {
|
|
||||||
completion.resumeWithException(exception)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return completion.context.interceptContinuationIfNeeded(continuation)
|
|
||||||
}
|
|
||||||
|
|
||||||
private inline fun processInvokeCallOnCoroutine(completion: Continuation<*>, block: () -> Any?) {
|
|
||||||
try {
|
try {
|
||||||
val result = block()
|
val result = block()
|
||||||
if (result !== COROUTINE_SUSPENDED) {
|
if (result !== COROUTINE_SUSPENDED) {
|
||||||
@@ -145,69 +76,3 @@ private inline fun processInvokeCallOnCoroutine(completion: Continuation<*>, blo
|
|||||||
completion.resumeWithException(t)
|
completion.resumeWithException(t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val UNDECIDED: Any? = Any()
|
|
||||||
private val RESUMED: Any? = Any()
|
|
||||||
private class Fail(val exception: Throwable)
|
|
||||||
|
|
||||||
@PublishedApi
|
|
||||||
internal class SafeContinuation<in T>
|
|
||||||
@PublishedApi internal constructor(
|
|
||||||
private val delegate: Continuation<T>,
|
|
||||||
initialResult: Any? = UNDECIDED
|
|
||||||
) : Continuation<T> {
|
|
||||||
public override val context: CoroutineContext
|
|
||||||
get() = delegate.context
|
|
||||||
|
|
||||||
@Volatile
|
|
||||||
private var result: Any? = initialResult
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
@JvmStatic
|
|
||||||
private val RESULT = AtomicReferenceFieldUpdater.newUpdater<SafeContinuation<*>, Any?>(
|
|
||||||
SafeContinuation::class.java, Any::class.java as Class<Any?>, "result")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun resume(value: T) {
|
|
||||||
while (true) { // lock-free loop
|
|
||||||
val result = this.result // atomic read
|
|
||||||
when {
|
|
||||||
result === UNDECIDED -> if (RESULT.compareAndSet(this, UNDECIDED, value)) return
|
|
||||||
result === COROUTINE_SUSPENDED -> if (RESULT.compareAndSet(this, COROUTINE_SUSPENDED, RESUMED)) {
|
|
||||||
delegate.resume(value)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
else -> throw IllegalStateException("Already resumed")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun resumeWithException(exception: Throwable) {
|
|
||||||
while (true) { // lock-free loop
|
|
||||||
val result = this.result // atomic read
|
|
||||||
when {
|
|
||||||
result === UNDECIDED -> if (RESULT.compareAndSet(this, UNDECIDED, Fail(exception))) return
|
|
||||||
result === COROUTINE_SUSPENDED -> if (RESULT.compareAndSet(this, COROUTINE_SUSPENDED, RESUMED)) {
|
|
||||||
delegate.resumeWithException(exception)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
else -> throw IllegalStateException("Already resumed")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@PublishedApi
|
|
||||||
internal fun getResult(): Any? {
|
|
||||||
var result = this.result // atomic read
|
|
||||||
if (result === UNDECIDED) {
|
|
||||||
if (RESULT.compareAndSet(this, UNDECIDED, COROUTINE_SUSPENDED)) return COROUTINE_SUSPENDED
|
|
||||||
result = this.result // reread volatile var
|
|
||||||
}
|
|
||||||
when {
|
|
||||||
result === RESUMED -> return COROUTINE_SUSPENDED // already called continuation, indicate COROUTINE_SUSPENDED upstream
|
|
||||||
result is Fail -> throw result.exception
|
|
||||||
else -> return result // either COROUTINE_SUSPENDED or data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@file:kotlin.jvm.JvmName("CoroutinesJvmKt")
|
||||||
|
@file:kotlin.jvm.JvmVersion
|
||||||
|
package kotlin.coroutines.experimental
|
||||||
|
|
||||||
|
import kotlin.coroutines.experimental.intrinsics.COROUTINE_SUSPENDED
|
||||||
|
import kotlin.coroutines.experimental.jvm.internal.CoroutineImpl
|
||||||
|
import kotlin.coroutines.experimental.jvm.internal.interceptContinuationIfNeeded
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates coroutine with receiver type [R] and result type [T].
|
||||||
|
* This function creates a new, fresh instance of suspendable computation every time it is invoked.
|
||||||
|
* To start executing the created coroutine, invoke `resume(Unit)` on the returned [Continuation] instance.
|
||||||
|
* The [completion] continuation is invoked when coroutine completes with result or exception.
|
||||||
|
*/
|
||||||
|
@SinceKotlin("1.1")
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
public fun <R, T> (suspend R.() -> T).createCoroutine(
|
||||||
|
receiver: R,
|
||||||
|
completion: Continuation<T>
|
||||||
|
): Continuation<Unit> =
|
||||||
|
SafeContinuation(
|
||||||
|
if (this !is CoroutineImpl)
|
||||||
|
buildContinuationByInvokeCall(completion) {
|
||||||
|
(this as Function2<R, Continuation<T>, Any?>).invoke(receiver, completion)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
((this as CoroutineImpl).create(receiver, completion) as CoroutineImpl).facade,
|
||||||
|
COROUTINE_SUSPENDED
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates coroutine without receiver and with result type [T].
|
||||||
|
* This function creates a new, fresh instance of suspendable computation every time it is invoked.
|
||||||
|
* To start executing the created coroutine, invoke `resume(Unit)` on the returned [Continuation] instance.
|
||||||
|
* The [completion] continuation is invoked when coroutine completes with result or exception.
|
||||||
|
*/
|
||||||
|
@SinceKotlin("1.1")
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
public fun <T> (suspend () -> T).createCoroutine(
|
||||||
|
completion: Continuation<T>
|
||||||
|
): Continuation<Unit> =
|
||||||
|
SafeContinuation(
|
||||||
|
if (this !is CoroutineImpl)
|
||||||
|
buildContinuationByInvokeCall(completion) {
|
||||||
|
(this as Function1<Continuation<T>, Any?>).invoke(completion)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
((this as CoroutineImpl).create(completion) as CoroutineImpl).facade,
|
||||||
|
COROUTINE_SUSPENDED
|
||||||
|
)
|
||||||
|
|
||||||
|
// INTERNAL DECLARATIONS
|
||||||
|
|
||||||
|
private inline fun <T> buildContinuationByInvokeCall(
|
||||||
|
completion: Continuation<T>,
|
||||||
|
crossinline block: () -> Any?
|
||||||
|
): Continuation<Unit> {
|
||||||
|
val continuation =
|
||||||
|
object : Continuation<Unit> {
|
||||||
|
override val context: CoroutineContext
|
||||||
|
get() = completion.context
|
||||||
|
|
||||||
|
override fun resume(value: Unit) {
|
||||||
|
processInvokeCallOnCoroutine(completion, block)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun resumeWithException(exception: Throwable) {
|
||||||
|
completion.resumeWithException(exception)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return completion.context.interceptContinuationIfNeeded(continuation)
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
@file:kotlin.jvm.JvmVersion
|
||||||
|
package kotlin.coroutines.experimental
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater
|
||||||
|
import kotlin.coroutines.experimental.intrinsics.COROUTINE_SUSPENDED
|
||||||
|
|
||||||
|
@PublishedApi
|
||||||
|
internal class SafeContinuation<in T>
|
||||||
|
internal constructor(
|
||||||
|
private val delegate: Continuation<T>,
|
||||||
|
initialResult: Any?
|
||||||
|
) : Continuation<T> {
|
||||||
|
|
||||||
|
@PublishedApi
|
||||||
|
internal constructor(delegate: Continuation<T>) : this(delegate, UNDECIDED)
|
||||||
|
|
||||||
|
public override val context: CoroutineContext
|
||||||
|
get() = delegate.context
|
||||||
|
|
||||||
|
@Volatile
|
||||||
|
private var result: Any? = initialResult
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val UNDECIDED: Any? = Any()
|
||||||
|
private val RESUMED: Any? = Any()
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
@JvmStatic
|
||||||
|
private val RESULT = AtomicReferenceFieldUpdater.newUpdater<SafeContinuation<*>, Any?>(
|
||||||
|
SafeContinuation::class.java, Any::class.java as Class<Any?>, "result")
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Fail(val exception: Throwable)
|
||||||
|
|
||||||
|
override fun resume(value: T) {
|
||||||
|
while (true) { // lock-free loop
|
||||||
|
val result = this.result // atomic read
|
||||||
|
when {
|
||||||
|
result === UNDECIDED -> if (RESULT.compareAndSet(this, UNDECIDED, value)) return
|
||||||
|
result === COROUTINE_SUSPENDED -> if (RESULT.compareAndSet(this, COROUTINE_SUSPENDED, RESUMED)) {
|
||||||
|
delegate.resume(value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
else -> throw IllegalStateException("Already resumed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun resumeWithException(exception: Throwable) {
|
||||||
|
while (true) { // lock-free loop
|
||||||
|
val result = this.result // atomic read
|
||||||
|
when {
|
||||||
|
result === UNDECIDED -> if (RESULT.compareAndSet(this, UNDECIDED, Fail(exception))) return
|
||||||
|
result === COROUTINE_SUSPENDED -> if (RESULT.compareAndSet(this, COROUTINE_SUSPENDED, RESUMED)) {
|
||||||
|
delegate.resumeWithException(exception)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
else -> throw IllegalStateException("Already resumed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@PublishedApi
|
||||||
|
internal fun getResult(): Any? {
|
||||||
|
var result = this.result // atomic read
|
||||||
|
if (result === UNDECIDED) {
|
||||||
|
if (RESULT.compareAndSet(this, UNDECIDED, COROUTINE_SUSPENDED)) return COROUTINE_SUSPENDED
|
||||||
|
result = this.result // reread volatile var
|
||||||
|
}
|
||||||
|
when {
|
||||||
|
result === RESUMED -> return COROUTINE_SUSPENDED // already called continuation, indicate COROUTINE_SUSPENDED upstream
|
||||||
|
result is Fail -> throw result.exception
|
||||||
|
else -> return result // either COROUTINE_SUSPENDED or data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user