Renaming after DM

This commit is contained in:
Roman Elizarov
2016-12-15 18:52:03 +03:00
committed by Stanislav Erokhin
parent 31081c6702
commit 7b079bd1f7
8 changed files with 273 additions and 459 deletions
@@ -1,108 +0,0 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package kotlin.coroutines
/**
* A strategy to intercept resumptions inside coroutine.
* Interceptor may shift resumption into another execution frame by scheduling asynchronous execution
* in this or another thread.
*/
@SinceKotlin("1.1")
public interface ResumeInterceptor {
/**
* Intercepts [Continuation.resume] invocation.
* This function must either return `false` or return `true` and invoke `continuation.resume(data)` asynchronously.
*/
public fun <P> interceptResume(data: P, continuation: Continuation<P>): Boolean = false
/**
* Intercepts [Continuation.resumeWithException] invocation.
* This function must either return `false` or return `true` and invoke `continuation.resumeWithException(exception)` asynchronously.
*/
public fun interceptResumeWithException(exception: Throwable, continuation: Continuation<*>): Boolean = false
}
/**
* Creates 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 result of the coroutine's execution is provided via invocation of [resultHandler].
*/
@SinceKotlin("1.1")
@Suppress("UNCHECKED_CAST")
public fun <R, T> (suspend R.() -> T).createCoroutine(
receiver: R,
resultHandler: Continuation<T>,
resumeInterceptor: ResumeInterceptor? = null
): Continuation<Unit> = (this as (R, Continuation<T>) -> Continuation<Unit>).invoke(receiver, withInterceptor(resultHandler, resumeInterceptor))
/**
* 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 result of the coroutine's execution is provided via invocation of [resultHandler].
*/
@SinceKotlin("1.1")
@Suppress("UNCHECKED_CAST")
public fun <R, T> (suspend R.() -> T).startCoroutine(
receiver: R,
resultHandler: Continuation<T>,
resumeInterceptor: ResumeInterceptor? = null
) {
createCoroutine(receiver, resultHandler, resumeInterceptor).resume(Unit)
}
/**
* Creates 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 result of the coroutine's execution is provided via invocation of [resultHandler].
* An optional [resumeInterceptor] may be specified to intercept resumes at suspension points inside the coroutine.
*/
@SinceKotlin("1.1")
@Suppress("UNCHECKED_CAST")
public fun <T> (suspend () -> T).createCoroutine(
resultHandler: Continuation<T>,
resumeInterceptor: ResumeInterceptor? = null
): Continuation<Unit> = (this as (Continuation<T>) -> Continuation<Unit>).invoke(withInterceptor(resultHandler, resumeInterceptor))
/**
* 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 result of the coroutine's execution is provided via invocation of [resultHandler].
* An optional [resumeInterceptor] may be specified to intercept resumes at suspension points inside the coroutine.
*/
@SinceKotlin("1.1")
@Suppress("UNCHECKED_CAST")
public fun <T> (suspend () -> T).startCoroutine(
resultHandler: Continuation<T>,
resumeInterceptor: ResumeInterceptor? = null
) {
createCoroutine(resultHandler, resumeInterceptor).resume(Unit)
}
// ------- internal stuff -------
internal interface InterceptableContinuation<P> : Continuation<P> {
val resumeInterceptor: ResumeInterceptor?
}
private fun <T> withInterceptor(resultHandler: Continuation<T>, resumeInterceptor: ResumeInterceptor?): Continuation<T> {
return if (resumeInterceptor == null) resultHandler else
object : InterceptableContinuation<T>, Continuation<T> by resultHandler {
override val resumeInterceptor: ResumeInterceptor? = resumeInterceptor
}
}
@@ -1,119 +0,0 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package kotlin.coroutines
import java.lang.IllegalStateException
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater
/**
* This function allows to obtain the current continuation instance inside suspend functions and suspend
* currently running coroutine.
* This function can be used in a tail-call position as the return value of another suspend function.
*
* 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. They should be called asynchronously either later in the same thread or
* from a different thread of execution.
* In practise, this restriction means that only _asynchronous_ promises and callbacks can be used to resume the continuation
* in this function.
* Repeated invocation of any resume function on continuation produces unspecified behavior.
* Use [runWithCurrentContinuation] as a safer way to obtain current continuation instance.
*/
//@SinceKotlin("1.1")
//public inline suspend fun <T> suspendWithCurrentContinuation(crossinline body: (Continuation<T>) -> Unit): T =
// maySuspendWithCurrentContinuation<T> { c: Continuation<T> ->
// body(c)
// SUSPENDED
// }
/**
* This function allows to safely obtain the current continuation instance inside suspend functions and suspend
* currently running coroutine.
* This function can be used in a tail-call position as the return value of another suspend function.
*
* In this function both [Continuation.resume] and [Continuation.resumeWithException] can be used either synchronously in
* the same stackframe 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 inline suspend fun <T> runWithCurrentContinuation(crossinline body: (Continuation<T>) -> Unit): T =
suspendWithCurrentContinuation { c: Continuation<T> ->
val safe = SafeContinuation(c)
body(safe)
safe.getResult()
}
private val UNDECIDED: Any? = Any()
private val RESUMED: Any? = Any()
private class Fail(val exception: Throwable)
@PublishedApi
internal class SafeContinuation<T> @PublishedApi internal constructor(private val delegate: Continuation<T>) : Continuation<T> {
@Volatile
private var result: Any? = UNDECIDED
companion object {
@Suppress("UNCHECKED_CAST")
@JvmStatic
private val RESULT_UPDATER = AtomicReferenceFieldUpdater.newUpdater<SafeContinuation<*>, Any?>(
SafeContinuation::class.java, Any::class.java as Class<Any?>, "result")
}
private fun cas(expect: Any?, update: Any?): Boolean =
RESULT_UPDATER.compareAndSet(this, expect, update)
override fun resume(data: T) {
while (true) { // lock-free loop
val result = this.result // atomic read
when (result) {
UNDECIDED -> if (cas(UNDECIDED, data)) return
SUSPENDED -> if (cas(SUSPENDED, RESUMED)) {
delegate.resume(data)
return
}
else -> throw IllegalStateException("Already resumed")
}
}
}
override fun resumeWithException(exception: Throwable) {
while (true) { // lock-free loop
val result = this.result // atomic read
when (result) {
UNDECIDED -> if (cas(UNDECIDED, Fail(exception))) return
SUSPENDED -> if (cas(SUSPENDED, RESUMED)) {
delegate.resumeWithException(exception)
return
}
else -> throw IllegalStateException("Already resumed")
}
}
}
@PublishedApi
internal fun getResult(): Any? {
val result = this.result // atomic read
if (result == UNDECIDED && cas(UNDECIDED, SUSPENDED)) return SUSPENDED
when (result) {
RESUMED -> return SUSPENDED // already called continuation, indicate SUSPENDED upstream
is Fail -> throw result.exception
else -> return result // either SUSPENDED or data
}
}
}
@@ -1,33 +0,0 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package kotlin.coroutines
/**
* Converts a callable references to `suspend` lambda into a tail-callable suspend function.
* It can be used to define arbitrary suspension function without tail-call restrictions, for example
*
* ```
* suspend fun awaitTwice(...): R = suspendable<R> {
* await(...) // calls one suspend function
* await(...) // calls another suspend function
* }
*/
@SinceKotlin("1.1")
suspend fun <T> suspendable(/*suspend*/ lambda: () -> T): T = suspendWithCurrentContinuation<T> { c ->
@Suppress("UNCHECKED_CAST")
(lambda as Function1<Continuation<T>, Any?>).invoke(c)
}
@@ -21,34 +21,33 @@ import kotlin.coroutines.*
private const val INTERCEPT_BIT_SET = 1 shl 31
private const val INTERCEPT_BIT_CLEAR = INTERCEPT_BIT_SET.inv()
@SinceKotlin("1.1")
abstract class CoroutineImpl : RestrictedCoroutineImpl, InterceptableContinuation<Any?> {
private val _resumeInterceptor: ResumeInterceptor?
abstract class CoroutineImpl : RestrictedCoroutineImpl, DispatchedContinuation<Any?> {
private val _dispatcher: ContinuationDispatcher?
override val resumeInterceptor: ResumeInterceptor?
get() = _resumeInterceptor
override val dispatcher: ContinuationDispatcher?
get() = _dispatcher
// this constructor is used to create a continuation instance for coroutine
constructor(arity: Int, resultContinuation: Continuation<Any?>?) : super(arity, resultContinuation) {
_resumeInterceptor = (resultContinuation as? InterceptableContinuation<*>)?.resumeInterceptor
constructor(arity: Int, completion: Continuation<Any?>?) : super(arity, completion) {
_dispatcher = (completion as? DispatchedContinuation<*>)?.dispatcher
}
override fun resume(data: Any?) {
if (_resumeInterceptor != null) {
override fun resume(value: Any?) {
if (_dispatcher != null) {
if (label and INTERCEPT_BIT_SET == 0) {
label = label or INTERCEPT_BIT_SET
if (_resumeInterceptor.interceptResume(data, this)) return
if (_dispatcher.dispatchResume(value, this)) return
}
label = label and INTERCEPT_BIT_CLEAR
}
super.resume(data)
super.resume(value)
}
override fun resumeWithException(exception: Throwable) {
if (_resumeInterceptor != null) {
if (_dispatcher != null) {
if (label and INTERCEPT_BIT_SET == 0) {
label = label or INTERCEPT_BIT_SET
if (_resumeInterceptor.interceptResumeWithException(exception, this)) return
if (_dispatcher.dispatchResumeWithException(exception, this)) return
}
label = label and INTERCEPT_BIT_CLEAR
}
@@ -56,10 +55,9 @@ abstract class CoroutineImpl : RestrictedCoroutineImpl, InterceptableContinuatio
}
}
@SinceKotlin("1.1")
abstract class RestrictedCoroutineImpl : Lambda, Continuation<Any?> {
@JvmField
protected var resultContinuation: Continuation<Any?>?
protected var completion: 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
@@ -67,102 +65,34 @@ abstract class RestrictedCoroutineImpl : Lambda, Continuation<Any?> {
protected var label: Int
// this constructor is used to create a continuation instance for coroutine
constructor(arity: Int, resultContinuation: Continuation<Any?>?) : super(arity) {
this.resultContinuation = resultContinuation
label = if (resultContinuation != null) 0 else -1
constructor(arity: Int, completion: Continuation<Any?>?) : super(arity) {
this.completion = completion
label = if (completion != null) 0 else -1
}
override fun resume(data: Any?) {
override fun resume(value: Any?) {
try {
val result = doResume(data, null)
if (result != SUSPENDED)
resultContinuation!!.resume(result)
val result = doResume(value, null)
if (result != CoroutineIntrinsics.SUSPENDED)
completion!!.resume(result)
} catch (e: Throwable) {
resultContinuation!!.resumeWithException(e)
completion!!.resumeWithException(e)
}
}
override fun resumeWithException(exception: Throwable) {
try {
val result = doResume(null, exception)
if (result != SUSPENDED)
resultContinuation!!.resume(result)
if (result != CoroutineIntrinsics.SUSPENDED)
completion!!.resume(result)
} catch (e: Throwable) {
resultContinuation!!.resumeWithException(e)
completion!!.resumeWithException(e)
}
}
protected abstract fun doResume(data: Any?, exception: Throwable?): Any?
}
/*
===========================================================================================================================
Showcase of coroutine compilation strategy.
Given this following "sample" suspend function code:
---------------------------------------------------------------------------------------------
@RestrictSuspension
class Receiver {
suspend fun yield(): SomeResult
internal interface DispatchedContinuation<in T> : Continuation<T> {
val dispatcher: ContinuationDispatcher?
}
suspend fun Receiver.sample(): String {
doSomethingBefore()
val yieldResult = yield() // suspension point
doSomethingAfter(yieldResult)
return "Done"
}
---------------------------------------------------------------------------------------------
The compiler emits the class with the following logic:
---------------------------------------------------------------------------------------------
class XXXCoroutine : RestrictedCoroutineImpl, Function2<Receiver, Continuation<*>, Any?> {
// ^^^^^^^^^^^^^^^^^^^^^^^
// Replace with CoroutineImpl if the coroutine is non-restricted
val receiver: Receiver?
// ^^^^^^^^^^^^^^^^^^^
// receiver is just a part of the captured scope and is only declared here is coroutine has it
// this constructor is used to create initial "factory" lambda object
constructor() : super(2, null) {
this.receiver = null
}
// this constructor is used to create a continuation instance for coroutine
constructor(receiver: Receiver, resultContinuation: Continuation<Any?>) : super(2, resultContinuation) {
// ^^^^^^^^^^^^^^^^^^^^
// no receiver parameter when compiling coroutine that does not have one
this.receiver = receiver
}
override fun doCreate(receiver: Any, resultContinuation: Continuation<*>): Continuation<Unit> {
return XXXCoroutine(receiver as Receiver, resultContinuation) // create the actual instance
// ^^^^^^^^^^^^^^^^^^^^
// ignore receiver parameter when compiling coroutine that does not have one,
// check that it is not null as check its proper type for coroutine that has a receiver
}
override fun doResume(data: Any?, exception: Throwable?): Any? {
var suspensionResult = data
switch (label) {
default:
throw IllegalStateException()
case 0:
doSomethingBefore()
label = 1 // set next label before calling suspend function
suspensionResult = receiver.yield(this)
if (suspensionResult == SUSPENDED) return SUSPENDED
// falls through with yeild result if it is available
case 1:
doSomethingAfter(supensionResult)
label = -1 // execution is over
return "Done" // we have result of execution
}
}
}
---------------------------------------------------------------------------------------------
*/