Как продолжить функцию приостановки в динамическом прокси в той же сопрограмме?
Я хочу продолжить функцию приостановки в динамическом прокси в той же сопрограмме. Пожалуйста, посмотрите на следующий код:
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
}