Skip to content

Commit efca56e

Browse files
committed
Fix another race condition in the SnapshotStateObserver
Adding observed objects was not syncrhonized with removing objects causing a perioudic NPE. Fixes: 192677711 Test: ./gradlew :compose:r:r:tDUT Change-Id: I6d716afa248624b125119219fe892e02282c39d9
1 parent 0399731 commit efca56e

File tree

2 files changed

+41
-1
lines changed

2 files changed

+41
-1
lines changed

compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateObserver.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,9 @@ class SnapshotStateObserver(private val onChangedExecutor: (callback: () -> Unit
5353
*/
5454
private val readObserver: (Any) -> Unit = { state ->
5555
if (!isPaused) {
56-
currentMap!!.addValue(state)
56+
synchronized(applyMaps) {
57+
currentMap!!.addValue(state)
58+
}
5759
}
5860
}
5961

compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/snapshots/SnapshotStateObserverTests.kt

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,44 @@ class SnapshotStateObserverTests {
324324
assertNull(threadException)
325325
}
326326

327+
@Test // regression test for 192677711, second case
328+
fun tryToReproduceSecondRaceCondtion() {
329+
var running = true
330+
var threadException: Exception? = null
331+
try {
332+
thread {
333+
try {
334+
while (running) {
335+
Snapshot.sendApplyNotifications()
336+
}
337+
} catch (e: Exception) {
338+
threadException = e
339+
}
340+
}
341+
342+
for (i in 1..10000) {
343+
val state1 by mutableStateOf(0)
344+
var state2 by mutableStateOf(true)
345+
val observer = SnapshotStateObserver({}).apply {
346+
start()
347+
}
348+
observer.observeReads(Unit, {}) {
349+
repeat(1000) {
350+
@Suppress("UNUSED_EXPRESSION")
351+
state1
352+
if (state2) {
353+
state2 = false
354+
}
355+
}
356+
}
357+
assertNull(threadException)
358+
}
359+
} finally {
360+
running = false
361+
}
362+
assertNull(threadException)
363+
}
364+
327365
private fun runSimpleTest(
328366
block: (modelObserver: SnapshotStateObserver, data: MutableState<Int>) -> Unit
329367
) {

0 commit comments

Comments
 (0)