Skip to content

Commit

Permalink
Fix another race condition in the SnapshotStateObserver
Browse files Browse the repository at this point in the history
Adding observed objects was not syncrhonized with
removing objects causing a perioudic NPE.

Fixes: 192677711
Test: ./gradlew :compose:r:r:tDUT
Change-Id: I6d716afa248624b125119219fe892e02282c39d9
  • Loading branch information
chuckjaz committed Jul 12, 2021
1 parent 0399731 commit efca56e
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ class SnapshotStateObserver(private val onChangedExecutor: (callback: () -> Unit
*/
private val readObserver: (Any) -> Unit = { state ->
if (!isPaused) {
currentMap!!.addValue(state)
synchronized(applyMaps) {
currentMap!!.addValue(state)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,44 @@ class SnapshotStateObserverTests {
assertNull(threadException)
}

@Test // regression test for 192677711, second case
fun tryToReproduceSecondRaceCondtion() {
var running = true
var threadException: Exception? = null
try {
thread {
try {
while (running) {
Snapshot.sendApplyNotifications()
}
} catch (e: Exception) {
threadException = e
}
}

for (i in 1..10000) {
val state1 by mutableStateOf(0)
var state2 by mutableStateOf(true)
val observer = SnapshotStateObserver({}).apply {
start()
}
observer.observeReads(Unit, {}) {
repeat(1000) {
@Suppress("UNUSED_EXPRESSION")
state1
if (state2) {
state2 = false
}
}
}
assertNull(threadException)
}
} finally {
running = false
}
assertNull(threadException)
}

private fun runSimpleTest(
block: (modelObserver: SnapshotStateObserver, data: MutableState<Int>) -> Unit
) {
Expand Down

0 comments on commit efca56e

Please sign in to comment.