Как продолжить функцию приостановки в динамическом прокси в той же сопрограмме? - PullRequest
0 голосов
/ 31 января 2019

Я хочу продолжить функцию приостановки в динамическом прокси в той же сопрограмме.Пожалуйста, посмотрите на следующий код:

interface Adder {
    suspend fun add(a: Int, b: Int): Int
}

val IH = InvocationHandler { _, method, args ->
    val continuation = args.last() as Continuation<*>
    val realArgs = args.take(args.size - 1)
    println("${method.name}$realArgs")
    GlobalScope.launch {
        delay(5_000)
        @Suppress("UNCHECKED_CAST") (continuation as Continuation<Int>).resume(3)
    }
    COROUTINE_SUSPENDED
}

fun main() {
    val adder = Proxy.newProxyInstance(
        Adder::class.java.classLoader, arrayOf(Adder::class.java), IH
    ) as Adder
    runBlocking {
        println(adder.add(1, 2))
    }
}

Работает нормально.Он запускает функцию задержки в новой сопрограмме.Однако это не то, что я хочу.

Я хочу запустить InvocationHandler в той же сопрограмме, что и та, которая была запущена с помощью runBlocking.Что-то вроде:

val IH = InvocationHandler { _, _, _ ->
    delay(5_000)
    3
}

Очевидно, что это не скомпилируется, потому что delay - это функция приостановки, которая должна выполняться в сопрограмме.Итак, вопрос: как я мог написать InvocationHandler для моего предполагаемого поведения?Буду очень признателен за любую помощь.

Я хотел бы использовать этот код в моей среде RPC.Мой реальный код заменит задержку вызова неблокирующими вызовами сокета Ktor для сериализации данных по проводам.Вы можете найти пример кода по адресу: https://raw.githubusercontent.com/softappeal/yass/master/kotlin/yass/test/ch/softappeal/yass/remote/SuspendProxy.kt

Ответы [ 2 ]

0 голосов
/ 02 февраля 2019

Я нашел решение для моей проблемы:

package ch.softappeal.yass

import kotlinx.coroutines.*
import java.lang.reflect.*
import kotlin.coroutines.*
import kotlin.test.*

typealias SuspendInvoker = suspend (method: Method, arguments: List<Any?>) -> Any?

private interface SuspendFunction {
    suspend fun invoke(): Any?
}

private val SuspendRemover = SuspendFunction::class.java.methods[0]

@Suppress("UNCHECKED_CAST")
fun <C : Any> proxy(contract: Class<C>, invoker: SuspendInvoker): C =
    Proxy.newProxyInstance(contract.classLoader, arrayOf(contract)) { _, method, arguments ->
        val continuation = arguments.last() as Continuation<*>
        val argumentsWithoutContinuation = arguments.take(arguments.size - 1)
        SuspendRemover.invoke(object : SuspendFunction {
            override suspend fun invoke() = invoker(method, argumentsWithoutContinuation)
        }, continuation)
    } as C

interface Adder {
    suspend fun add(a: Int, b: Int): Int
}

class SuspendProxyTest {
    @Test
    fun test() {
        val adder = proxy(Adder::class.java) { method, arguments ->
            println("${method.name}$arguments")
            delay(100)
            3
        }
        runBlocking { assertEquals(3, adder.add(1, 2)) }
    }
}

Есть ли какие-либо комментарии?
Это хорошее / проблемное решение?
Может ли / должно быть "удаление функции приостановки" бытьдобавлен в библиотеку kotlin.coroutines?

0 голосов
/ 01 февраля 2019

использовать runBlocking внутри InvocationHandler:

val IH = InvocationHandler { _, _, _ ->

    runBlocking{
        delay(5_000)// now you can use suspend functions here
    }

    3
}
...