Should instance variables used inside Kotlin suspending functions be Volatile? - kotlin

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?

Related

Jetpack Compose Access Room Database

I need to get some database objects from Room inside of a composable function. Currently when I try to call:
#Composable
fun loadDashboard() {
val db = DatabaseService.getDatabase(null)
val userDao = db.userDao()
val userModel = userDao.getOne()
}
I receive an error:
java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
Is there a best practice solution for accessing the database in this situation so I can get data to make an API call?
Best practice would be to create a repository class and do database operations there.
You can return a flow from the get method of the repository and emit the result when the operation is done. This flow can then be collected by a viewmodel inside a coroutine that is dispatched on the IO thread. When the result is collected, store it inside a state that is observed by the composable.
You can change your userDao.getOne() function to suspending function and call it from ViewModel inside a scope, ViewModel has viewModelScope by default, or your custom scope, this makes testing easy.
If you don't want to use ViewModel or repo, you can simply call this function inside LaunchedEffect by calling your function in its lambda.
Or produceState functions of Compose
Data with loading, success or error states with produceState for instance is as
#Composable
private fun getOneFromDb(): State<Result> {
return produceState<Result>(initialValue = Result.Loading) {
// In a coroutine, can make suspend calls
val one = db.getOne()
// Update State with either an Error or Success result.
// This will trigger a recomposition where this State is read
value = if (one == null) {
Result.Error
} else {
Result.Success(one)
}
}
}
val oneState = getOneFromDb()
sealed class Result {
object Loading : Result()
object Error : Result()
class Success(val one: OneModel) : Result()
}

How to lock two coroutines but allow original coroutine to enter

I launch a Coroutine to do some work. I need this to have a mutex. However, sometimes the doWork function calls one() again but a deadlock happens.
private val scope = CoroutineScope(Dispatchers.IO)
private val a = A()
fun start() {
scope.launch {
a.one()
}
}
Then
class A {
private val mutex = Mutex()
suspend fun one() {
mutex.withLock {
doWork()
}
}
}
What I am doing causes a deadlock, because the one() is already locked. Ideally I would get something like #Synchronized in Java which lets the same thread come in, but I know Coroutines are not threads.
Is there anything I can use to solve this? I cannot change the problem too much because some of this code I cannot change myself.
Use communicating coroutines
You said you can't change some of the code, so this solution may not be an option for you. Locks aren't often a good fit with coroutines, though. A more idiomatic solution is to manage your shared resources by having different coroutines communicate with one another.
Instead of using a lock, you make it so that only one coroutine is ever allowed to access the shared resource. Other coroutines may send it work to do, but they may not access the shared resource directly. This guarantees that only one thing ever accesses the shared resource at any given time.
Say our shared resource is a function doSomething() that isn't thread-safe and should only be called by one thread at a time. We launch an actor coroutine that will receive requests. This coroutine is the 'owner' of the shared resource. Anything that wants to call doSomething() must do so by sending a request to this actor. Many things may send requests to the actor, but it will process the requests one at a time. Each time the actor receives a request, it simply calls the doSomething() function. Here I've used a Request class which can contain whatever parameters you need to pass to the shared function. It looks like this:
data class Request(...)
fun start() {
val requests = scope.actor<Request> {
consumeEach { request ->
doSomething(request)
}
}
scope.launch {
requests.send(Request(...))
}
}
suspend fun doSomething(request: Request) {
// do some non-thread-safe work
}

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.

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

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