Fixed corotine context performance by using reference equality for contexts and keys
Added documentation explaining that keys are compared by reference
This commit is contained in:
@@ -19,12 +19,14 @@ package kotlin.coroutines.experimental
|
||||
/**
|
||||
* Persistent context for the coroutine. It is an indexed set of [Element] instances.
|
||||
* An indexed set is a mix between a set and a map.
|
||||
* Every element in this set has a unique [Key].
|
||||
* Every element in this set has a unique [Key]. Keys are compared _by reference_.
|
||||
*/
|
||||
@SinceKotlin("1.1")
|
||||
public interface CoroutineContext {
|
||||
/**
|
||||
* Returns the element with the given [key] from this context or `null`.
|
||||
* Keys are compared _by reference_, that is to get an element from the context the reference to its actual key
|
||||
* object must be presented to this function.
|
||||
*/
|
||||
public operator fun <E : Element> get(key: Key<E>): E?
|
||||
|
||||
@@ -42,7 +44,8 @@ public interface CoroutineContext {
|
||||
|
||||
/**
|
||||
* Returns a context containing elements from this context, but without an element with
|
||||
* the specified [key].
|
||||
* the specified [key]. Keys are compared _by reference_, that is to remove an element from the context
|
||||
* the reference to its actual key object must be presented to this function.
|
||||
*/
|
||||
public fun minusKey(key: Key<*>): CoroutineContext
|
||||
|
||||
@@ -58,6 +61,7 @@ public interface CoroutineContext {
|
||||
|
||||
/**
|
||||
* Key for the elements of [CoroutineContext]. [E] is a type of element with this key.
|
||||
* Keys in the context are compared _by reference_.
|
||||
*/
|
||||
public interface Key<E : Element>
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ import kotlin.coroutines.experimental.CoroutineContext.*
|
||||
public abstract class AbstractCoroutineContextElement(public override val key: Key<*>) : Element {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
public override operator fun <E : Element> get(key: Key<E>): E? =
|
||||
if (this.key == key) this as E else null
|
||||
if (this.key === key) this as E else null
|
||||
|
||||
public override fun <R> fold(initial: R, operation: (R, Element) -> R): R =
|
||||
operation(initial, this)
|
||||
@@ -34,7 +34,7 @@ public abstract class AbstractCoroutineContextElement(public override val key: K
|
||||
plusImpl(context)
|
||||
|
||||
public override fun minusKey(key: Key<*>): CoroutineContext =
|
||||
if (this.key == key) EmptyCoroutineContext else this
|
||||
if (this.key === key) EmptyCoroutineContext else this
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,9 +77,9 @@ private class CombinedContext(val left: CoroutineContext, val element: Element)
|
||||
public override fun minusKey(key: Key<*>): CoroutineContext {
|
||||
element[key]?.let { return left }
|
||||
val newLeft = left.minusKey(key)
|
||||
return when (newLeft) {
|
||||
left -> this
|
||||
EmptyCoroutineContext -> element
|
||||
return when {
|
||||
newLeft === left -> this
|
||||
newLeft === EmptyCoroutineContext -> element
|
||||
else -> CombinedContext(newLeft, element)
|
||||
}
|
||||
}
|
||||
@@ -115,15 +115,15 @@ private class CombinedContext(val left: CoroutineContext, val element: Element)
|
||||
}
|
||||
|
||||
private fun CoroutineContext.plusImpl(context: CoroutineContext): CoroutineContext =
|
||||
if (context == EmptyCoroutineContext) this else // fast path -- avoid lambda creation
|
||||
if (context === EmptyCoroutineContext) this else // fast path -- avoid lambda creation
|
||||
context.fold(this) { acc, element ->
|
||||
val removed = acc.minusKey(element.key)
|
||||
if (removed == EmptyCoroutineContext) element else {
|
||||
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
|
||||
if (left === EmptyCoroutineContext) CombinedContext(element, interceptor) else
|
||||
CombinedContext(CombinedContext(left, element), interceptor)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user