Vertx plus Kotlin coroutines hangs forever


#1

Hi everyone,

(I already tried to get help from here but nobody seems able to solve it)

I am rewriting some Java Vertx asynch code using Kotlin coroutines for learning purposes. However, when I try to test a simple HTTP call, the coroutine based test hangs forever and I really don’t understand where is the issue. Here a reproducer:

@RunWith(VertxUnitRunner::class)
class HelloWorldTest {

    private val vertx: Vertx = Vertx.vertx()

    @Before
    fun setUp(context: TestContext) {
        // HelloWorldVerticle is a simple http server that replies "Hello, World!" to whatever call
        vertx.deployVerticle(HelloWorldVerticle::class.java!!.getName(), context.asyncAssertSuccess())
    }

    // ORIGINAL ASYNC TEST HERE. IT WORKS AS EXPECTED
    @Test
    fun testAsync(context: TestContext) {
        val atc = context.async()
        vertx.createHttpClient().getNow(8080, "localhost", "/") { response ->
            response.handler { body ->
                context.assertTrue(body.toString().equals("Hello, World!"))
                atc.complete()
            }
        }
    }

    // First attempt, it hangs forever, the response is never called
    @Test
    fun testSync1(context: TestContext) = runBlocking<Unit> {
        val atc = context.async()
        val body = await<HttpClientResponse> {
            vertx.createHttpClient().getNow(8080, "localhost", "/", { response -> response.handler {it}} )
        }
        context.assertTrue(body.toString().equals("Hello, World!"))
        atc.complete()
    }

    // Second attempt, it hangs forever, the response is never called
    @Test
    fun testSync2(context: TestContext) = runBlocking<Unit> {
        val atc = context.async()
        val response = await<HttpClientResponse> {
                vertx.createHttpClient().getNow(8080, "localhost", "/", it )
        }
        response.handler { body ->
            context.assertTrue(body.toString().equals("Hello, World!"))
            atc.complete()
        }
    }

    suspend fun <T> await(callback: (Handler<T>) -> Unit) =
            suspendCoroutine<T> { cont ->
                callback(Handler { result: T ->
                    cont.resume(result)
                })
            }
}

this issue is making me crazy :sweat:


#2

runBlocking use the test thread, you should use the Vert.x event loop and provide the right context to runBlocking.

runBlocking(context =  vertx.asCoroutineDispatcher())

I hope this help:

However the issue may be caused by handler registration after the notification, so notification is lost (forever).

Are you check the handler invocation?


#3

Hi @fvasco

thanks for your help! Indeed executing the test in a vertx specific coroutine dispatcher solved the issue.
Nevertheless, now I found another strange behavior, in fact, if I pass the “it” variable as last parameter of the getNow() method it works correctly, but with the curly brackets notation it still hangs:

@Test
fun testSync2(context: TestContext) = runVertxCoroutine {
    val atc = context.async()
    val response = await<HttpClientResponse> {
            vertx.createHttpClient().getNow(8080, "localhost", "/", it ) // This one works fine
        //    vertx.createHttpClient().getNow(8080, "localhost", "/") {it} // THIS ONE HANGS
    }
    response.handler { body ->
        context.assertTrue(body.toString().equals("Hello, World!"))
        atc.complete()
    }
}

shouldn’t the two notation be equivalent?


#4

No, you have wrote something like:

vertx.createHttpClient().getNow(8080, "localhost", "/") Handler{ return it } // THIS ONE HANGS

Your lamba is a new Handler, you should invoke it and return Unit

So

list.map(it)

is different to

list.map {it}