diff --git a/libraries/stdlib/coroutines/common/src/kotlin/CoroutinesH.kt b/libraries/stdlib/coroutines/common/src/kotlin/CoroutinesH.kt new file mode 100644 index 00000000000..9ed8cb5eeee --- /dev/null +++ b/libraries/stdlib/coroutines/common/src/kotlin/CoroutinesH.kt @@ -0,0 +1,22 @@ +/* + * 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 + +@PublishedApi +@SinceKotlin("1.3") +internal expect class SafeContinuation : Continuation { + internal constructor(delegate: Continuation, initialResult: Any?) + + @PublishedApi + internal constructor(delegate: Continuation) + + @PublishedApi + internal fun getResult(): Any? + + override val context: CoroutineContext + override fun resume(value: T): Unit + override fun resumeWithException(exception: Throwable): Unit +} diff --git a/libraries/stdlib/coroutines/common/src/kotlin/CoroutinesIntrinsicsH.kt b/libraries/stdlib/coroutines/common/src/kotlin/CoroutinesIntrinsicsH.kt new file mode 100644 index 00000000000..86e9e683f98 --- /dev/null +++ b/libraries/stdlib/coroutines/common/src/kotlin/CoroutinesIntrinsicsH.kt @@ -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.intrinsics + +import kotlin.coroutines.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.3") +public expect inline fun (suspend () -> T).startCoroutineUninterceptedOrReturn( + completion: Continuation +): 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.3") +public expect inline fun (suspend R.() -> T).startCoroutineUninterceptedOrReturn( + receiver: R, + completion: Continuation +): Any? + +@SinceKotlin("1.3") +public expect fun (suspend () -> T).createCoroutineUnchecked( + completion: Continuation +): Continuation + +@SinceKotlin("1.3") +public expect fun (suspend R.() -> T).createCoroutineUnchecked( + receiver: R, + completion: Continuation +): Continuation diff --git a/libraries/stdlib/coroutines/jvm/src/kotlin/coroutines/SafeContinuationJvm.kt b/libraries/stdlib/coroutines/jvm/src/kotlin/coroutines/SafeContinuationJvm.kt new file mode 100644 index 00000000000..bcb4952e9a7 --- /dev/null +++ b/libraries/stdlib/coroutines/jvm/src/kotlin/coroutines/SafeContinuationJvm.kt @@ -0,0 +1,83 @@ +/* + * 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") +@file:kotlin.jvm.JvmVersion +package kotlin.coroutines + +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater +import kotlin.* +import kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED + +@PublishedApi +@SinceKotlin("1.3") +internal actual class SafeContinuation +internal actual constructor( + private val delegate: Continuation, + initialResult: Any? +) : Continuation { + + @PublishedApi + internal actual constructor(delegate: Continuation) : 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, Any?>( + SafeContinuation::class.java, Any::class.java as Class, "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 + } + } +} diff --git a/libraries/stdlib/coroutines/jvm/src/kotlin/coroutines/intrinsics/IntrinsicsJvm.kt b/libraries/stdlib/coroutines/jvm/src/kotlin/coroutines/intrinsics/IntrinsicsJvm.kt new file mode 100644 index 00000000000..b979cb0d3f1 --- /dev/null +++ b/libraries/stdlib/coroutines/jvm/src/kotlin/coroutines/intrinsics/IntrinsicsJvm.kt @@ -0,0 +1,114 @@ +/* + * 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") +@file:kotlin.jvm.JvmVersion +package kotlin.coroutines.intrinsics +import kotlin.coroutines.* + +/** + * 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.3") +@Suppress("UNCHECKED_CAST") +@kotlin.internal.InlineOnly +public actual inline fun (suspend () -> T).startCoroutineUninterceptedOrReturn( + completion: Continuation +): Any? = (this as Function1, 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.3") +@Suppress("UNCHECKED_CAST") +@kotlin.internal.InlineOnly +public actual inline fun (suspend R.() -> T).startCoroutineUninterceptedOrReturn( + receiver: R, + completion: Continuation +): Any? = (this as Function2, 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.3") +@kotlin.jvm.JvmVersion +public actual fun (suspend () -> T).createCoroutineUnchecked( + completion: Continuation +): Continuation = + if (this !is kotlin.coroutines.jvm.internal.CoroutineImpl) + buildContinuationByInvokeCall(completion) { + @Suppress("UNCHECKED_CAST") + (this as Function1, Any?>).invoke(completion) + } + else + (this.create(completion) as kotlin.coroutines.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.3") +@kotlin.jvm.JvmVersion +public actual fun (suspend R.() -> T).createCoroutineUnchecked( + receiver: R, + completion: Continuation +): Continuation = + if (this !is kotlin.coroutines.jvm.internal.CoroutineImpl) + buildContinuationByInvokeCall(completion) { + @Suppress("UNCHECKED_CAST") + (this as Function2, Any?>).invoke(receiver, completion) + } + else + (this.create(receiver, completion) as kotlin.coroutines.jvm.internal.CoroutineImpl).facade + +// INTERNAL DEFINITIONS + +@kotlin.jvm.JvmVersion +private inline fun buildContinuationByInvokeCall( + completion: Continuation, + crossinline block: () -> Any? +): Continuation { + val continuation = + object : Continuation { + 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.jvm.internal.interceptContinuationIfNeeded(completion.context, continuation) +} diff --git a/libraries/stdlib/coroutines/jvm/src/kotlin/coroutines/jvm/internal/CoroutineImpl.kt b/libraries/stdlib/coroutines/jvm/src/kotlin/coroutines/jvm/internal/CoroutineImpl.kt new file mode 100644 index 00000000000..9a5045c10ed --- /dev/null +++ b/libraries/stdlib/coroutines/jvm/src/kotlin/coroutines/jvm/internal/CoroutineImpl.kt @@ -0,0 +1,64 @@ +/* + * 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") +@file:JvmVersion +package kotlin.coroutines.jvm.internal + +import java.lang.IllegalStateException +import kotlin.coroutines.Continuation +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.processBareContinuationResume +import kotlin.jvm.internal.Lambda + +/** + * @suppress + */ +@SinceKotlin("1.3") +public abstract class CoroutineImpl( + arity: Int, + @JvmField + protected var completion: Continuation? +) : Lambda(arity), Continuation { + + // 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? = null + + val facade: Continuation 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 { + throw IllegalStateException("create(Continuation) has not been overridden") + } + + open fun create(value: Any?, completion: Continuation<*>): Continuation { + throw IllegalStateException("create(Any?;Continuation) has not been overridden") + } +} diff --git a/libraries/stdlib/coroutines/jvm/src/kotlin/coroutines/jvm/internal/CoroutineIntrinsics.kt b/libraries/stdlib/coroutines/jvm/src/kotlin/coroutines/jvm/internal/CoroutineIntrinsics.kt new file mode 100644 index 00000000000..7aed4044eda --- /dev/null +++ b/libraries/stdlib/coroutines/jvm/src/kotlin/coroutines/jvm/internal/CoroutineIntrinsics.kt @@ -0,0 +1,26 @@ +/* + * 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") +@file:JvmVersion +@file:JvmName("CoroutineIntrinsics") +package kotlin.coroutines.jvm.internal + +import kotlin.coroutines.Continuation +import kotlin.coroutines.ContinuationInterceptor +import kotlin.coroutines.CoroutineContext + +/** + * @suppress + */ +@SinceKotlin("1.3") +public fun normalizeContinuation(continuation: Continuation): Continuation = + (continuation as? CoroutineImpl)?.facade ?: continuation + +@SinceKotlin("1.3") +internal fun interceptContinuationIfNeeded( + context: CoroutineContext, + continuation: Continuation +) = context[ContinuationInterceptor]?.interceptContinuation(continuation) ?: continuation diff --git a/libraries/stdlib/coroutines/src/kotlin/coroutines/ContinuationInterceptor.kt b/libraries/stdlib/coroutines/src/kotlin/coroutines/ContinuationInterceptor.kt new file mode 100644 index 00000000000..33d9bd5dfa5 --- /dev/null +++ b/libraries/stdlib/coroutines/src/kotlin/coroutines/ContinuationInterceptor.kt @@ -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 + +/** + * 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.3") +public interface ContinuationInterceptor : CoroutineContext.Element { + /** + * The key that defines *the* context interceptor. + */ + companion object Key : CoroutineContext.Key + + /** + * 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 interceptContinuation(continuation: Continuation): Continuation +} diff --git a/libraries/stdlib/coroutines/src/kotlin/coroutines/CoroutineContext.kt b/libraries/stdlib/coroutines/src/kotlin/coroutines/CoroutineContext.kt new file mode 100644 index 00000000000..f497249c9d4 --- /dev/null +++ b/libraries/stdlib/coroutines/src/kotlin/coroutines/CoroutineContext.kt @@ -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 + +/** + * 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.3") +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 get(key: Key): 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 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 get(key: Key): E? = + if (this.key === key) this as E else null + + public override fun 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 +} diff --git a/libraries/stdlib/coroutines/src/kotlin/coroutines/CoroutineContextImpl.kt b/libraries/stdlib/coroutines/src/kotlin/coroutines/CoroutineContextImpl.kt new file mode 100644 index 00000000000..4948034e6c2 --- /dev/null +++ b/libraries/stdlib/coroutines/src/kotlin/coroutines/CoroutineContextImpl.kt @@ -0,0 +1,89 @@ +/* + * 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 + +import kotlin.coroutines.CoroutineContext.* + +/** + * Base class for [CoroutineContext.Element] implementations. + */ +@SinceKotlin("1.3") +public abstract class AbstractCoroutineContextElement(public override val key: Key<*>) : Element + +/** + * An empty coroutine context. + */ +@SinceKotlin("1.3") +public object EmptyCoroutineContext : CoroutineContext { + public override fun get(key: Key): E? = null + public override fun 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 +@SinceKotlin("1.3") +internal class CombinedContext(val left: CoroutineContext, val element: Element) : CoroutineContext { + override fun get(key: Key): 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 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 + } + "]" +} diff --git a/libraries/stdlib/coroutines/src/kotlin/coroutines/Coroutines.kt b/libraries/stdlib/coroutines/src/kotlin/coroutines/Coroutines.kt new file mode 100644 index 00000000000..e488b90b451 --- /dev/null +++ b/libraries/stdlib/coroutines/src/kotlin/coroutines/Coroutines.kt @@ -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 + +/** + * Interface representing a continuation after a suspension point that returns value of type `T`. + */ +@SinceKotlin("1.3") +public interface Continuation { + /** + * 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.3") +@Target(AnnotationTarget.CLASS) +@Retention(AnnotationRetention.BINARY) +public annotation class RestrictsSuspension diff --git a/libraries/stdlib/coroutines/src/kotlin/coroutines/CoroutinesLibrary.kt b/libraries/stdlib/coroutines/src/kotlin/coroutines/CoroutinesLibrary.kt new file mode 100644 index 00000000000..68c8af7a677 --- /dev/null +++ b/libraries/stdlib/coroutines/src/kotlin/coroutines/CoroutinesLibrary.kt @@ -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:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") +@file:kotlin.jvm.JvmName("CoroutinesKt") +package kotlin.coroutines + +import kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED +import kotlin.coroutines.intrinsics.createCoroutineUnchecked +import kotlin.coroutines.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.3") +@Suppress("UNCHECKED_CAST") +public fun (suspend R.() -> T).startCoroutine( + receiver: R, + completion: Continuation +) { + 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.3") +@Suppress("UNCHECKED_CAST") +public fun (suspend () -> T).startCoroutine( + completion: Continuation +) { + 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.3") +@Suppress("UNCHECKED_CAST") +public fun (suspend R.() -> T).createCoroutine( + receiver: R, + completion: Continuation +): Continuation = 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.3") +@Suppress("UNCHECKED_CAST") +public fun (suspend () -> T).createCoroutine( + completion: Continuation +): Continuation = 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.3") +public suspend inline fun suspendCoroutine(crossinline block: (Continuation) -> Unit): T = + suspendCoroutineOrReturn { c: Continuation -> + 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.3") +@Suppress("WRONG_MODIFIER_TARGET") +@InlineOnly +public suspend inline val coroutineContext: CoroutineContext + get() { + throw NotImplementedError("Implemented as intrinsic") + } + +// INTERNAL DECLARATIONS + +@SinceKotlin("1.3") +@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).resume(result) + } + } catch (t: Throwable) { + completion.resumeWithException(t) + } +} diff --git a/libraries/stdlib/coroutines/src/kotlin/coroutines/intrinsics/Intrinsics.kt b/libraries/stdlib/coroutines/src/kotlin/coroutines/intrinsics/Intrinsics.kt new file mode 100644 index 00000000000..f4d0f38f17c --- /dev/null +++ b/libraries/stdlib/coroutines/src/kotlin/coroutines/intrinsics/Intrinsics.kt @@ -0,0 +1,62 @@ +/* + * 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") +@file:kotlin.jvm.JvmName("IntrinsicsKt") +@file:kotlin.jvm.JvmMultifileClass + +package kotlin.coroutines.intrinsics + +import kotlin.coroutines.* + +/** + * 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.3") +@kotlin.internal.InlineOnly +@Suppress("UNUSED_PARAMETER") +public suspend inline fun suspendCoroutineOrReturn(crossinline block: (Continuation) -> 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.3") +@kotlin.internal.InlineOnly +public suspend inline fun suspendCoroutineUninterceptedOrReturn(crossinline block: (Continuation) -> Any?): T = + throw NotImplementedError("Implementation of suspendCoroutineUninterceptedOrReturn is intrinsic") + +/** + * Intercept continuation with [ContinuationInterceptor]. + */ +@SinceKotlin("1.3") +@kotlin.internal.InlineOnly +public inline fun Continuation.intercepted(): Continuation = + 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.3") +public val COROUTINE_SUSPENDED: Any = Any() + diff --git a/libraries/stdlib/coroutines/src/kotlin/sequences/SequenceBuilder.kt b/libraries/stdlib/coroutines/src/kotlin/sequences/SequenceBuilder.kt new file mode 100644 index 00000000000..d55aefebbae --- /dev/null +++ b/libraries/stdlib/coroutines/src/kotlin/sequences/SequenceBuilder.kt @@ -0,0 +1,183 @@ +/* + * 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.sequences + +import kotlin.* +import kotlin.coroutines.* +import kotlin.coroutines.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.3") +public fun buildSequence(builderAction: suspend SequenceBuilder.() -> Unit): Sequence = 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.3") +public fun buildIterator(builderAction: suspend SequenceBuilder.() -> Unit): Iterator { + val iterator = SequenceBuilderIterator() + 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.3") +public abstract class SequenceBuilder 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) + + /** + * Yields a collections of values to the [Iterator] being built. + * + * @sample samples.collections.Sequences.Building.buildSequenceYieldAll + */ + public suspend fun yieldAll(elements: Iterable) { + 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) = 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 : SequenceBuilder(), Iterator, Continuation { + private var state = State_NotReady + private var nextValue: T? = null + private var nextIterator: Iterator? = null + var nextStep: Continuation? = 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) { + 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 +} diff --git a/libraries/stdlib/jvm/build.gradle b/libraries/stdlib/jvm/build.gradle index eb89c5ba509..404b830593e 100644 --- a/libraries/stdlib/jvm/build.gradle +++ b/libraries/stdlib/jvm/build.gradle @@ -34,6 +34,14 @@ sourceSets { srcDir "../experimental" } } + coroutines { + kotlin { + srcDir '../coroutines/jvm/src' + srcDir '../coroutines/common/src' + srcDir '../coroutines/src' + } + compileClasspath += sourceSets.main.output + } test { kotlin { srcDir 'test' @@ -80,6 +88,14 @@ task distJar(type: Jar) { // from sourceSets.java9.output } +task coroutinesJar(type: Jar) { + baseName = 'kotlin-stdlib-coroutines' + version = null + manifestAttributes(manifest, project, 'Main', true) + project.configure(manifest) { attributes 'Kotlin-Version': '1.3' } + from("${rootDir}/dist/builtins") + from sourceSets.coroutines.output +} sourcesJar { from "${rootDir}/core/builtins/native" @@ -121,6 +137,13 @@ dist { } } +task coroutinesDist(type: Copy) { + from coroutinesJar + into distDir +} + +dist.dependsOn coroutinesDist + task dexMethodCount(type: DexMethodCount) { from jar ownPackages = ['kotlin'] @@ -159,4 +182,18 @@ compileExperimentalKotlin { compileJava9Sources(project, 'kotlin.stdlib') +compileCoroutinesKotlin { + kotlinOptions { + languageVersion = "1.3" + apiVersion = "1.3" + freeCompilerArgs = [ + "-version", + "-Xallow-kotlin-package", + "-Xmultifile-parts-inherit", + "-Xdump-declarations-to=${buildDir}/stdlib-coroutines-declarations.json", + "-module-name", "kotlin-stdlib-coroutines" + ] + } +} + kotlin.experimental.coroutines 'enable'