CoroutineContext and ContinuationInterceptor (instead of dispatcher)
This commit is contained in:
committed by
Denis Zharkov
parent
f611e39a69
commit
8d6a913cee
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package kotlin.coroutines
|
||||
|
||||
/**
|
||||
* Base class for [CoroutineContextElement] implementations.
|
||||
*/
|
||||
@SinceKotlin("1.1")
|
||||
public abstract class AbstractCoroutineContextElement : CoroutineContextElement {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
public override operator fun <E : CoroutineContextElement> get(key: CoroutineContextKey<E>): E? =
|
||||
if (this.contextKey == key) this as E else null
|
||||
|
||||
public override fun <R> fold(initial: R, operation: (R, CoroutineContextElement) -> R): R =
|
||||
operation(initial, this)
|
||||
|
||||
public override operator fun plus(context: CoroutineContext): CoroutineContext =
|
||||
plusImpl(context)
|
||||
|
||||
public override fun minusKey(key: CoroutineContextKey<*>): CoroutineContext =
|
||||
if (this.contextKey == key) EmptyCoroutineContext else this
|
||||
}
|
||||
|
||||
/**
|
||||
* An empty coroutine context.
|
||||
*/
|
||||
@SinceKotlin("1.1")
|
||||
public object EmptyCoroutineContext : CoroutineContext {
|
||||
public override fun <E : CoroutineContextElement> get(key: CoroutineContextKey<E>): E? = null
|
||||
public override fun <R> fold(initial: R, operation: (R, CoroutineContextElement) -> R): R = initial
|
||||
public override fun plus(context: CoroutineContext): CoroutineContext = context
|
||||
public override fun minusKey(key: CoroutineContextKey<*>): CoroutineContext = this
|
||||
public override fun hashCode(): Int = 0
|
||||
public override fun toString(): String = "EmptyCoroutineContext"
|
||||
}
|
||||
|
||||
//--------------------- private impl ---------------------
|
||||
|
||||
// this class is not exposed, but is hidden inside implementations
|
||||
// this is a left-biased list, so that `plus` works naturally
|
||||
private class CombinedContext(val left: CoroutineContext, val element: CoroutineContextElement) : CoroutineContext {
|
||||
override fun <E : CoroutineContextElement> get(key: CoroutineContextKey<E>): E? {
|
||||
var cur = this
|
||||
while (true) {
|
||||
cur.element[key]?.let { return it }
|
||||
val next = cur.left
|
||||
if (next is CombinedContext) {
|
||||
cur = next
|
||||
} else {
|
||||
return next[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override fun <R> fold(initial: R, operation: (R, CoroutineContextElement) -> R): R =
|
||||
operation(left.fold(initial, operation), element)
|
||||
|
||||
public override operator fun plus(context: CoroutineContext): CoroutineContext =
|
||||
plusImpl(context)
|
||||
|
||||
public override fun minusKey(key: CoroutineContextKey<*>): CoroutineContext {
|
||||
element[key]?.let { return left }
|
||||
val newLeft = left.minusKey(key)
|
||||
return when (newLeft) {
|
||||
left -> this
|
||||
EmptyCoroutineContext -> element
|
||||
else -> CombinedContext(newLeft, element)
|
||||
}
|
||||
}
|
||||
|
||||
private fun size(): Int =
|
||||
if (left is CombinedContext) left.size() + 1 else 2
|
||||
|
||||
private fun contains(element: CoroutineContextElement): Boolean =
|
||||
get(element.contextKey) == element
|
||||
|
||||
private fun containsAll(context: CombinedContext): Boolean {
|
||||
var cur = context
|
||||
while (true) {
|
||||
if (!contains(cur.element)) return false
|
||||
val next = cur.left
|
||||
if (next is CombinedContext) {
|
||||
cur = next
|
||||
} else {
|
||||
return contains(next as CoroutineContextElement)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean =
|
||||
this === other || other is CombinedContext && other.size() == size() && other.containsAll(this)
|
||||
|
||||
override fun hashCode(): Int = left.hashCode() + element.hashCode()
|
||||
|
||||
override fun toString(): String =
|
||||
"[" + fold("") { acc, element ->
|
||||
if (acc.isEmpty()) element.toString() else acc + ", " + element
|
||||
} + "]"
|
||||
}
|
||||
|
||||
private fun CoroutineContext.plusImpl(context: CoroutineContext): CoroutineContext =
|
||||
if (context == EmptyCoroutineContext) this else // fast path -- avoid lambda creation
|
||||
context.fold(this) { acc, element ->
|
||||
val removed = acc.minusKey(element.contextKey)
|
||||
if (removed == EmptyCoroutineContext) element else {
|
||||
// make sure interceptor is always last in the context (and thus is fast to get when present)
|
||||
val interceptor = removed[ContinuationInterceptor]
|
||||
if (interceptor == null) CombinedContext(removed, element) else {
|
||||
val left = removed.minusKey(ContinuationInterceptor)
|
||||
if (left == EmptyCoroutineContext) CombinedContext(element, interceptor) else
|
||||
CombinedContext(CombinedContext(left, element), interceptor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,30 +11,26 @@ import kotlin.coroutines.intrinsics.*
|
||||
* 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 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 fun <R, T> (suspend R.() -> T).createCoroutine(
|
||||
receiver: R,
|
||||
completion: Continuation<T>,
|
||||
dispatcher: ContinuationDispatcher? = null
|
||||
): Continuation<Unit> = (this as kotlin.jvm.internal.SuspendFunction1<R, T>).create(receiver, withDispatcher(completion, dispatcher))
|
||||
completion: Continuation<T>
|
||||
): Continuation<Unit> = (this as kotlin.jvm.internal.SuspendFunction1<R, T>).create(receiver, completion)
|
||||
|
||||
/**
|
||||
* 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")
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
public fun <R, T> (suspend R.() -> T).startCoroutine(
|
||||
receiver: R,
|
||||
completion: Continuation<T>,
|
||||
dispatcher: ContinuationDispatcher? = null
|
||||
completion: Continuation<T>
|
||||
) {
|
||||
(this as Function2<R, Continuation<T>, Any?>).invoke(receiver, withDispatcher(completion, dispatcher))
|
||||
(this as Function2<R, Continuation<T>, Any?>).invoke(receiver, completion)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -42,28 +38,24 @@ public fun <R, T> (suspend R.() -> T).startCoroutine(
|
||||
* 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 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 fun <T> (suspend () -> T).createCoroutine(
|
||||
completion: Continuation<T>,
|
||||
dispatcher: ContinuationDispatcher? = null
|
||||
): Continuation<Unit> = (this as kotlin.jvm.internal.SuspendFunction0<T>).create(withDispatcher(completion, dispatcher))
|
||||
completion: Continuation<T>
|
||||
): Continuation<Unit> = (this as kotlin.jvm.internal.SuspendFunction0<T>).create(completion)
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* An optional [dispatcher] may be specified to customise dispatch of continuations between suspension points inside the coroutine.
|
||||
*/
|
||||
@SinceKotlin("1.1")
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
public fun <T> (suspend () -> T).startCoroutine(
|
||||
completion: Continuation<T>,
|
||||
dispatcher: ContinuationDispatcher? = null
|
||||
completion: Continuation<T>
|
||||
) {
|
||||
(this as Function1<Continuation<T>, Any?>).invoke(withDispatcher(completion, dispatcher))
|
||||
(this as Function1<Continuation<T>, Any?>).invoke(completion)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,20 +76,15 @@ public inline suspend fun <T> suspendCoroutine(crossinline block: (Continuation<
|
||||
|
||||
// INTERNAL DECLARATIONS
|
||||
|
||||
@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER", "CANNOT_OVERRIDE_INVISIBLE_MEMBER")
|
||||
private fun <T> withDispatcher(completion: Continuation<T>, dispatcher: ContinuationDispatcher?): Continuation<T> {
|
||||
return if (dispatcher == null) completion else
|
||||
object : kotlin.jvm.internal.DispatchedContinuation<T>, Continuation<T> by completion {
|
||||
override val dispatcher: ContinuationDispatcher? = dispatcher
|
||||
}
|
||||
}
|
||||
|
||||
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>) : Continuation<T> {
|
||||
public override val context: CoroutineContext
|
||||
get() = delegate.context
|
||||
|
||||
@Volatile
|
||||
private var result: Any? = UNDECIDED
|
||||
|
||||
|
||||
Reference in New Issue
Block a user