Creating `Uni` from a coroutine / suspending function - kotlin

We are using quarkus to process messages this run on a regular function
in that we have to call a suspend function
basically
fun process (msg:Message):Message{
val resFrom:Data = runBlocking{fetchDataFromDB(msg.key)}
val processedMsg = processRoutingKey(msg,resFrom)
return processedMsg
}
We would like to get the data as a Uni (https://smallrye.io/smallrye-mutiny/getting-started/creating-unis)
so basically we would like to get back
fun process (msg:Message){
val resFrom:Uni<Data> = ConvertUni {fetchDataFromDB(msg.key)}
}
We need the uni further downstream one time to process some data but we would like to return a Uni from the method meaning
fun process (msg:Message):Uni<Message>{
val resFrom:Uni<Data> = ConvertUni {fetchDataFromDB(msg.key)}
val processed:Uni<Message> =process(msg,resfrom)
return processed
}

The signature fun process(msg:Message): Uni<Message> implies that some asynchronous mechanism needs to be started and will outlive the method call. It's like returning a Future or a Deferred. The function returns immediately but the underlying processing is not done yet.
In the coroutines world, this means you need to start a coroutine. However, like any async mechanism, it requires you to be conscious about where it will run, and for how long. This is defined by the CoroutineScope you use to start the coroutine, and this is why coroutine builders like async require such a scope.
So you need to pass a CoroutineScope to your function if you want it to start a coroutine that will last longer than the function call:
fun CoroutineScope.process(msg:Message): Uni<Message> {
val uniResult = async { fetchDataFromDB(msg.key) }.asUni()
return process(msg, uniResult)
}
Here Deferred<T>.asUni() is provided by the library mutiny-kotlin. In the examples given in their doc, they use GlobalScope instead of asking the caller to pass a coroutine scope. This is usually a bad practice because it means you don't control the lifetime of the started coroutine, and you may leak things if you're not careful.
Accepting a CoroutineScope as receiver means the caller of the method can choose the scope of this coroutine, which will automatically cancel the coroutine when appropriate, and will also define the thread pool / event loop on which the coroutine runs.
Now, with that in mind, you see that you'll be using a mix of coroutines and Uni at the same level of API here, which is not great. I would advise to instead stick with suspend functions all the way, until you really have to convert to Uni.

Related

How does Continuation work in Kotlin Coroutine?

I am studying about CPS. I was wondering how does it work.
Object createPost(
Token token,
Item item,
Continuation<Post> const){...}
interface Continuation<in T> {
val context: CoroutineContext
fun resume(value: T)
fun resumeWithException(exception: Throwable)
}
People says CPS is just callbacks and nothing more than that.
But
I don't know why interface is used in here as a parameter.
I don't know what does <in T> do in the Continuation interface.
Continuation is a parameter but, what does it do actually inside and how is it be called under the hood?
End-user perspective
For the end-user the situation is relatively simple: continuation represents an execution flow that was suspended. It allows to resume the execution by invoking resume() or resumeWithException().
For example, assume we want to suspend for a second and then resume execution. We ask coroutines machinery to suspend, it provides a continuation object, we store it and at a later time we invoke resume() on it. Continuation object "knows" how to resume the execution:
suspend fun foo() {
println("foo:1")
val result = suspendCoroutine { cont ->
thread {
Thread.sleep(1000)
cont.resume("OK")
}
}
println("foo:2:$result")
}
suspendCoroutine() is one of possible ways to suspend and acquire a continuation to resume later. thread() and Thread.sleep() is just for demo purposes - usually, we should use delay() instead.
Very often we suspend to acquire some kind of data. This is why continuations support resuming with a result value. In above example we can see that the result of suspendCoroutine() is stored as result and we resume the continuation by passing "OK". This way after resuming result holds "OK". That explains <in T>.
Internals
This is much more complicated. Kotlin is executed in runtimes that don't support coroutines or suspending. For example, JVM can't wait inside a function without blocking any threads. This is simply not possible (I intentionally ignore Project Loom here). To make this possible, Kotlin compiler has to manipulate the bytecode and continuations take important part in this process.
As you noticed, every suspend function receives additional parameter of Continuation type. This object is used to control the process of resuming, it helps returning to the function caller and it holds the current coroutine context. Additionally, suspend functions return Any/Object to allow to signal their state to the caller.
Assume we have another function calling the first one:
suspend fun bar() {
println("bar:1")
foo()
println("bar:2")
}
Then we invoke bar(). Bytecode of both foo() and bar() is much more complicated than you would expect by looking at above source code. This is what's happening:
bar() is invoked with a continuation of its caller (let's ignore for now what does that mean).
bar() checks if it "owns" the passed continuation. It sees not, so it assumes this is a continuation of its caller and that this is the initial execution of bar().
bar() creates its own continuation object and stores the caller's continuation inside it.
bar() starts executing as normal and gets to foo() point.
It stores the local state, so the code offset, values of local variables, etc. in its continuation.
bar() invokes foo() passing its continuation.
foo() checks if it owns the passed continuation. It doesn't, continuation is owned by bar(), so foo() creates its own continuation, stores bar()'s continuation in it and starts a normal execution.
Execution gets to suspendCoroutine() and similarly as earlier, the local state is stored inside foo()'s continuation.
Continuation of foo() is provided to the end-user inside the lambda passed to suspendCoroutine().
Now, foo() wants to suspend its execution, so it... returns... Yes, as said earlier, waiting without blocking the thread is not possible, so the only way to free the thread is by returning from the function.
foo() returns with a special value that says: "execution was suspended".
bar() reads this special value and also suspends, so also immediately returns.
The whole call stack folds and the thread is free to go do something else.
1 second passes and we invoke cont.resume().
Continuation of foo() knows how to resume the execution from the suspendCoroutine() point.
Continuation invokes foo() function passing itself as a parameter.
foo() checks if it owns the passed continuation - this time it does, so it assumes this is not an initial call to foo(), but it is a request to resume execution. It reads the stored state from the continuation, it loads local variables and jumps to the proper code offset.
Execution progresses normally until it gets to the point where it needs to return from foo() to bar().
foo() knows that this time it was not invoked by bar(), so simply returning won't work. But it still keeps a continuation of its caller, so bar() suspended at exactly the point where foo() needs to return.
foo() returns with magic value that says: "resume the continuation of my caller".
Continuation of bar() is resumed from the point where it executed foo().
Process continues.
As you can see, this is pretty complicated. Normally, users of coroutines should not need to understand how they work internally.
Additional important notes:
If foo() would not suspend, it would return normally to bar() and bar() would continue execution as usual. This is to decrease the overhead of the whole process in the case suspending is not needed.
When resuming, continuations don't invoke their functions directly, but they ask the dispatcher to do it. Dispatcher is stored inside CoroutineContext, so also inside the continuation.
Note that because continuations keep a reference to the caller's continuation, they form a chain of continuations. This can be used to produce the stack trace as the real call stack has been lost when suspending.

How to run blocking Java code concurrently in Kotlin?

I'm working on a new side project with a goal of more deeply learning Kotlin, and I'm having a little trouble figuring out how to mix Kotlin-style concurrency with code not written with coroutines in mind (JOOQ in this case). The function below is in one of my DAOs, and in that map block, I want to update a bunch of rows in the DB. In this specific example, the updates are actually dependent on the previous one completing, so it will need to be done sequentially, but I'm interested in how this code could be modified to run the updates in parallel, since there will undoubtedly be use cases I have that don't need to be run sequentially.
suspend fun updateProductChoices(choice: ProductChoice) = withContext(Dispatchers.IO) {
ctx().transaction { config ->
val tx = DSL.using(config)
val previousRank = tx.select(PRODUCT_CHOICE.RANK)
.from(PRODUCT_CHOICE)
.where(PRODUCT_CHOICE.STORE_PRODUCT_ID.eq(choice.storeProductId))
.and(PRODUCT_CHOICE.PRODUCT_ID.eq(choice.productId))
.fetchOne(PRODUCT_CHOICE.RANK)
(previousRank + 1..choice.rank).map { rank ->
tx.update(PRODUCT_CHOICE)
.set(PRODUCT_CHOICE.RANK, rank - 1)
.where(PRODUCT_CHOICE.PRODUCT_ID.eq(choice.productId))
.and(PRODUCT_CHOICE.RANK.eq(rank))
.execute()
}
}
}
Would the best way be to wrap the transaction lambda in runBlocking and each update in an async, then awaitAll the result? Also possibly worth noting that the JOOQ queries support executeAsync() which returns a CompletionStage.
Yes, use JOOQ's executeAsync. With executeAsync, you can remove the withContext(Dispatchers.IO) because the call is no longer blocking.
The kotlinx-coroutines-jdk8 library includes coroutines integration with CompletionStage, so you can do a suspending await on it (docs).
To perform the updates in parallel, note that the same library can convert a CompletionStage to a Deferred (docs). Therefore, if you change the call to execute to executeAsync().asDeferred() you will get a list of Deferreds, on which you can awaitAll().

Kotlin function argument as suspendable

Currently, I have the following in my code
fun use(consumer: (T) -> Unit) {
consumer(this.value)
}
suspend fun useS(consumer: suspend (T) -> Unit) {
consumer(this.value)
}
These are 2 methods that are actually doing the same. However, I was not able to merge them into one, nor to use overloaded method. In some places my consumer argument is a regular function, on other places it is suspend function; I do not have control over that.
Is it possible to have just one method, regardless of my consumer "suspendability"?
EDIT: forgot to mention that this.value is private and hence using inline would not work - still, I am in control of that, so might change the visibility of the value field.
IF the code really is as simple as you've provided, simply using the non-suspend version and make it inline would solve your issues.
By making it inline (and thus inlining the consumer) it allows the inner block to use the calling environment of the caller. This is why all the library helper functions like also can be used in a suspend function and call suspend functions without explicitly being suspend functions themselves.

Dealing with suspend and non-suspend in Kotlin function parameters

I am fairly new to Kotlin, and am getting to grips with it's implementation of co-routines. I understand that any function that we may want Kotlin to deal with in a non-blocking way needs to be annotated with suspend, and that such functions can only be executed within a co-routine (or within another suspend function). So far so good.
However I keep coming across a problem with utility functions that accept other functions as parameters. For instance with arrow's Try:
suspend fun somethingAsync() = 1 + 1
Try { 1 + 1 } // All is well
Try { somethingAsync() } // Uh oh....
As the parameter to Try's invoke function/operator is not annotated with suspend, the second call will be rejected by the compiler. How does someone deal with this when writing utility functions that can not know if the code inside the passed function or lambda requires suspend or not? Writing a suspend and a non-suspend version of every such function seems incredibly tedious. Have I missed an obvious way to deal with this situation?
First, let's deal with suspend. What it means is this particular function blocks. Not that this function is asynchronous.
Usually, blocking means IO, but not always. In your example, the function doesn't block, nor does it something in an asynchronous manner (hence Async suffix is incorrect there). But lets assume actual utility code does block for some reason.
Now dealing with suspending functions is something that is done on the caller side. Meaning, what would you like to do while this is being executed:
fun doSomething() {
Try { somethingAsync() }
}
If you're fine with doSomething to block, then you can use runBlocking:
fun doSomething() = runBlocking {
Try { somethingAsync() }
}

Why is it impossible to use method references to `suspend` functions in Kotlin?

I have a list of Job instances which I want to cancel at some point after launch. This looks as follows:
val jobs = arrayListOf<Job>()
//launch and add jobs...
jobs.forEach { it.cancelAndJoin() } // cancels the jobs and waits for completion
Unfortunately, it's not possible to use a method reference here. The reason: cancelAndJoin is a suspend function, as the compiler complains:
jobs.forEach (Job::cancelAndJoin)
"Error:(30, 24) Kotlin: Unsupported [Callable references to suspend functions]"
Why doesn't this work?
UPD: This has already been implemented in Kotlin 1.3.x. Taking a callable reference to a suspending function gives you an instance of KSuspendFunctionN (N = 0, 1, ...). This type has its invoke operator defined as a suspending function, so that you can invoke such a callable reference suspending a coroutine in the same way as a direct invocation would.
Basically, supporting this requires an additional portion of language design and does not simply come bundled with coroutines.
Why is it non-trivial? Because when you take a callable reference of an ordinary function e.g. String::reversed, you get something like a KFunction1<String, String>. If you could do the same with a suspend function, what would you expect to get?
If it's the same KFunctionN<...>, then there's an obvious problem that you can pass it around where an ordinary function is expected and call it, violating the rule that suspend functions can only be called inside coroutines (where the compiler transforms their call sites).
So, it should be something more specific. (I'm currently only speculating, without any idea of actual design attempts) It could be, for example, a SuspendKFunctionN<...>, with its invoke(...) being a suspending function, or it could (less likely) be a special notation only for passing a function reference where a suspend (T) -> R is expected, but anyway, a feature like this requires thorough design to be future-proof.
These helpers currently lack in Kotlin Standard library, but you can implement your own.
For example:
suspend fun <T> Iterable<T>.forEachAsync(action: suspend (T) -> Unit): Unit {
val list = this.map { e ->
async(...) {
action(e)
}
}
list.forEach { it.await() }
}
However, what context to pass to async now depends on the threading model your service is using (i.e. do you want to do multi-threading or want to keep everything in a single thread).