I want to continue a suspend function in a dynamic proxy in the same coroutine.
Please have a look at the following code:
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))
}
}
It works fine. It runs the delay function in a new coroutine.
However, that’s not what I want.
I want to run the InvocationHandler in the same coroutine as the one that was started with runBlocking.
Something like:
val IH = InvocationHandler { _, _, _ ->
delay(5_000)
3
}
This obviously won’t compile because delay is a suspend function that must be run in a coroutine.
So the question is: How could I write the InvocationHandler for my intended behavior?
Any help would be very much appreciated.
I also searched here and there to solve a similar problem, but I didn’t get a satisfactory answer. So, in the end, I found the answer myself.
This is my answer.
interface Adder {
suspend fun add(a: Int, b: Int): Int
}
@Suppress("FunctionName", "NAME_SHADOWING", "UNCHECKED_CAST")
fun SuspendInvocationHandler(block: suspend (proxy: Any, method: Method, args: Array<*>?) -> Any?) =
InvocationHandler { proxy, method, args ->
val cont = args?.lastOrNull() as? Continuation<*>
if (cont == null) {
val args = args.orEmpty()
runBlocking {
block(proxy, method, args)
}
} else {
val args = args.dropLast(1).toTypedArray()
val suspendInvoker = block as (Any, Method, Array<*>?, Continuation<*>) -> Any?
suspendInvoker(proxy, method, args, cont)
}
}
fun main() {
val IH = SuspendInvocationHandler { proxy, method, args ->
delay(5_000)
3
}
val adder = Proxy.newProxyInstance(
Adder::class.java.classLoader, arrayOf(Adder::class.java), IH
) as Adder
runBlocking {
println(adder.add(1, 2))
}
}