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:
Roman Elizarov
2017-02-06 13:06:39 +03:00
parent 2f0e7b54d5
commit 5895c211a1
2 changed files with 14 additions and 10 deletions
@@ -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)
}
}