Skip to content

Commit

Permalink
Bypassing cancellation errors
Browse files Browse the repository at this point in the history
Refs: #48
  • Loading branch information
Nikolay Kochetkov committed Sep 29, 2023
1 parent 215ccc5 commit 7a672d4
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import com.motorro.rxlcemodel.common.Logger
import com.motorro.rxlcemodel.coroutines.service.ServiceSet
import com.motorro.rxlcemodel.lce.LceState
import com.motorro.rxlcemodel.lce.LceState.*
import coroutinesRunCatching
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

Expand Down Expand Up @@ -104,7 +105,7 @@ class CacheThenNetLceModel<DATA: Any, PARAMS: Any>(
type = if (null == data) Loading.Type.LOADING else Loading.Type.REFRESHING
)
)
runCatching { loadAndCacheNetwork() }.onFailure { error ->
coroutinesRunCatching { loadAndCacheNetwork() }.onFailure { error ->
if (error !is CancellationException) {
withLogger {
modelLog(WARNING, "Error getting data from network - ERROR: $error")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import com.motorro.rxlcemodel.common.UpdateOperationState
import com.motorro.rxlcemodel.common.UpdateOperationState.*
import com.motorro.rxlcemodel.coroutines.service.CacheService
import com.motorro.rxlcemodel.lce.LceState
import coroutinesRunCatching
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.*
Expand Down Expand Up @@ -55,15 +56,15 @@ abstract class UpdateWrapper<DATA: Any, PARAMS: Any>(
*/
protected suspend fun doUpdate(dataSource: suspend (params: PARAMS) -> Entity<DATA>) {
networkOperationState.emit(LOADING)
try {
coroutinesRunCatching {
withContext(ioDispatcher) {
withLogger { modelLog(INFO, "Subscribing network...") }
val data = dataSource(params)
withLogger { modelLog(INFO, "Saving to cache...") }
cacheService.save(params, data)
}
networkOperationState.emit(IDLE)
} catch (error: Throwable) {
}.onFailure { error ->
withLogger { modelLog(WARNING, "Network error: $error") }
networkOperationState.emit(ERROR(error))
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import kotlinx.coroutines.CancellationException

/**
* Calls the specified function [block] with `this` value as its receiver and returns its encapsulated result if invocation was successful,
* catching any [Throwable] exception besides [CancellationException] that was thrown from the [block] function execution and encapsulating it as a failure.
*/
public inline fun <T, R> T.coroutinesRunCatching(block: T.() -> R): Result<R> {
return try {
Result.success(block())
} catch (c: CancellationException) {
throw c
} catch (e: Throwable) {
Result.failure(e)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package com.motorro.rxlcemodel.coroutines
import com.motorro.rxlcemodel.lce.LceState
import com.motorro.rxlcemodel.lce.combine
import com.motorro.rxlcemodel.lce.map
import coroutinesRunCatching
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.*

Expand Down Expand Up @@ -117,9 +118,7 @@ fun <DATA_1: Any, DATA_2: Any> Flow<LceState<DATA_1>>.flatMapSingleData(mapper:

suspend fun stateMapper(data1: DATA_1?): LceState<DATA_2> = when(data1) {
null -> LceState.Loading(null, false)
else -> try {
LceState.Content(mapper(data1), true)
} catch (e: Throwable) {
else -> coroutinesRunCatching { LceState.Content(mapper(data1), true) }.getOrElse { e ->
LceState.Error(null, false, e)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
package com.motorro.rxlcemodel.coroutines

import com.motorro.rxlcemodel.lce.LceState
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
Expand All @@ -22,6 +23,7 @@ import kotlinx.coroutines.test.runTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertTrue

@OptIn(ExperimentalCoroutinesApi::class)
class LceUtilsKtTest {
Expand Down Expand Up @@ -247,6 +249,23 @@ class LceUtilsKtTest {
)
}

@Test
fun singleDataMapperBypassesCancellationExceptions() = runTest {
val error1 = Exception("error 1")
val error2 = CancellationException("Cancelled")

@Suppress("UNUSED_PARAMETER")
fun mapper(input: Int): String = throw error2

val source = flowOf(
LceState.Loading(null, false),
LceState.Content(2, true)
)

val result = source.flatMapSingleData(::mapper).toList()
assertEquals(1, result.size)
}

@Test
fun mapsUseCaseData() = runTest {
val error = Exception()
Expand Down

0 comments on commit 7a672d4

Please sign in to comment.