Switch sourcesets of experimental and release coroutines

- Move experimental coroutines out of the main source root.
- Include experimental coroutines into the coroutines source set.
- Include release coroutines into the main source set.
This commit is contained in:
Ilya Gorbunov
2018-08-31 20:53:08 +03:00
parent 0fa2d29779
commit e22ca022d4
31 changed files with 25 additions and 30 deletions
@@ -0,0 +1,108 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package kotlin.coroutines.experimental
import kotlin.coroutines.experimental.intrinsics.*
@JsName("CoroutineImpl")
internal abstract class CoroutineImpl(private val resultContinuation: Continuation<Any?>) : Continuation<Any?> {
protected var state = 0
protected var exceptionState = 0
protected var result: Any? = null
protected var exception: Throwable? = null
protected var finallyPath: Array<Int>? = null
public override val context: CoroutineContext = resultContinuation.context
val facade: Continuation<Any?> = context[ContinuationInterceptor]?.interceptContinuation(this) ?: this
override fun resume(value: Any?) {
result = value
doResumeWrapper()
}
override fun resumeWithException(exception: Throwable) {
state = exceptionState
this.exception = exception
doResumeWrapper()
}
protected fun doResumeWrapper() {
processBareContinuationResume(resultContinuation) { doResume() }
}
protected abstract fun doResume(): Any?
}
private val UNDECIDED: Any? = Any()
private val RESUMED: Any? = Any()
private class Fail(val exception: Throwable)
@PublishedApi
internal actual class SafeContinuation<in T>
internal actual constructor(
private val delegate: Continuation<T>,
initialResult: Any?
) : Continuation<T> {
@PublishedApi
internal actual constructor(delegate: Continuation<T>) : this(delegate, UNDECIDED)
public actual override val context: CoroutineContext
get() = delegate.context
private var result: Any? = initialResult
actual override fun resume(value: T) {
when {
result === UNDECIDED -> {
result = value
}
result === COROUTINE_SUSPENDED -> {
result = RESUMED
delegate.resume(value)
}
else -> {
throw IllegalStateException("Already resumed")
}
}
}
actual override fun resumeWithException(exception: Throwable) {
when {
result === UNDECIDED -> {
result = Fail(exception)
}
result === COROUTINE_SUSPENDED -> {
result = RESUMED
delegate.resumeWithException(exception)
}
else -> {
throw IllegalStateException("Already resumed")
}
}
}
@PublishedApi
internal actual fun getResult(): Any? {
if (result === UNDECIDED) {
result = COROUTINE_SUSPENDED
}
val result = this.result
return when {
result === RESUMED -> {
COROUTINE_SUSPENDED // already called continuation, indicate SUSPENDED upstream
}
result is Fail -> {
throw result.exception
}
else -> {
result // either SUSPENDED or data
}
}
}
}
@@ -0,0 +1,46 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
package kotlin.coroutines.experimental.intrinsics
import kotlin.coroutines.experimental.Continuation
@SinceKotlin("1.1")
@Suppress("UNCHECKED_CAST")
@kotlin.internal.InlineOnly
public actual inline fun <T> (suspend () -> T).startCoroutineUninterceptedOrReturn(
completion: Continuation<T>
): Any? = this.asDynamic()(completion, false)
@SinceKotlin("1.1")
@Suppress("UNCHECKED_CAST")
@kotlin.internal.InlineOnly
public actual inline fun <R, T> (suspend R.() -> T).startCoroutineUninterceptedOrReturn(
receiver: R,
completion: Continuation<T>
): Any? = this.asDynamic()(receiver, completion, false)
@SinceKotlin("1.1")
public actual fun <R, T> (suspend R.() -> T).createCoroutineUnchecked(
receiver: R,
completion: Continuation<T>
): Continuation<Unit> = this.asDynamic()(receiver, completion, true).facade
@SinceKotlin("1.1")
public actual fun <T> (suspend () -> T).createCoroutineUnchecked(
completion: Continuation<T>
): Continuation<Unit> = this.asDynamic()(completion, true).facade
/**
* This value is used as a return value of [suspendCoroutineOrReturn] `block` argument to state that
* the execution was suspended and will not return any result immediately.
*/
@SinceKotlin("1.1")
public actual val COROUTINE_SUSPENDED: Any = CoroutineSuspendedMarker
private object CoroutineSuspendedMarker
@@ -0,0 +1,81 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package kotlin.coroutines.experimental
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater
import kotlin.*
import kotlin.coroutines.experimental.intrinsics.COROUTINE_SUSPENDED
@PublishedApi
internal actual class SafeContinuation<in T>
internal actual constructor(
private val delegate: Continuation<T>,
initialResult: Any?
) : Continuation<T> {
@PublishedApi
internal actual constructor(delegate: Continuation<T>) : this(delegate, UNDECIDED)
public actual 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)
actual 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")
}
}
}
actual 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 actual 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,126 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
@file:kotlin.jvm.JvmName("IntrinsicsKt")
@file:kotlin.jvm.JvmMultifileClass
@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
package kotlin.coroutines.experimental.intrinsics
import kotlin.coroutines.experimental.*
/**
* Starts unintercepted coroutine without receiver and with result type [T] and executes it until its first suspension.
* Returns the result of the coroutine or throws its exception if it does not suspend or [COROUTINE_SUSPENDED] if it suspends.
* In the later case, the [completion] continuation is invoked when coroutine completes with result or exception.
* This function is designed to be used from inside of [suspendCoroutineOrReturn] to resume the execution of suspended
* coroutine using a reference to the suspending function.
*/
@SinceKotlin("1.1")
@Suppress("UNCHECKED_CAST")
@kotlin.internal.InlineOnly
public actual inline fun <T> (suspend () -> T).startCoroutineUninterceptedOrReturn(
completion: Continuation<T>
): Any? = (this as Function1<Continuation<T>, Any?>).invoke(completion)
/**
* Starts unintercepted coroutine with receiver type [R] and result type [T] and executes it until its first suspension.
* Returns the result of the coroutine or throws its exception if it does not suspend or [COROUTINE_SUSPENDED] if it suspends.
* In the later case, the [completion] continuation is invoked when coroutine completes with result or exception.
* This function is designed to be used from inside of [suspendCoroutineOrReturn] to resume the execution of suspended
* coroutine using a reference to the suspending function.
*/
@SinceKotlin("1.1")
@Suppress("UNCHECKED_CAST")
@kotlin.internal.InlineOnly
public actual inline fun <R, T> (suspend R.() -> T).startCoroutineUninterceptedOrReturn(
receiver: R,
completion: Continuation<T>
): Any? = (this as Function2<R, Continuation<T>, Any?>).invoke(receiver, completion)
// JVM declarations
/**
* Creates a 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.
*
* This function is _unchecked_. Repeated invocation of any resume function on the resulting continuation corrupts the
* state machine of the coroutine and may result in arbitrary behaviour or exception.
*/
@SinceKotlin("1.1")
public actual fun <T> (suspend () -> T).createCoroutineUnchecked(
completion: Continuation<T>
): Continuation<Unit> =
if (this !is kotlin.coroutines.experimental.jvm.internal.CoroutineImpl)
buildContinuationByInvokeCall(completion) {
@Suppress("UNCHECKED_CAST")
(this as Function1<Continuation<T>, Any?>).invoke(completion)
}
else
(this.create(completion) as kotlin.coroutines.experimental.jvm.internal.CoroutineImpl).facade
/**
* Creates a 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.
*
* This function is _unchecked_. Repeated invocation of any resume function on the resulting continuation corrupts the
* state machine of the coroutine and may result in arbitrary behaviour or exception.
*/
@SinceKotlin("1.1")
public actual fun <R, T> (suspend R.() -> T).createCoroutineUnchecked(
receiver: R,
completion: Continuation<T>
): Continuation<Unit> =
if (this !is kotlin.coroutines.experimental.jvm.internal.CoroutineImpl)
buildContinuationByInvokeCall(completion) {
@Suppress("UNCHECKED_CAST")
(this as Function2<R, Continuation<T>, Any?>).invoke(receiver, completion)
}
else
(this.create(receiver, completion) as kotlin.coroutines.experimental.jvm.internal.CoroutineImpl).facade
// INTERNAL DEFINITIONS
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) {
processBareContinuationResume(completion, block)
}
override fun resumeWithException(exception: Throwable) {
completion.resumeWithException(exception)
}
}
return kotlin.coroutines.experimental.jvm.internal.interceptContinuationIfNeeded(completion.context, continuation)
}
/**
* This value is used as a return value of [suspendCoroutineOrReturn] `block` argument to state that
* the execution was suspended and will not return any result immediately.
*/
@SinceKotlin("1.1")
public actual val COROUTINE_SUSPENDED: Any get() = CoroutineSingletons.COROUTINE_SUSPENDED
// Using enum here ensures two important properties:
// 1. It makes SafeContinuation serializable with all kinds of serialization frameworks (since all of them natively support enums)
// 2. It improves debugging experience, since you clearly see toString() value of those objects and what package they come from
private enum class CoroutineSingletons {
COROUTINE_SUSPENDED
}
@@ -0,0 +1,61 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package kotlin.coroutines.experimental.jvm.internal
import java.lang.IllegalStateException
import kotlin.coroutines.experimental.Continuation
import kotlin.coroutines.experimental.CoroutineContext
import kotlin.coroutines.experimental.processBareContinuationResume
import kotlin.jvm.internal.Lambda
/**
* @suppress
*/
abstract class CoroutineImpl(
arity: Int,
@JvmField
protected var completion: Continuation<Any?>?
) : Lambda<Any?>(arity), Continuation<Any?> {
// label == -1 when coroutine cannot be started (it is just a factory object) or has already finished execution
// label == 0 in initial part of the coroutine
@JvmField
protected var label: Int = if (completion != null) 0 else -1
private val _context: CoroutineContext? = completion?.context
override val context: CoroutineContext
get() = _context!!
private var _facade: Continuation<Any?>? = null
val facade: Continuation<Any?> get() {
if (_facade == null) _facade = interceptContinuationIfNeeded(_context!!, this)
return _facade!!
}
override fun resume(value: Any?) {
processBareContinuationResume(completion!!) {
doResume(value, null)
}
}
override fun resumeWithException(exception: Throwable) {
processBareContinuationResume(completion!!) {
doResume(null, exception)
}
}
protected abstract fun doResume(data: Any?, exception: Throwable?): Any?
open fun create(completion: Continuation<*>): Continuation<Unit> {
throw IllegalStateException("create(Continuation) has not been overridden")
}
open fun create(value: Any?, completion: Continuation<*>): Continuation<Unit> {
throw IllegalStateException("create(Any?;Continuation) has not been overridden")
}
}
@@ -0,0 +1,23 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
@file:JvmName("CoroutineIntrinsics")
package kotlin.coroutines.experimental.jvm.internal
import kotlin.coroutines.experimental.Continuation
import kotlin.coroutines.experimental.ContinuationInterceptor
import kotlin.coroutines.experimental.CoroutineContext
/**
* @suppress
*/
fun <T> normalizeContinuation(continuation: Continuation<T>): Continuation<T> =
(continuation as? CoroutineImpl)?.facade ?: continuation
internal fun <T> interceptContinuationIfNeeded(
context: CoroutineContext,
continuation: Continuation<T>
) = context[ContinuationInterceptor]?.interceptContinuation(continuation) ?: continuation
@@ -0,0 +1,144 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package kotlin.coroutines.experimental.migration
import kotlin.coroutines.experimental.Continuation as ExperimentalContinuation
import kotlin.coroutines.experimental.CoroutineContext as ExperimentalCoroutineContext
import kotlin.coroutines.experimental.AbstractCoroutineContextElement as ExperimentalAbstractCoroutineContextElement
import kotlin.coroutines.experimental.EmptyCoroutineContext as ExperimentalEmptyCoroutineContext
import kotlin.coroutines.experimental.ContinuationInterceptor as ExperimentalContinuationInterceptor
import kotlin.coroutines.*
import kotlin.coroutines.intrinsics.*
/**
* Converts [Continuation] to [ExperimentalContinuation].
*/
@SinceKotlin("1.3")
public fun <T> Continuation<T>.toExperimentalContinuation(): ExperimentalContinuation<T> =
(this as? ContinuationMigration<T>)?.continuation ?: ExperimentalContinuationMigration(this)
/**
* Converts [ExperimentalContinuation] to [Continuation].
*/
@SinceKotlin("1.3")
public fun <T> ExperimentalContinuation<T>.toContinuation(): Continuation<T> =
(this as? ExperimentalContinuationMigration<T>)?.continuation ?: ContinuationMigration(this)
/**
* Converts [CoroutineContext] to [ExperimentalCoroutineContext].
*/
@SinceKotlin("1.3")
public fun CoroutineContext.toExperimentalCoroutineContext(): ExperimentalCoroutineContext {
val interceptor = get(ContinuationInterceptor.Key)
val migration = get(ContextMigration.Key)
val remainder = minusKey(ContinuationInterceptor.Key).minusKey(ContextMigration.Key)
val original = migration?.context ?: ExperimentalEmptyCoroutineContext
val result = if (remainder === EmptyCoroutineContext) original else original + ExperimentalContextMigration(remainder)
return if (interceptor == null) result else result + interceptor.toExperimentalContinuationInterceptor()
}
/**
* Converts [ExperimentalCoroutineContext] to [CoroutineContext].
*/
@SinceKotlin("1.3")
public fun ExperimentalCoroutineContext.toCoroutineContext(): CoroutineContext {
val interceptor = get(ExperimentalContinuationInterceptor.Key)
val migration = get(ExperimentalContextMigration.Key)
val remainder = minusKey(ExperimentalContinuationInterceptor.Key).minusKey(ExperimentalContextMigration.Key)
val original = migration?.context ?: EmptyCoroutineContext
val result = if (remainder === ExperimentalEmptyCoroutineContext) original else original + ContextMigration(remainder)
return if (interceptor == null) result else result + interceptor.toContinuationInterceptor()
}
/**
* Converts [ContinuationInterceptor] to [ExperimentalContinuationInterceptor].
*/
@SinceKotlin("1.3")
public fun ContinuationInterceptor.toExperimentalContinuationInterceptor(): ExperimentalContinuationInterceptor =
(this as? ContinuationInterceptorMigration)?.interceptor ?: ExperimentalContinuationInterceptorMigration(this)
/**
* Converts [ExperimentalContinuationInterceptor] to [ContinuationInterceptor].
*/
@SinceKotlin("1.3")
public fun ExperimentalContinuationInterceptor.toContinuationInterceptor(): ContinuationInterceptor =
(this as? ExperimentalContinuationInterceptorMigration)?.interceptor ?: ContinuationInterceptorMigration(this)
// ------------------ converter classes ------------------
// Their name starts with "Experimental" if they implement the corresponding Experimental interfaces
private class ExperimentalContinuationMigration<T>(val continuation: Continuation<T>): ExperimentalContinuation<T> {
override val context = continuation.context.toExperimentalCoroutineContext()
override fun resume(value: T) = continuation.resume(value)
override fun resumeWithException(exception: Throwable) = continuation.resumeWithException(exception)
}
private class ContinuationMigration<T>(val continuation: ExperimentalContinuation<T>): Continuation<T> {
override val context: CoroutineContext = continuation.context.toCoroutineContext()
override fun resumeWith(result: SuccessOrFailure<T>) {
result
.onSuccess { continuation.resume(it) }
.onFailure { continuation.resumeWithException(it) }
}
}
private class ExperimentalContextMigration(val context: CoroutineContext): ExperimentalAbstractCoroutineContextElement(Key) {
companion object Key : ExperimentalCoroutineContext.Key<ExperimentalContextMigration>
}
private class ContextMigration(val context: ExperimentalCoroutineContext): AbstractCoroutineContextElement(Key) {
companion object Key : CoroutineContext.Key<ContextMigration>
}
private class ExperimentalContinuationInterceptorMigration(val interceptor: ContinuationInterceptor) : ExperimentalContinuationInterceptor {
override val key: ExperimentalCoroutineContext.Key<*>
get() = ExperimentalContinuationInterceptor.Key
override fun <T> interceptContinuation(continuation: ExperimentalContinuation<T>): ExperimentalContinuation<T> =
interceptor.interceptContinuation(continuation.toContinuation()).toExperimentalContinuation()
}
private class ContinuationInterceptorMigration(val interceptor: ExperimentalContinuationInterceptor) : ContinuationInterceptor {
override val key: CoroutineContext.Key<*>
get() = ContinuationInterceptor.Key
override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> =
interceptor.interceptContinuation(continuation.toExperimentalContinuation()).toContinuation()
}
internal fun <R> ((Continuation<R>) -> Any?).toExperimentalSuspendFunction(): (ExperimentalContinuation<R>) -> Any? =
ExperimentalSuspendFunction0Migration(this)
internal fun <T1, R> ((T1, Continuation<R>) -> Any?).toExperimentalSuspendFunction(): (T1, ExperimentalContinuation<R>) -> Any? =
ExperimentalSuspendFunction1Migration(this)
internal fun <T1, T2, R> ((T1, T2, Continuation<R>) -> Any?).toExperimentalSuspendFunction(): (T1, T2, ExperimentalContinuation<R>) -> Any? =
ExperimentalSuspendFunction2Migration(this)
private class ExperimentalSuspendFunction0Migration<R>(
val function: (Continuation<R>) -> Any?
) : (ExperimentalContinuation<R>) -> Any? {
override fun invoke(continuation: ExperimentalContinuation<R>): Any? {
return function(continuation.toContinuation())
}
}
private class ExperimentalSuspendFunction1Migration<T1, R>(
val function: (T1, Continuation<R>) -> Any?
) : (T1, ExperimentalContinuation<R>) -> Any? {
override fun invoke(t1: T1, continuation: ExperimentalContinuation<R>): Any? {
return function(t1, continuation.toContinuation())
}
}
private class ExperimentalSuspendFunction2Migration<T1, T2, R>(
val function: (T1, T2, Continuation<R>) -> Any?
) : (T1, T2, ExperimentalContinuation<R>) -> Any? {
override fun invoke(t1: T1, t2: T2, continuation: ExperimentalContinuation<R>): Any? {
return function(t1, t2, continuation.toContinuation())
}
}
@@ -0,0 +1,30 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package kotlin.coroutines.experimental
/**
* Marks coroutine context element that intercepts coroutine continuations.
* The coroutines framework uses [ContinuationInterceptor.Key] to retrieve the interceptor and
* intercepts all coroutine continuations with [interceptContinuation] invocations.
*/
@SinceKotlin("1.1")
public interface ContinuationInterceptor : CoroutineContext.Element {
/**
* The key that defines *the* context interceptor.
*/
companion object Key : CoroutineContext.Key<ContinuationInterceptor>
/**
* Returns continuation that wraps the original [continuation], thus intercepting all resumptions.
* This function is invoked by coroutines framework when needed and the resulting continuations are
* cached internally per each instance of the original [continuation].
*
* By convention, implementations that install themselves as *the* interceptor in the context with
* the [Key] shall also scan the context for other element that implement [ContinuationInterceptor] interface
* and use their [interceptContinuation] functions, too.
*/
public fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T>
}
@@ -0,0 +1,79 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
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]. 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?
/**
* Accumulates entries of this context starting with [initial] value and applying [operation]
* from left to right to current accumulator value and each element of this context.
*/
public fun <R> fold(initial: R, operation: (R, Element) -> R): R
/**
* Returns a context containing elements from this context and elements from other [context].
* The elements from this context with the same key as in the other one are dropped.
*/
public operator fun plus(context: CoroutineContext): CoroutineContext =
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 {
// 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)
}
}
}
/**
* Returns a context containing elements from this context, but without an element with
* 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
/**
* An element of the [CoroutineContext]. An element of the coroutine context is a singleton context by itself.
*/
public interface Element : CoroutineContext {
/**
* A key of this coroutine context element.
*/
public val key: Key<*>
@Suppress("UNCHECKED_CAST")
public override operator fun <E : Element> get(key: Key<E>): E? =
if (this.key === key) this as E else null
public override fun <R> fold(initial: R, operation: (R, Element) -> R): R =
operation(initial, this)
public override fun minusKey(key: Key<*>): CoroutineContext =
if (this.key === key) EmptyCoroutineContext else this
}
/**
* 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>
}
@@ -0,0 +1,88 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package kotlin.coroutines.experimental
import kotlin.coroutines.experimental.CoroutineContext.*
/**
* Base class for [CoroutineContext.Element] implementations.
*/
@SinceKotlin("1.1")
public abstract class AbstractCoroutineContextElement(public override val key: Key<*>) : Element
/**
* An empty coroutine context.
*/
@SinceKotlin("1.1")
public object EmptyCoroutineContext : CoroutineContext {
public override fun <E : Element> get(key: Key<E>): E? = null
public override fun <R> fold(initial: R, operation: (R, Element) -> R): R = initial
public override fun plus(context: CoroutineContext): CoroutineContext = context
public override fun minusKey(key: Key<*>): CoroutineContext = this
public override fun hashCode(): Int = 0
public override fun toString(): String = "EmptyCoroutineContext"
}
//--------------------- internal impl ---------------------
// this class is not exposed, but is hidden inside implementations
// this is a left-biased list, so that `plus` works naturally
internal class CombinedContext(val left: CoroutineContext, val element: Element) : CoroutineContext {
override fun <E : Element> get(key: Key<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, Element) -> R): R =
operation(left.fold(initial, operation), element)
public override fun minusKey(key: Key<*>): CoroutineContext {
element[key]?.let { return left }
val newLeft = left.minusKey(key)
return when {
newLeft === left -> this
newLeft === EmptyCoroutineContext -> element
else -> CombinedContext(newLeft, element)
}
}
private fun size(): Int =
if (left is CombinedContext) left.size() + 1 else 2
private fun contains(element: Element): Boolean =
get(element.key) == 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 Element)
}
}
}
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
} + "]"
}
@@ -0,0 +1,38 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package kotlin.coroutines.experimental
/**
* Interface representing a continuation after a suspension point that returns value of type `T`.
*/
@SinceKotlin("1.1")
public interface Continuation<in T> {
/**
* Context of the coroutine that corresponds to this continuation.
*/
public val context: CoroutineContext
/**
* Resumes the execution of the corresponding coroutine passing [value] as the return value of the last suspension point.
*/
public fun resume(value: T)
/**
* Resumes the execution of the corresponding coroutine so that the [exception] is re-thrown right after the
* last suspension point.
*/
public fun resumeWithException(exception: Throwable)
}
/**
* Classes and interfaces marked with this annotation are restricted when used as receivers for extension
* `suspend` functions. These `suspend` extensions can only invoke other member or extension `suspend` functions on this particular
* receiver only and are restricted from calling arbitrary suspension functions.
*/
@SinceKotlin("1.1")
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.BINARY)
public annotation class RestrictsSuspension
@@ -0,0 +1,21 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package kotlin.coroutines.experimental
@PublishedApi
internal expect class SafeContinuation<in T> : Continuation<T> {
internal constructor(delegate: Continuation<T>, initialResult: Any?)
@PublishedApi
internal constructor(delegate: Continuation<T>)
@PublishedApi
internal fun getResult(): Any?
override val context: CoroutineContext
override fun resume(value: T): Unit
override fun resumeWithException(exception: Throwable): Unit
}
@@ -0,0 +1,44 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package kotlin.coroutines.experimental.intrinsics
import kotlin.coroutines.experimental.Continuation
/**
* Starts unintercepted coroutine without receiver and with result type [T] and executes it until its first suspension.
* Returns the result of the coroutine or throws its exception if it does not suspend or [COROUTINE_SUSPENDED] if it suspends.
* In the later case, the [completion] continuation is invoked when coroutine completes with result or exception.
* This function is designed to be used from inside of [suspendCoroutineOrReturn] to resume the execution of suspended
* coroutine using a reference to the suspending function.
*/
@SinceKotlin("1.1")
public expect inline fun <T> (suspend () -> T).startCoroutineUninterceptedOrReturn(
completion: Continuation<T>
): Any?
/**
* Starts unintercepted coroutine with receiver type [R] and result type [T] and executes it until its first suspension.
* Returns the result of the coroutine or throws its exception if it does not suspend or [COROUTINE_SUSPENDED] if it suspends.
* In the later case, the [completion] continuation is invoked when coroutine completes with result or exception.
* This function is designed to be used from inside of [suspendCoroutineOrReturn] to resume the execution of suspended
* coroutine using a reference to the suspending function.
*/
@SinceKotlin("1.1")
public expect inline fun <R, T> (suspend R.() -> T).startCoroutineUninterceptedOrReturn(
receiver: R,
completion: Continuation<T>
): Any?
@SinceKotlin("1.1")
public expect fun <T> (suspend () -> T).createCoroutineUnchecked(
completion: Continuation<T>
): Continuation<Unit>
@SinceKotlin("1.1")
public expect fun <R, T> (suspend R.() -> T).createCoroutineUnchecked(
receiver: R,
completion: Continuation<T>
): Continuation<Unit>
@@ -0,0 +1,117 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
@file:kotlin.jvm.JvmName("CoroutinesKt")
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
package kotlin.coroutines.experimental
import kotlin.coroutines.experimental.intrinsics.COROUTINE_SUSPENDED
import kotlin.coroutines.experimental.intrinsics.createCoroutineUnchecked
import kotlin.coroutines.experimental.intrinsics.suspendCoroutineOrReturn
import kotlin.internal.InlineOnly
/**
* 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 or exception.
*/
@SinceKotlin("1.1")
@Suppress("UNCHECKED_CAST")
public fun <R, T> (suspend R.() -> T).startCoroutine(
receiver: R,
completion: Continuation<T>
) {
createCoroutineUnchecked(receiver, completion).resume(Unit)
}
/**
* 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 or exception.
*/
@SinceKotlin("1.1")
@Suppress("UNCHECKED_CAST")
public fun <T> (suspend () -> T).startCoroutine(
completion: Continuation<T>
) {
createCoroutineUnchecked(completion).resume(Unit)
}
/**
* Creates a 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.
* Repeated invocation of any resume function on the resulting continuation produces [IllegalStateException].
*/
@SinceKotlin("1.1")
@Suppress("UNCHECKED_CAST")
public fun <R, T> (suspend R.() -> T).createCoroutine(
receiver: R,
completion: Continuation<T>
): Continuation<Unit> = SafeContinuation(createCoroutineUnchecked(receiver, completion), COROUTINE_SUSPENDED)
/**
* Creates a 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.
* Repeated invocation of any resume function on the resulting continuation produces [IllegalStateException].
*/
@SinceKotlin("1.1")
@Suppress("UNCHECKED_CAST")
public fun <T> (suspend () -> T).createCoroutine(
completion: Continuation<T>
): Continuation<Unit> = SafeContinuation(createCoroutineUnchecked(completion), COROUTINE_SUSPENDED)
/**
* 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 inline fun <T> suspendCoroutine(crossinline block: (Continuation<T>) -> Unit): T =
suspendCoroutineOrReturn { c: Continuation<T> ->
val safe = SafeContinuation(c)
block(safe)
safe.getResult()
}
/**
* Continuation context of current coroutine.
*
* This allows the user code to not pass an extra [CoroutineContext] parameter in basic coroutine builders
* like [launch](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/launch.html)
* and [async](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/async.html),
* but still provide easy access to coroutine context.
*/
@SinceKotlin("1.2")
@Suppress("WRONG_MODIFIER_TARGET")
@InlineOnly
public suspend inline val coroutineContext: CoroutineContext
get() {
throw NotImplementedError("Implemented as intrinsic")
}
// INTERNAL DECLARATIONS
@kotlin.internal.InlineOnly
internal inline fun processBareContinuationResume(completion: Continuation<*>, block: () -> Any?) {
try {
val result = block()
if (result !== COROUTINE_SUSPENDED) {
@Suppress("UNCHECKED_CAST")
(completion as Continuation<Any?>).resume(result)
}
} catch (t: Throwable) {
completion.resumeWithException(t)
}
}
@@ -0,0 +1,184 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
@file:kotlin.jvm.JvmMultifileClass
@file:kotlin.jvm.JvmName("SequenceBuilderKt")
package kotlin.coroutines.experimental
import kotlin.*
import kotlin.coroutines.experimental.intrinsics.*
/**
* Builds a [Sequence] lazily yielding values one by one.
*
* @see kotlin.sequences.generateSequence
*
* @sample samples.collections.Sequences.Building.buildSequenceYieldAll
* @sample samples.collections.Sequences.Building.buildFibonacciSequence
*/
@SinceKotlin("1.1")
public fun <T> buildSequence(builderAction: suspend SequenceBuilder<T>.() -> Unit): Sequence<T> = Sequence { buildIterator(builderAction) }
/**
* Builds an [Iterator] lazily yielding values one by one.
*
* @sample samples.collections.Sequences.Building.buildIterator
* @sample samples.collections.Iterables.Building.iterable
*/
@SinceKotlin("1.1")
public fun <T> buildIterator(builderAction: suspend SequenceBuilder<T>.() -> Unit): Iterator<T> {
val iterator = SequenceBuilderIterator<T>()
iterator.nextStep = builderAction.createCoroutineUnchecked(receiver = iterator, completion = iterator)
return iterator
}
/**
* Builder for a [Sequence] or an [Iterator], provides [yield] and [yieldAll] suspension functions.
*
* @see buildSequence
* @see buildIterator
*
* @sample samples.collections.Sequences.Building.buildSequenceYieldAll
* @sample samples.collections.Sequences.Building.buildFibonacciSequence
*/
@RestrictsSuspension
@SinceKotlin("1.1")
public abstract class SequenceBuilder<in T> internal constructor() {
/**
* Yields a value to the [Iterator] being built.
*
* @sample samples.collections.Sequences.Building.buildSequenceYieldAll
* @sample samples.collections.Sequences.Building.buildFibonacciSequence
*/
public abstract suspend fun yield(value: T)
/**
* Yields all values from the `iterator` to the [Iterator] being built.
*
* The sequence of values returned by the given iterator can be potentially infinite.
*
* @sample samples.collections.Sequences.Building.buildSequenceYieldAll
*/
public abstract suspend fun yieldAll(iterator: Iterator<T>)
/**
* Yields a collections of values to the [Iterator] being built.
*
* @sample samples.collections.Sequences.Building.buildSequenceYieldAll
*/
public suspend fun yieldAll(elements: Iterable<T>) {
if (elements is Collection && elements.isEmpty()) return
return yieldAll(elements.iterator())
}
/**
* Yields potentially infinite sequence of values to the [Iterator] being built.
*
* The sequence can be potentially infinite.
*
* @sample samples.collections.Sequences.Building.buildSequenceYieldAll
*/
public suspend fun yieldAll(sequence: Sequence<T>) = yieldAll(sequence.iterator())
}
private typealias State = Int
private const val State_NotReady: State = 0
private const val State_ManyNotReady: State = 1
private const val State_ManyReady: State = 2
private const val State_Ready: State = 3
private const val State_Done: State = 4
private const val State_Failed: State = 5
private class SequenceBuilderIterator<T> : SequenceBuilder<T>(), Iterator<T>, Continuation<Unit> {
private var state = State_NotReady
private var nextValue: T? = null
private var nextIterator: Iterator<T>? = null
var nextStep: Continuation<Unit>? = null
override fun hasNext(): Boolean {
while (true) {
when (state) {
State_NotReady -> {}
State_ManyNotReady ->
if (nextIterator!!.hasNext()) {
state = State_ManyReady
return true
} else {
nextIterator = null
}
State_Done -> return false
State_Ready, State_ManyReady -> return true
else -> throw exceptionalState()
}
state = State_Failed
val step = nextStep!!
nextStep = null
step.resume(Unit)
}
}
override fun next(): T {
when (state) {
State_NotReady, State_ManyNotReady -> return nextNotReady()
State_ManyReady -> {
state = State_ManyNotReady
return nextIterator!!.next()
}
State_Ready -> {
state = State_NotReady
@Suppress("UNCHECKED_CAST")
val result = nextValue as T
nextValue = null
return result
}
else -> throw exceptionalState()
}
}
private fun nextNotReady(): T {
if (!hasNext()) throw NoSuchElementException() else return next()
}
private fun exceptionalState(): Throwable = when (state) {
State_Done -> NoSuchElementException()
State_Failed -> IllegalStateException("Iterator has failed.")
else -> IllegalStateException("Unexpected state of the iterator: $state")
}
suspend override fun yield(value: T) {
nextValue = value
state = State_Ready
return suspendCoroutineOrReturn { c ->
nextStep = c
COROUTINE_SUSPENDED
}
}
suspend override fun yieldAll(iterator: Iterator<T>) {
if (!iterator.hasNext()) return
nextIterator = iterator
state = State_ManyReady
return suspendCoroutineOrReturn { c ->
nextStep = c
COROUTINE_SUSPENDED
}
}
// Completion continuation implementation
override fun resume(value: Unit) {
state = State_Done
}
override fun resumeWithException(exception: Throwable) {
throw exception // just rethrow
}
override val context: CoroutineContext
get() = EmptyCoroutineContext
}
@@ -0,0 +1,60 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
@file:kotlin.jvm.JvmName("IntrinsicsKt")
@file:kotlin.jvm.JvmMultifileClass
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
package kotlin.coroutines.experimental.intrinsics
import kotlin.coroutines.experimental.*
/**
* Obtains the current continuation instance inside suspend functions and either suspends
* currently running coroutine or returns result immediately without suspension.
*
* If the [block] returns the special [COROUTINE_SUSPENDED] value, it means that suspend function did suspend the execution and will
* not return any result immediately. In this case, the [Continuation] provided to the [block] shall be invoked at some moment in the
* future when the result becomes available to resume the computation.
*
* Otherwise, the return value of the [block] must have a type assignable to [T] and represents the result of this suspend function.
* It means that the execution was not suspended and the [Continuation] provided to the [block] shall not be invoked.
* As the result type of the [block] is declared as `Any?` and cannot be correctly type-checked,
* its proper return type remains on the conscience of the suspend function's author.
*
* Note that it is not recommended to call either [Continuation.resume] nor [Continuation.resumeWithException] functions synchronously
* in the same stackframe where suspension function is run. Use [suspendCoroutine] as a safer way to obtain current
* continuation instance.
*/
@SinceKotlin("1.1")
@kotlin.internal.InlineOnly
@Suppress("UNUSED_PARAMETER")
public suspend inline fun <T> suspendCoroutineOrReturn(crossinline block: (Continuation<T>) -> Any?): T =
suspendCoroutineUninterceptedOrReturn { cont -> block(cont.intercepted()) }
/**
* Obtains the current continuation instance inside suspend functions and either suspends
* currently running coroutine or returns result immediately without suspension.
*
* Unlike [suspendCoroutineOrReturn] it does not intercept continuation.
*/
@SinceKotlin("1.2")
@kotlin.internal.InlineOnly
public suspend inline fun <T> suspendCoroutineUninterceptedOrReturn(crossinline block: (Continuation<T>) -> Any?): T =
throw NotImplementedError("Implementation of suspendCoroutineUninterceptedOrReturn is intrinsic")
/**
* Intercept continuation with [ContinuationInterceptor].
*/
@SinceKotlin("1.2")
@kotlin.internal.InlineOnly
public inline fun <T> Continuation<T>.intercepted(): Continuation<T> =
throw NotImplementedError("Implementation of intercepted is intrinsic")
/**
* This value is used as a return value of [suspendCoroutineOrReturn] `block` argument to state that
* the execution was suspended and will not return any result immediately.
*/
@SinceKotlin("1.1")
public expect val COROUTINE_SUSPENDED: Any // get() = CoroutineSuspendedMarker