Shivam Verma
Shivam Verma

Reputation: 8023

Prevent concurrent execution of part of two suspend functions

I have two suspend methods that I launch parallely inside a ViewModel:

init {
    viewModelScope.launch(Dispatchers.Default) {
        launch { loadTotalCirculation() }
        launch { loadMarketPrice() }
    }
}

private suspend fun loadTotalCirculation() {
    val totalCirculation = bitcoinChartsRepository.getTotalCirculation(5, TimeUnit.HOURS)
    _viewState.value = _viewState.value.copy(totalCirculation = chartViewEntityMapper(totalCirculation))
}

private suspend fun loadMarketPrice() {
    val marketPrice = bitcoinChartsRepository.getMarketPrice(27, TimeUnit.DAYS)
    _viewState.value = _viewState.value.copy(marketPrice = chartViewEntityMapper(marketPrice))
}

However, I would like to prevent the concurrent execution of the _viewState.value = _viewState.value.copy... parts in both my methods. What's the best way to achieve this ?

Upvotes: 1

Views: 860

Answers (1)

broot
broot

Reputation: 28422

There are several possibilities to synchronize parallel coroutines. Probably the easiest is to create a single thread context, similar to main thread or to use Mutex. Note this mutex is designed specifically for couroutines, it is not something from Java stdlib.

Single thread context:

val context = Executors.newSingleThreadExecutor().asCoroutineDispatcher()

private suspend fun loadTotalCirculation() {
    val totalCirculation = bitcoinChartsRepository.getTotalCirculation(5, TimeUnit.HOURS)
    withContext (context) {
        _viewState.value = _viewState.value.copy(totalCirculation = chartViewEntityMapper(totalCirculation))
    }
}

private suspend fun loadMarketPrice() {
    val marketPrice = bitcoinChartsRepository.getMarketPrice(27, TimeUnit.DAYS)
    withContext (context) {
        _viewState.value = _viewState.value.copy(marketPrice = chartViewEntityMapper(marketPrice))
    }
}

Alternatively, instead of creating your own thread, you can reuse the main thread by: withContext(Dispatchers.Main).

Mutex:

val mutex = Mutex()

private suspend fun loadTotalCirculation() {
    val totalCirculation = bitcoinChartsRepository.getTotalCirculation(5, TimeUnit.HOURS)
    mutex.withLock {
        _viewState.value = _viewState.value.copy(totalCirculation = chartViewEntityMapper(totalCirculation))
    }
}

private suspend fun loadMarketPrice() {
    val marketPrice = bitcoinChartsRepository.getMarketPrice(27, TimeUnit.DAYS)
    mutex.withLock {
        _viewState.value = _viewState.value.copy(marketPrice = chartViewEntityMapper(marketPrice))
    }
}

Using a main thread or mutex is probably preferred, because if we create our own thread, we need to make sure to properly stop/close it when we won't need it anymore.

You can read more in this article from the official docs: https://kotlinlang.org/docs/shared-mutable-state-and-concurrency.html

Upvotes: 4

Related Questions