Implement basic support for coroutines in JVM backend

This commit is contained in:
Denis Zharkov
2016-05-25 14:46:38 +03:00
parent 077fc528d1
commit 75e112e752
30 changed files with 1151 additions and 45 deletions
+54
View File
@@ -0,0 +1,54 @@
// WITH_RUNTIME
// FULL_JDK
fun box(): String {
val x = gen().joinToString()
if (x != "1, 2") return "fail1: $x"
val y = gen().joinToString()
if (y != "-1") return "fail2: $y"
return "OK"
}
var was = false
fun gen() = generate<Int> {
if (was) {
yield(-1)
return@generate
}
for (i in 1..2) {
yield(i)
}
was = true
}
// LIBRARY CODE
fun <T> generate(coroutine c: GeneratorController<T>.() -> Continuation<Unit>): Sequence<T> = object : Sequence<T> {
override fun iterator(): Iterator<T> {
val iterator = GeneratorController<T>()
iterator.setNextStep(c(iterator))
return iterator
}
}
class GeneratorController<T>() : AbstractIterator<T>() {
private lateinit var nextStep: Continuation<Unit>
override fun computeNext() {
nextStep.resume(Unit)
}
fun setNextStep(step: Continuation<Unit>) {
this.nextStep = step
}
suspend fun yield(value: T, c: Continuation<Unit>) {
setNext(value)
setNextStep(c)
}
fun handleResult(result: Unit, c: Continuation<Nothing>) {
done()
}
}
@@ -0,0 +1,35 @@
class Controller {
var i = 0
suspend fun suspendHere(x: Continuation<String>) {
x.resume((i++).toString())
}
}
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
c(Controller()).resume(Unit)
}
fun box(): String {
var result = ""
builder {
result += "-"
result += suspendHere()
if (result == "-0") {
builder {
result += "+"
result += suspendHere()
result += suspendHere()
result += "#"
}
result += suspendHere()
result += "&"
}
}
if (result != "-0+01#1&") return "fail: $result"
return "OK"
}
@@ -0,0 +1,30 @@
class Controller {
var res = 0
suspend fun suspendHere(x: Continuation<String>) {
x.resume("OK")
}
operator fun handleResult(x: Int, y: Continuation<Nothing>) {
res = x
}
}
fun builder(coroutine c: Controller.() -> Continuation<Unit>): Int {
val controller = Controller()
c(controller).resume(Unit)
return controller.res
}
fun box(): String {
var result = ""
val handledResult = builder {
result = suspendHere()
return@builder 56
}
if (handledResult != 56) return "fail 1: $handledResult"
return result
}
+19
View File
@@ -0,0 +1,19 @@
class Controller {
suspend fun suspendHere(x: Continuation<String>) {
x.resume("OK")
}
}
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
c(Controller()).resume(Unit)
}
fun box(): String {
var result = ""
builder {
result = suspendHere()
}
return result
}
@@ -0,0 +1,24 @@
class Controller {
suspend fun suspendHere(x: Continuation<String>) {
x.resumeWithException(RuntimeException("OK"))
}
}
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
c(Controller()).resume(Unit)
}
fun box(): String {
var result = ""
builder {
try {
suspendHere()
result = "fail"
} catch (e: RuntimeException) {
result = "OK"
}
}
return result
}
@@ -0,0 +1,30 @@
class Controller {
var res = 0
suspend fun suspendHere(x: Continuation<String>) {
x.resume("OK")
}
operator fun handleResult(x: Int, y: Continuation<Nothing>) {
res = x
}
}
fun builder(coroutine c: Controller.() -> Continuation<Unit>): Int {
val controller = Controller()
c(controller).resume(Unit)
return controller.res
}
fun box(): String {
var result = ""
val handledResult = builder {
result = suspendHere()
56
}
if (handledResult != 56) return "fail 1: $handledResult"
return result
}
@@ -0,0 +1,34 @@
class Controller {
var i = 0
suspend fun suspendHere(x: Continuation<Int>) {
x.resume(i++)
}
suspend fun suspendThere(x: Continuation<String>) {
x.resume("?")
}
}
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
c(Controller()).resume(Unit)
}
fun box(): String {
var result = ""
builder {
result += "-"
for (i in 0..5) {
if (i % 2 == 0) {
result += suspendHere().toString()
}
else if (i == 3) {
result += suspendThere()
}
}
result += "+"
}
if (result != "-01?2+") return "fail: $result"
return "OK"
}
+81
View File
@@ -0,0 +1,81 @@
// WITH_RUNTIME
// FULL_JDK
import java.util.concurrent.CompletableFuture
fun foo(): CompletableFuture<String> = CompletableFuture.supplyAsync { "foo" }
fun bar(v: String): CompletableFuture<String> = CompletableFuture.supplyAsync { "bar with $v" }
fun exception(v: String): CompletableFuture<String> = CompletableFuture.supplyAsync { throw RuntimeException(v) }
fun foobar(x: String, y: String) = x + y
fun box(): String {
var result = ""
fun log(x: String) {
synchronized(result) {
if (result.isNotEmpty()) result += "\n"
result += x
}
}
val future = async<String> {
log("start")
val x = await(foo())
log("got '$x'")
val y = foobar("123 ", await(bar(x)))
log("got '$y' after '$x'")
y
}
future.whenComplete { value, t ->
log("completed with '$value'")
}
future.join()
java.lang.Thread.sleep(1000)
val readResult = synchronized(result) {
result
}
val expectedResult =
"""
|start
|got 'foo'
|got '123 bar with foo' after 'foo'
|completed with '123 bar with foo'""".trimMargin().trim('\n', ' ')
if (expectedResult != readResult) return readResult
return "OK"
}
// LIBRARY CODE
fun <T> async(coroutine c: FutureController<T>.() -> Continuation<Unit>): CompletableFuture<T> {
val controller = FutureController<T>()
c(controller).resume(Unit)
return controller.future
}
class FutureController<T> {
val future = CompletableFuture<T>()
suspend fun <V> await(f: CompletableFuture<V>, machine: Continuation<V>) {
f.whenComplete { value, throwable ->
if (throwable == null)
machine.resume(value)
else
machine.resumeWithException(throwable)
}
}
fun handleResult(value: T, c: Continuation<Nothing>) {
future.complete(value)
}
fun handleException(t: Throwable, c: Continuation<Nothing>) {
future.completeExceptionally(t)
}
}
+61
View File
@@ -0,0 +1,61 @@
// WITH_RUNTIME
// FULL_JDK
import java.util.concurrent.CompletableFuture
fun exception(v: String): CompletableFuture<String> = CompletableFuture.supplyAsync { throw RuntimeException(v) }
fun foobar(x: String, y: String) = x + y
fun box(): String {
var result = ""
val future = async<String>() {
try {
await(exception("OK"))
} catch (e: Exception) {
result = e.cause?.message!!
}
"56"
}
future.join()
if (future.get() != "56") return "fail: ${future.get()}"
java.lang.Thread.sleep(1000)
return result
}
fun <T> async(coroutine c: FutureController<T>.() -> Continuation<Unit>): CompletableFuture<T> {
val controller = FutureController<T>()
c(controller).resume(Unit)
return controller.future
}
class FutureController<T> {
val future = CompletableFuture<T>()
suspend fun <V> await(f: CompletableFuture<V>, machine: Continuation<V>) {
f.whenComplete { value, throwable ->
try {
if (throwable == null)
machine.resume(value)
else
machine.resumeWithException(throwable)
} catch (e: Exception) {
future.completeExceptionally(e)
}
}
}
fun handleResult(value: T, c: Continuation<Nothing>) {
future.complete(value)
}
fun handleException(t: Throwable, c: Continuation<Nothing>) {
future.completeExceptionally(t)
}
}