JS: implement new coroutine convention

This commit is contained in:
Alexey Andreev
2016-12-14 19:38:00 +03:00
committed by Stanislav Erokhin
parent 9fd1ac72a9
commit e2d969d8b0
20 changed files with 353 additions and 315 deletions
+162
View File
@@ -0,0 +1,162 @@
/*
* 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")
public fun <R, T> (suspend R.() -> T).createCoroutine(
receiver: R,
resultHandler: Continuation<T>,
resumeInterceptor: ResumeInterceptor? = null
): Continuation<Unit> = this.asDynamic().call(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")
public fun <T> (suspend () -> T).createCoroutine(
resultHandler: Continuation<T>,
resumeInterceptor: ResumeInterceptor? = null
): Continuation<Unit> = this.asDynamic()(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 -------
private fun <T> withInterceptor(resultHandler: Continuation<T>, resumeInterceptor: ResumeInterceptor?): Continuation<T> {
val finalResumeInterceptor = resumeInterceptor ?: object : ResumeInterceptor {
override fun <P> interceptResume(data: P, continuation: Continuation<P>) = false
override fun interceptResumeWithException(exception: Throwable, continuation: Continuation<*>) = false
}
return object : Continuation<T> by resultHandler, ResumeInterceptor by finalResumeInterceptor {}
}
@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
private val resumeInterceptor = resultContinuation as? ResumeInterceptor
override fun resume(data: Any?) {
if (resumeInterceptor != null && (state and INTERCEPTING) == 0) {
state = state or INTERCEPTING
if (resumeInterceptor.interceptResume(data, this)) {
state = state and INTERCEPTING.inv()
return
}
}
state = state and INTERCEPTING.inv()
this.result = data
try {
val result = doResume()
if (result != SUSPENDED) {
resultContinuation.resume(result)
}
}
catch (e: Throwable) {
resultContinuation.resumeWithException(e)
}
}
override fun resumeWithException(exception: Throwable) {
if (resumeInterceptor != null && (state and INTERCEPTING) == 0) {
state = state or INTERCEPTING
if (resumeInterceptor.interceptResumeWithException(exception, this)) {
state = state and INTERCEPTING.inv()
return
}
}
state = exceptionState
this.exception = exception
try {
val result = doResume()
if (result != SUSPENDED) {
resultContinuation.resume(result)
}
}
catch (e: Throwable) {
resultContinuation.resumeWithException(e)
}
}
protected abstract fun doResume(): Any?
}
private const val INTERCEPTING = 1 shl 31