# How to continue a suspend function in a dynamic proxy in the same coroutine?

**URL:** <https://discuss.kotlinlang.org/t/how-to-continue-a-suspend-function-in-a-dynamic-proxy-in-the-same-coroutine/11391>\
**Category:** Support\
**Created:** [January 30, 2019, 7:35pm UTC](https://discuss.kotlinlang.org/t/how-to-continue-a-suspend-function-in-a-dynamic-proxy-in-the-same-coroutine/11391 "2019-01-30T19:35:50Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![Angelo\_Salvade](https://avatars.discourse-cdn.com/v4/letter/a/b2d939/32.png) [@Angelo\_Salvade](https://discuss.kotlinlang.org/u/Angelo_Salvade)\
**Post date:** [January 30, 2019, 7:35pm UTC](https://discuss.kotlinlang.org/t/how-to-continue-a-suspend-function-in-a-dynamic-proxy-in-the-same-coroutine/11391/1 "2019-01-30T19:35:50Z")

</div>

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’d like to use this code in my RPC framework.  
My real code would replace the delay call with non-blocking Ktor socket calls for serializing the data over the wire.  
You can find the code example at: [https://raw.githubusercontent.com/softappeal/yass/master/kotlin/yass/test/ch/softappeal/yass/remote/SuspendProxy.kt](https://raw.githubusercontent.com/softappeal/yass/master/kotlin/yass/test/ch/softappeal/yass/remote/SuspendProxy.kt)

---

<div class="post-metadata">

**Author:** ![Angelo\_Salvade](https://avatars.discourse-cdn.com/v4/letter/a/b2d939/32.png) [@Angelo\_Salvade](https://discuss.kotlinlang.org/u/Angelo_Salvade)\
**Post date:** [February 2, 2019, 2:30pm UTC](https://discuss.kotlinlang.org/t/how-to-continue-a-suspend-function-in-a-dynamic-proxy-in-the-same-coroutine/11391/2 "2019-02-02T14:30:19Z")

</div>

I’ve found a solution for my problem:

```
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)) }
    }
}

```

Any comments?  
Is this a good/problematic solution?  
Could/should the “removing of suspend functionality” be added to the `kotlin.coroutines` library?

---

<div class="post-metadata">

**Author:** ![nanodeath](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.kotlinlang.org/nanodeath/32/1803_2.png) [@nanodeath](https://discuss.kotlinlang.org/u/nanodeath)\
**Post date:** [December 4, 2019, 2:04am UTC](https://discuss.kotlinlang.org/t/how-to-continue-a-suspend-function-in-a-dynamic-proxy-in-the-same-coroutine/11391/3 "2019-12-04T02:04:21Z")

</div>

This was just what I needed, thanks!

It does seem like there should be a better way, though.

---

<div class="post-metadata">

**Author:** ![Angelo\_Salvade](https://avatars.discourse-cdn.com/v4/letter/a/b2d939/32.png) [@Angelo\_Salvade](https://discuss.kotlinlang.org/u/Angelo_Salvade)\
**Post date:** [December 14, 2019, 11:11am UTC](https://discuss.kotlinlang.org/t/how-to-continue-a-suspend-function-in-a-dynamic-proxy-in-the-same-coroutine/11391/4 "2019-12-14T11:11:54Z")

</div>

@nanodeath Thanks.

There is a more mature version at [https://github.com/softappeal/yass2/blob/v0.4.0/src/jvmMain/kotlin/ch/softappeal/yass2/InterceptorReflection.kt](https://github.com/softappeal/yass2/blob/v0.4.0/src/jvmMain/kotlin/ch/softappeal/yass2/InterceptorReflection.kt)

If you know of a better solution, please let me know.

---

<div class="post-metadata">

**Author:** ![wildcaravan](https://avatars.discourse-cdn.com/v4/letter/w/e9bcb4/32.png) [@wildcaravan](https://discuss.kotlinlang.org/u/wildcaravan)\
**Post date:** [April 6, 2021, 3:00pm UTC](https://discuss.kotlinlang.org/t/how-to-continue-a-suspend-function-in-a-dynamic-proxy-in-the-same-coroutine/11391/7 "2021-04-06T15:00:18Z")

</div>

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.

```kotlin
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))
    }
}

```

---

<div class="post-metadata">

**Author:** ![wrongwrong](https://avatars.discourse-cdn.com/v4/letter/w/ac8455/32.png) [@wrongwrong](https://discuss.kotlinlang.org/u/wrongwrong)\
**Post date:** [December 9, 2022, 6:31pm UTC](https://discuss.kotlinlang.org/t/how-to-continue-a-suspend-function-in-a-dynamic-proxy-in-the-same-coroutine/11391/8 "2022-12-09T18:31:03Z")

</div>

This URL seemed to be a broken.  
I share the new URL.

> <https://github.com/softappeal/yass2/blob/00de0edd7786f474226be524e66933e837af29be/yass2-reflect/src/jvmMain/kotlin/reflect/ReflectionInterceptor.kt#L24-L29>

Also, I personally felt it would be more type safe to write the following

```kotlin
fun <T> invokeSuspendFunction(
    continuation: Continuation<*>,
    block: suspend () -> T
): T = @Suppress("UNCHECKED_CAST") (block as (Continuation<*>) -> T)(continuation)

override fun invoke(proxy: Any, method: Method, args: Array<out Any>?): Any? {
    if (/* some condition */) {
        val continuation = args!!.last() as Continuation<*>
        return invokeSuspendFunction(continuation) { /* some suspend call */ }
    }
    TODO("Not yet implemented")
}

```
