Calling a non-inlined library function with a suspending lambda that could have been inlined - kotlin

I am calling a non-inlined library function, providing my own function f that needs to suspend because it receives from a channel. When the library function is called I simply want to wait for it to complete in the current context. The library function will always call f within its body (specifically, the library function does pre, f, then post, where f must be called between pre and post, so it could have been an inlined function). However within f the outer coroutine context no longer applies.
My first thought was to surround the suspending call with runBlocking, but this causes a deadlock, because the (potentially single) thread is now blocked until receive completes, which prevents the producer from progressing.
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
// Some non-inlined library function that calls given function f immediately with some library provided value
fun libraryFunction(f: (Int) -> Int) =
f(5)
fun main() {
// Some outer loop that may sometimes only have 1 thread
runBlocking(newSingleThreadContext("thread")) {
// An existing channel
val channel = produce {
send("whatwewant")
}
// Our code
libraryFunction { libraryProvidedValue ->
println(libraryProvidedValue)
runBlocking {
println(channel.receive())
}
// A return value the library needs
7
}
}
}
What is the best way to solve this issue? Can using the inner runBlocking be prevented?
In other words: is there anyway to pinky-promise that we are, in fact, still within the same coroutine in the lambda function?
As an additional illustration, the following works:
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
// Some non-inlined library function that calls given function f immediately with some library provided value
fun libraryFunction(f: (Int) -> Int) =
f(5)
fun main() {
// Some outer loop that may sometimes only have 1 thread
runBlocking(newSingleThreadContext("thread")) {
// An existing channel
val channel = produce {
send("whatwewant")
}
// <inline the start of the body of libraryFunction here> (which gives libraryProvidedValue)
// Our code
println(libraryProvidedValue)
println(channel.receive())
// <inline the end of the body of libraryFunction here>
}
}
Update: In this case, the only real issue seems to be that the compiler is not aware of the surrounding coroutine being the same in the lambda function body - but the code is entirely possible to run successfully if it did (as can be seen by inlining the library function). In essence, there is nothing wrong with the flow of this code in particular (because the library function calls the lambda function within its body), but it is a shortcoming in the lack of guarantees the compiler can determine. While runBlocking makes the compiler happy, it has unwanted side effects (notably, the nested blocking part, which makes communication with the outside difficult due to the outer runBlocking blocking up the potentially only thread).
Because of this, I decided to rewrite my entire code surrounding this in a style that uses Deferred with an await at the top level, instead of suspend functions. I would regard this as very un-kotlin-ic, and it comes with potential problems of its own (like resource leaks), but it works for my scenario.
Note that this still does not answer the question posed in any way, but I wanted to note it as an alternative decision to make for future users faced with a similar problem.

Unfortunately there is only one way to achieve what you want - to use runBlocking, but it has consequences like you described. suspend functions should be called from a coroutine (runBlocking in this case) or another suspend function. So to achieve this without using runBlocking libraryFunction function must accept a suspend f function and be suspend itself:
suspend fun libraryFunction(f: suspend (Int) -> Int) = f(5)
I would suggest first to receive the value from the channel, and then call the libraryFunction:
runBlocking(newSingleThreadContext("thread")) {
// An existing channel
val channel = produce {
send("whatwewant")
}
// Our code
val receivedValue = channel.receive()
libraryFunction { libraryProvidedValue ->
println(libraryProvidedValue)
println(receivedValue)
// A return value the library needs
7
}
}

Related

Kotlin: Get Current CoroutineContext.Key without access to the CoroutineScope?

I'm trying to intercept the System.out print statements, and in a multithreaded program, I'm planning on adding these to a map using a CoroutineContext.Key as the map key, so I know which coroutine the output belongs to.
My child methods being executed don't have access to the CoroutineScope as this was kicked off on a parent method.
I was hoping for a static method along the lines of CoroutineContext.currentKey but this doesn't look like it exists.
I've achieved a similar thing in C#, using their Task.CurrentId
Is there any way for me to achieve this?
Thanks
You can create your own thread-local variable to keep your own identifier of the coroutine or even directly its saved output and use ThreadLocal.asContextElement() extension function to convert it to the coroutine context element. Now, if you start your coroutine with this element, then the specified value of this thread-local variable will be automatically installed into the corresponding thread-local variable as the this coroutine hops from thread to thread. See the following example code:
import kotlinx.coroutines.*
val myId = ThreadLocal<String>()
// I'm not a suspending function, yet I know what coroutine I work in
fun whereAmI() {
println("I'm in coroutine '${myId.get()}'")
}
fun main() = runBlocking<Unit> {
launch(myId.asContextElement("First")) {
whereAmI()
}
launch(myId.asContextElement("Second")) {
whereAmI()
}
}

Convey intended thread type (IO, default, main) when declaring suspend function

When designing an API with a suspend function, sometimes I want to convey that this function should be called on, say, an IO thread. Other times that it is essential to do so.
Often it seems obvious; for example a database call should be called using Dispatchers.IO but if it's an interface function, then the caller cannot assume this.
What is the best approach here?
If the suspend function really must run in a specific context, then declare it directly in the function body.
suspend fun doInIO() = withContext(Dispatchers.IO) {
}
If the caller should be able to change the dispatcher, the function can add the dispatcher as a default parameter.
suspend fun doInIO(context: CoroutineContext = Dispatchers.IO) = withContext(context) {
}
There is no strict mechanism for contracts like that, so you are flexible with choosing the mechanism that suits you and your team.
1) Always use withContext(Dispatcher.IO). This is both strict and performant, if a method is invoked from within IO context it will be fast-path'ed.
2) Naming/annotation-based conventions. You can make an agreement in the team that any method which ends with IO or has a specific annotation should be invoked with Dispatchers.IO. This approach works mostly in small teams and only for project-private API. Once you start exporting it as a library/module for other teams such contracts tend to be broken.
3) You can mix the previous approach with a validation:
suspend fun readFile(file: ...) {
require(coroutineContext[ContinuationInterceptor] == Dispatcher.IO) {
"Expected IO dispatcher, but has ${coroutineContext[ContinuationInterceptor]} instead"
}
// read file
}
But this validation works only if you are not wrapping IO dispatcher in some kind of delegate/proxy. In that case, you should make validation aware of such proxies, something like:
fun validateIoDispatcher(dispatcher: ContinuationInterceptor) {
if (dispatcher is Dispatchers.IO) return
if (dispatcher is ProjectSpecificIoDispatcher) return
if (dispatcher is ProjectSpecificWrapperDispatcher) {
validateIoDispatcher(dispatcher.delegate)
} else {
error("Expected IO dispatcher, but has $dispatcher")
}
}
I want to convey that this function should be called on, say, an IO thread. Other times that it is essential to do so.
Not sure what the difference is between "should" and "essential", but having these approaches in mind you can combine it with default method parameters such as suspend fun probablyIO(dispatcher: CoroutineDispatcher = Dispatchers.IO) or more flexible naming/annotation conventions.

How to suspend a coroutine at a specific point

I am at loss with the following problem.
I have the following code:
val parentJob: Job = Job()
launch(parent = parentJob) {
while (true)
{
if (!parentJob.isCompleted)
{
// I want to control suspension here
println("Resumed")
}
}
}
I would like to be able to control, somehow akin to a semaphore, when should the coroutine suspend and when to resume exactly in the commented part of the snippet
I know there's suspendCancellableCoroutine but I am unsure how to use it or if it is appropriate here
How can this be achieved or are there any tutorials about this?
It would be more helpful to think about coroutines in terms of callbacks and continuations, not threads and semaphores.
Internally, when the coroutine is suspended, the entire chain of suspend fun calls returns with a special internal object, COROUTINE_SUSPENDED. So the whole execution of a coroutine is just a function call during which a callback object is being built up. This callback is the continuation and when you call it, execution resumes from the place which returned the special COROUTINE_SUSPENDED object.
Kotlin runs the block you pass to suspendCancellableCoroutine with the continuation object as the parameter. So you should save this object and make it available to the code outside the coroutine.
Here's some code that may help your understanding. Note there's no need to create a separate parent job if you want to cancel the coroutine. You can just cancel the Job instance that launch returns and the coroutine will not resume after suspension.
import kotlin.coroutines.*
import kotlinx.coroutines.*
var continuation: Continuation<String>? = null
fun main(args: Array<String>) {
val job = GlobalScope.launch(Dispatchers.Unconfined) {
while (true) {
println(suspendHere())
}
}
continuation!!.resume("Resumed first time")
continuation!!.resume("Resumed second time")
job.cancel()
continuation!!.resume("This shouldn't print")
}
suspend fun suspendHere() = suspendCancellableCoroutine<String> {
continuation = it
}
If you still need an explicit check (because there aren't enough suspension points on the execution path), you can just use the isActive property which is available directly to the block:
while (isActive) ...

What's the recommended way to delay Kotlin's buildSequence?

I'm trying to poll a paginated API and provide new items to the user as they appear.
fun connect(): Sequence<T> = buildSequence {
while (true) {
// result is a List<T>
val result = dataSource.getFirstPage()
yieldAll(/* the new data in `result` */)
// Block the thread for a little bit
}
}
Here's the sample usage:
for (item in connect()) {
// do something as each item is made available
}
My first thought was to use the delay function, but I get this message:
Restricted suspended functions can only invoke member or extension suspending functions on their restricted coroutine scope
This is the signature for buildSequence:
public fun <T> buildSequence(builderAction: suspend SequenceBuilder<T>.() -> Unit): Sequence<T>
I think this message means that I can only use the suspend functions in SequenceBuilder: yield and yieldAll and that using arbitrary suspend function calls aren't allowed.
Right now I'm using this to block the sequence building by one second after every time the API is polled:
val resumeTime = System.nanoTime() + TimeUnit.SECONDS.toNanos(1)
while (resumeTime > System.nanoTime()) {
// do nothing
}
This works, but it really doesn't seem like a good solution. Has anybody encountered this issue before?
Why does it not work? Some research
When we look at buildSequence, we can see that it takes an builderAction: suspend SequenceBuilder<T>.() -> Unit as its argument. As a client of that method, you'll be able to hand on a suspend lambda that has SequenceBuilder as its receiver (read about lambda with receiver here).
The SequenceBuilder itself is annotated with RestrictSuspension:
#RestrictsSuspension
#SinceKotlin("1.1")
public abstract class SequenceBuilder<in T> ...
The annotation is defined and commented like this:
/**
* Classes and interfaces marked with this annotation are restricted
* when used as receivers for extension `suspend` functions.
* These `suspend` extensions can only invoke other member or extension
* `suspend` functions on this particular receiver only
* and are restricted from calling arbitrary suspension functions.
*/
#SinceKotlin("1.1") #Target(AnnotationTarget.CLASS) #Retention(AnnotationRetention.BINARY)
public annotation class RestrictsSuspension
As the RestrictSuspension documentation tells, in the case of buildSequence, you can pass a lambda with SequenceBuilder as its receiver but with restricted possibilities since you'll only be able to call "other member or extension suspend functions on this particular receiver". That means, the block passed to buildSequence may call any method defined on SequenceBuilder (like yield, yieldAll). Since, on the other hand, the block is "restricted from calling arbitrary suspension functions", using delay does not work. The resulting compiler error verifies it:
Restricted suspended functions can only invoke member or extension suspending functions on their restricted coroutine scope.
Ultimately, you need to be aware that the buildSequence creates a coroutine that is an example of a synchronous coroutine. In your example, the sequence code will be executed in the same thread that consumes the sequence by calling connect().
How to delay the sequence?
As we learned, The buildSequence creates a synchronous sequence. It's fine to use regular Thread blocking here:
fun connect(): Sequence<T> = buildSequence {
while (true) {
val result = dataSource.getFirstPage()
yieldAll(result)
Thread.sleep(1000)
}
}
But, do you really want an entire thread to be blocked? Alternatively, you can implement asynchronous sequences as described here. As a result, using delay and other suspending functions will be valid.
Just for an alternate solution...
If what you're really trying to do is asynchronously produce elements, you can use Flows which are basically asynchronous sequences.
Here is a quick table:
Sync
Async
Single
Normal valuefun example(): String
suspendingsuspend fun example(): Stringorfun example(): Deferred<String>
Many
Sequencefun example(): Sequence<String>
Flowfun example(): Flow<String>
You can convert your Sequence<T> to a Flow<T> by replacing the sequence { ... } builder with the flow { ... } builder and then replace yield/yieldAll with emit/emitAll:
fun example(): Flow<String> = flow {
(1..5).forEach { getString().let { emit(it) } }
}
suspend fun getString(): String = { ... }
So, for your example:
fun connect(): Flow<T> = flow {
while (true) {
// Call suspend function to get data from dataSource
val result: List<T> = dataSource.getFirstPage()
emitAll(result)
// _Suspend_ for a little bit
delay(1000)
}
}

COROUTINE_SUSPENDED and suspendCoroutineOrReturn in Kotlin

The idea of coroutines in kotlin was to abstract the notion of suspension and callbacks and write simple sequential code. You never need to worry if the coroutine is suspended or not, similar to threads.
What is the purpose of suspendCoroutineOrReturn and COROUTINE_SUSPENDED and in what case would you use them?
The suspendCoroutineOrReturn and COROUTINE_SUSPENDED intrinsics were introduced very recently in 1.1 to address the particular stack overflow problem.
Here is an example:
fun problem() = async {
repeat(10_000) {
await(work())
}
}
Where await simply waits for completion:
suspend fun <T> await(f: CompletableFuture<T>, c: Continuation<T>): Unit {
f.whenComplete { value, exception -> // <- await$lambda
if (exception != null) c.resumeWithException(exception) else
c.resume(value)
}
}
Let's have a look at the case when work doesn't really suspend, but returns the result immediately (for example, cached).
The state machine, which is what coroutines are compiled into in Kotlin, is going to make the following calls:
problem$stateMachine, await, CompletableFuture.whenComplete, await$lambda, ContinuationImpl.resume, problem$stateMachine, await, ...
In essence, nothing is ever suspended and the state machine invokes itself within the same execution thread again and again, which ends up with StackOverflowError.
A suggested solution is to allow await return a special token (COROUTINE_SUSPENDED) to distinguish whether the coroutine actually did suspend or not, so that the state machine could avoid stack overflow.
Next, suspendCoroutineOrReturn is there to control coroutine execution. Here is its declaration:
public inline suspend fun <T> suspendCoroutineOrReturn(crossinline block: (Continuation<T>) -> Any?): T
Note that it receives a block that is provided with a continuation. Basically it is a way to access the Continuation instance,
which is normally hidden away and appears only during the compilation. The block is also allowed to return any value or COROUTINE_SUSPENDED.
Since this all looks rather complicated, Kotlin tries to hide it away and recommends to use just suspendCoroutine function, which internally does all the stuff mentioned above for you.
Here's the correct await implementation which avoids StackOverflowError (side note: await is shipped in Kotlin lib, and it's actually an extension function, but it's not that important for this discussion)
suspend fun <T> await(f: CompletableFuture<T>): T =
suspendCoroutine { c ->
f.whenComplete { value, exception ->
if (exception != null) c.resumeWithException(exception) else
c.resume(value)
}
}
But if you ever want to take over fine-graned control over coroutine continuation, you should call suspendCoroutineOrReturn and return COROUTINE_SUSPENDED whenever an external call is made.