Skip to content

Commit 150d079

Browse files
authored
Update state in runUpdatingState when CancellationException occurs (#5243)
1 parent e73ff0b commit 150d079

File tree

3 files changed

+50
-8
lines changed

3 files changed

+50
-8
lines changed

libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/AsyncAction.kt

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ package io.element.android.libraries.architecture
1010
import androidx.compose.runtime.MutableState
1111
import androidx.compose.runtime.Stable
1212
import io.element.android.libraries.core.extensions.runCatchingExceptions
13+
import kotlinx.coroutines.TimeoutCancellationException
1314
import kotlin.contracts.ExperimentalContracts
1415
import kotlin.contracts.InvocationKind
1516
import kotlin.contracts.contract
@@ -159,16 +160,19 @@ suspend inline fun <T> runUpdatingState(
159160
callsInPlace(resultBlock, InvocationKind.EXACTLY_ONCE)
160161
}
161162
state.value = AsyncAction.Loading
162-
return resultBlock().fold(
163+
return try {
164+
resultBlock()
165+
} catch (e: TimeoutCancellationException) {
166+
state.value = AsyncAction.Failure(errorTransform(e))
167+
throw e
168+
}.fold(
163169
onSuccess = {
164170
state.value = AsyncAction.Success(it)
165171
Result.success(it)
166172
},
167173
onFailure = {
168174
val error = errorTransform(it)
169-
state.value = AsyncAction.Failure(
170-
error = error,
171-
)
175+
state.value = AsyncAction.Failure(error)
172176
Result.failure(error)
173177
}
174178
)
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright 2025 New Vector Ltd.
3+
*
4+
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
5+
* Please see LICENSE files in the repository root for full details.
6+
*/
7+
8+
package io.element.android.libraries.architecture
9+
10+
import androidx.compose.runtime.MutableState
11+
import androidx.compose.runtime.mutableStateOf
12+
import kotlinx.coroutines.TimeoutCancellationException
13+
import kotlinx.coroutines.delay
14+
import kotlinx.coroutines.test.runTest
15+
import kotlinx.coroutines.withTimeout
16+
import org.junit.Assert.assertSame
17+
import org.junit.Assert.assertTrue
18+
import org.junit.Assert.fail
19+
import org.junit.Test
20+
import kotlin.time.Duration.Companion.milliseconds
21+
22+
class AsyncActionTest {
23+
@Test
24+
fun `updates state on timeout`() = runTest {
25+
val state: MutableState<AsyncAction<Int>> = mutableStateOf(AsyncAction.Uninitialized)
26+
val timeoutMillis = 500L
27+
val operationTimeMillis = 1000L
28+
29+
try {
30+
runUpdatingState(state = state) {
31+
withTimeout(timeoutMillis.milliseconds) {
32+
delay(operationTimeMillis)
33+
}
34+
Result.success(0)
35+
}
36+
fail("Expected TimeoutCancellationException, but nothing was thrown")
37+
} catch (e: TimeoutCancellationException) {
38+
assertTrue(state.value.isFailure())
39+
assertSame(e, state.value.errorOrNull())
40+
}
41+
}
42+
}

libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomContentForwarder.kt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import io.element.android.libraries.matrix.api.core.RoomId
1414
import io.element.android.libraries.matrix.api.room.ForwardEventException
1515
import io.element.android.libraries.matrix.impl.roomlist.roomOrNull
1616
import io.element.android.libraries.matrix.impl.timeline.runWithTimelineListenerRegistered
17-
import kotlinx.coroutines.CancellationException
1817
import kotlinx.coroutines.withTimeout
1918
import org.matrix.rustcomponents.sdk.MsgLikeKind
2019
import org.matrix.rustcomponents.sdk.RoomListService
@@ -63,9 +62,6 @@ class RoomContentForwarder(
6362
}
6463
}.onFailure {
6564
failedForwardingTo.add(RoomId(room.id()))
66-
if (it is CancellationException) {
67-
throw it
68-
}
6965
}
7066
}
7167

0 commit comments

Comments
 (0)