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

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

Related

Should instance variables used inside Kotlin suspending functions be Volatile?

Should instance variables used inside suspending functions be #Volatile, because under the hood multiple threads might access it?
Consider this example:
class VolatileDemo {
private var blockedUserCount = 0 // Shouldn't this be #Volatile?
suspend fun countBlockedUsers() {
while (moreUsers()) {
val user = getNextUserFromDb() // calls another suspending function
if (user.isBlocked())
blockedUserCount++ // may execute in a different thread, so have a stale value?
}
println(blockedUserCount)
}
suspend fun getNextUserFromDb(): User { ... }
}
The blockedUserCount++ inside the while loop is called after the getNextUserFromDb(), which is a suspending function. Doesn't it mean that blockedUserCount++ might get executd in different thread each time? So, shouldn't it be declared as #Volatile, so that the value is correctly accessed by the threads that access it?

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

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

What is different Job as a argument at CoroutineScope and launch?

This two code run exactly same. What is different putting Job in CoroutineScope and launch?
private val job = CoroutineScope(Dispatchers.Main).launch(start = CoroutineStart.LAZY) {
for(i in 10 downTo 0) {
Log.d("test", ": $i")
delay(1000)
}
}
CoroutineScope(Dispatchers.Main+job).launch{ }
CoroutineScope(Dispatchers.Main).launch(job) { }
Technically, both result in the same behavior, but the main point is that neither is a good way to use the CoroutineScope() factory. This is the idiomatic way to write the same thing:
GlobalScope.launch(Dispatchers.Main+job) { ... }
If this raises your eyebrows ("Don't use GlobalScope!"), that's because it should — your examples are just another way to make the same mistake, with more verbose code. You construct a CoroutineScope without holding a reference to it, resulting in exactly the same unbounded and non-cancellable scope as the GlobalScope singleton.
In addition, the way you use a Job instance that is a handle to an actual coroutine, is also wrong: the job associated with a coroutine scope should be a standalone instance returned from either Job() or SupervisorJob(). Its only purpose is to serve as the central point from which to cancel the entire scope or inspect its state.
It doesn't seem like there are many differences between the two, looking at the source code. The operator fun plus documentation states
Returns a context containing elements from this context and elements
from other context. The elements from this context with the same key
as in the other one are dropped.
which explains how your first test works. For the second, calling launch with a context parameter, calls into CoroutineScope.newCoroutineContext
Creates a context for the new coroutine. It installs
Dispatchers.Default when no other dispatcher or
ContinuationInterceptor is specified, and adds optional support for
debugging facilities (when turned on).
and looking at the source code of it:
public actual fun CoroutineScope.newCoroutineContext(context: CoroutineContext): CoroutineContext {
val combined = coroutineContext + context
val debug = if (DEBUG) combined + CoroutineId(COROUTINE_ID.incrementAndGet()) else combined
return if (combined !== Dispatchers.Default && combined[ContinuationInterceptor] == null)
debug + Dispatchers.Default else debug
}
We can see it also ends up using the operator fun plus.
There are some other differences that seem negligable to mention here, but ultimately the context of the launched coroutine looks to be the same in both of your tests.

Should you pass coroutineScope as function argument?

I am experimenting with coroutines and feel unsure about passing coroutineScope to plain Kotlin UseCase. Can such approach create memory leaks?
Suppose we are initialising our UseCase in VM and will try to pass viewModelScope:
class UploadUseCase(private val imagesPreparingForUploadUseCase: ImagesPreparingForUploadUseCase){
fun execute(coroutineScope: CoroutineScope, bitmap: Bitmap) {
coroutineScope.launch {
val resizedBitmap = withContext(Dispatchers.IO) {
imagesPreparingForUploadUseCase.getResizedBitmap(bitmap, MAX_SIZE)
}
}
}
}
Is it safe code? No difference if I would declare this exact code in VM instead?If no, that means I could pass coroutineScope as constructor argument....Now I initially thought that I should create my execute method in a following way:
fun CoroutineScope.execute(bitmap: Bitmap) {
launch {
val resizedBitmap = withContext(Dispatchers.IO) {
imagesPreparingForUploadUseCase.getResizedBitmap(bitmap, MAX_SIZE)
}
}
}
}
As far as I understand we use extension function in order for method to use parent coroutineScope. That means, I don't need to pass coroutineScope as argument and just change method to use extension function.
However, in my surprise VM cannot see this method available! Why this method is not available from VM to call?
This is marked as red in VM:
private fun uploadPhoto(bitmap: Bitmap, isImageUploaded: Boolean) {
prepareDataForUploadingUseCase.execute(bitmap)
}
This is not marked red from VM:
private fun uploadPhoto(bitmap: Bitmap, isImageUploaded: Boolean) {
prepareDataForUploadingUseCase.execute(viewModelScope, bitmap)
}
​
If my understanding is wrong, why would I use CoroutineScope as extension function instead of passing coroutineScope as function argument?
Passing it as a parameter vs using it as an extension function receiver is effectively the same in the end result. Extension function receivers are basically another parameter that you are passing to the function, just with rearranged syntax for convenience. So you can't use an extension function as a "cheat" to avoid passing a receiver.
But either way, I see it as kind of a clumsy design to have to provide a scope and then hiding the coroutine setup inside the function. This results in spreading coroutine scope manipulation across both sides of the function barrier. The function that calls this function has to be aware that some coroutine is going to get called on the scope it passes, but it doesn't know whether it needs to worry about how to handle cancellation and what it's allowed to do with the scope that it passed.
In my opinion, it would be cleaner to either do this:
suspend fun execute(bitmap: Bitmap) = withContext(Dispatchers.IO) {
imagesPreparingForUploadUseCase.getResizedBitmap(bitmap, MAX_SIZE)
}
so the calling function can launch the coroutine and handle the entire coroutine in one place. Or pass no coroutine scope, but have the execute function internally generate its own scope (that is dependent on lifecycleScope or viewModelScope if applicable), and handle its own cancellation behavior. Here's an example of creating a child scope of the lifecycle scope and adding it to some collection of jobs that you might want to cancel under certain circumstances.
fun execute(bitmap: Bitmap) {
lifecycleScope.launch {
bitmapScopes += coroutineScope(Dispatchers.IO) {
imagesPreparingForUploadUseCase.getResizedBitmap(bitmap, MAX_SIZE)
}
}
}
I am answering this specific question: "Why this method is not available from VM to call?"
The method is not available because it takes a receiver (CoroutineScope), but you already have an implicit receiver due to being inside a type declaration: UploadUseCase. Therefore, you cannot just call the second form of the method, because you would somehow have to specify two receivers.
Luckily, Kotlin provides an easy way to do exactly that, the with method.
private fun uploadPhoto(bitmap: Bitmap, isImageUploaded: Boolean) {
with(prepareDataForUploadingUseCase) {
viewModelScope.execute(bitmap)
}
}
However, I would say that this is quite weird, and agree with #Marko Novakovic that you should remove this responsibility from UseCase.
You can pass CoroutineScope as a function parameter, no problem with that. However I would advise you to remove that responsibility from UseCase. Launch coroutines from ViewModel, Presenter etc.
Extension functions are to be called on the instance of extension type. You don't need to call launch {} and withContext inside same function. Do either. launch(Dispatchers.IO) {}.
Extension functions are not just to access parent scope, you can use them for whatever you need them for, you choose.

How can I return an awaited value in Kotlin?

I need test() to return a player from my db. I know I can use a callback but how can I make this work with async await?
fun test(): Player {
launch(UI) {
val player = async(CommonPool) { MainActivity.database?.playerDao()!!.loadPlayer() }.await()
return player
}
}
Currently the error is return is not allowed here
In JavaScript for example I would make test async then await it's result from where it's called.
It is impossible to run a coroutine on a raw thread. At the very least you must turn an existing thread into one that spins a top-level event loop. You achieve this with a runBlocking call on the very top of the thread's call stack (i.e., inside its run() method).
On a GUI thread or any other kind of thread that runs an event loop, you need a matching Dispatcher that submits coroutines to this event loop. Kotlin already provides dispatchers for Swing, JavaFX, Android etc. In these cases you need to launch a coroutine from some existing GUI event handler, like this:
myScope.launch {
val player = test()
... use the player ...
}
myScope must be an object that implements CoroutineScope with something like this:
override val coroutineContext = Dispatchers.Main + SupervisorJob()
This will give you a way to cleanly cancel all the coroutines running within the same scope, by calling
coroutineContext[Job]!!.cancel()
My example uses the Main dispatcher, which resolves to the GUI thread when you import the Kotlin coroutines library matching your UI framework.
The test() function must become a suspend fun that temporarily switches the dispatcher to a thread pool for blocking operations. Here's how a basic example could look:
suspend fun test() = withContext(Dispatchers.IO) {
MainActivity.database?.playerDao()!!.loadPlayer()
}
Finally, note I don't mention async at all in this answer. Kotlin's async has a very specific purpose, it is not a general facility like in other languages. Its purpose is strictly parallel decomposition, where you decompose a single task into several concurrent subtasks.