Problem with inline functions with coroutines and exposed


#1

Hi,

The following code does not compile. It is based on the pipeline example. The problem is in the produceNumbers function. The send call of the producer is embedded in a lambda. This works if the lambda is inline. (see someInlineLamda). The transaction call is not inline and gives a compile error (Error:(24, 13) Kotlin: Suspension functions can be called only within coroutine body).

If I move the transaction code to another inline lambda (see someInlineTransactionLambda) the lambda needs to be crossinline due to non-local returns but the error remains.

Any ideas on how to resolve this?

import kotlinx.coroutines.experimental.*
import kotlinx.coroutines.experimental.channels.*
import org.jetbrains.exposed.sql.transactions.transaction

inline fun <E> someInlineLambda(x: Int, function: (Int) -> E): Unit {
    for (i in 0..1) 
        function(x)
}

inline fun <E> someInlineTransactionLambda(x: Int, crossinline function: (Int) -> E): Unit {
    transaction {
        for (i in 0..1) {
            function(x)
        }
    }
}

fun produceNumbers() = produce<Int>(CommonPool) {
    var x = 1
    while (true) {
        someInlineLambda(x) {
            send(x++)     // OK - inline lambda
        }
        transaction {    // start transaction in exposed land
            send(x++)    // Error:(24, 13) Kotlin: Suspension functions can be called only within coroutine body
        }
        someInlineTransactionLamda {    // start transaction in exposed land, wrapped in inline lambda
            send(x++)    // did not work :(
        }
    }
}

fun square(numbers: ReceiveChannel<Int>) = produce<Int>(CommonPool) {
    for (x in numbers) send(x * x)
}

fun main(args: Array<String>) = runBlocking<Unit> {
    val numbers = produceNumbers() // produces integers from 1 and on
    val squares = square(numbers) // squares integers
    for (i in 1..5) println(squares.receive()) // print first five
    println("Done!") // we are done
    squares.cancel() // need to cancel these coroutines in a larger app
    numbers.cancel()
}

#2

To make non-inline function work with suspensions you’ll have to explicitly mark the functions and their lambda arguments with suspend keyword.


#3

Ok got it. Thanks.