Как продолжить функцию приостановки в динамическом прокси в той же сопрограмме?

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

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 ответа

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

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 библиотека?

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

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

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

    3
}
Другие вопросы по тегам