smdufb
smdufb

Reputation: 565

Having coroutine wait for previous calls

I still haven't fully grasped Kotlin coroutines yet.

Basically I want a coroutine to wait for any previous calls to finish before executing. The following code seems to work. But is it doing what I think it's doing?

private var saveJob: Job? = null

fun save() {
    saveJob = someScope.launch {
        saveJob?.join()
        withContext(Dispatchers.IO) {
            // suspending database operation
        }
    }
}

As far as I can tell, the code is doing what I want. But is it?

Upvotes: 4

Views: 649

Answers (2)

Marko Topolnik
Marko Topolnik

Reputation: 200256

Keep in mind that the launched code is concurrent to the code outside it. That means that what you wrote has a race condition: the outside code may have already assigned the new job to saveJob when you try to join() it, resulting in a deadlock.

I guess what you want is to trigger a save operation in the background, and the operation itself will pull all the data to save from somewhere else. You probably don't want a queue of save jobs, just ensure that everything is saved at the point you call save(). If you called save a bit earlier and a new save job hasn't yet started, those two calls can be coalesced into a single save operation.

Furthermore, you say that you have a suspending database operation. Suspending code doesn't belong in the IO dispatcher, which is there only if you have to perform many blocking operations concurrently.

All told I'd suggest using an actor:

val actor = someScope.actor<Unit>(capacity = CONFLATED) {
    // suspending database operation
}

fun save() = someScope.launch {
    actor.send(Unit)
}

Upvotes: 3

apksherlock
apksherlock

Reputation: 8371

Basically it's not wrong. Although I would recommend just to go imperatively just as you program outside of a coroutine. Saving the response in the variable would ensure that the next line won't execute until the response of the first:

scope.launch(someDispatcher){
  val dataFromDelayingCode = getDelayingData()
  if(delayingData.isFinished){

  }
}

Upvotes: 1

Related Questions