Merge "Revert "Allow newly created state objects to be accessible in other snapshots"" into androidx-main
diff --git a/compose/runtime/runtime/api/current.txt b/compose/runtime/runtime/api/current.txt
index 0163760..886c585 100644
--- a/compose/runtime/runtime/api/current.txt
+++ b/compose/runtime/runtime/api/current.txt
@@ -864,14 +864,12 @@
property public abstract boolean readOnly;
property public abstract androidx.compose.runtime.snapshots.Snapshot root;
field public static final androidx.compose.runtime.snapshots.Snapshot.Companion Companion;
- field public static final int PreexistingSnapshotId = 1; // 0x1
}
public static final class Snapshot.Companion {
method public androidx.compose.runtime.snapshots.Snapshot getCurrent();
method public inline <T> T global(kotlin.jvm.functions.Function0<? extends T> block);
method public boolean isApplyObserverNotificationPending();
- method public boolean isInSnapshot();
method public void notifyObjectsInitialized();
method public <T> T observe(optional kotlin.jvm.functions.Function1<java.lang.Object,kotlin.Unit>? readObserver, optional kotlin.jvm.functions.Function1<java.lang.Object,kotlin.Unit>? writeObserver, kotlin.jvm.functions.Function0<? extends T> block);
method @SuppressCompatibility @androidx.compose.runtime.InternalComposeApi public int openSnapshotCount();
@@ -884,7 +882,6 @@
method public inline <T> T withoutReadObservation(kotlin.jvm.functions.Function0<? extends T> block);
property public final androidx.compose.runtime.snapshots.Snapshot current;
property public final boolean isApplyObserverNotificationPending;
- property public final boolean isInSnapshot;
}
public final class SnapshotApplyConflictException extends java.lang.Exception {
diff --git a/compose/runtime/runtime/api/restricted_current.txt b/compose/runtime/runtime/api/restricted_current.txt
index 5240627..d282011 100644
--- a/compose/runtime/runtime/api/restricted_current.txt
+++ b/compose/runtime/runtime/api/restricted_current.txt
@@ -901,7 +901,6 @@
property public abstract boolean readOnly;
property public abstract androidx.compose.runtime.snapshots.Snapshot root;
field public static final androidx.compose.runtime.snapshots.Snapshot.Companion Companion;
- field public static final int PreexistingSnapshotId = 1; // 0x1
}
public static final class Snapshot.Companion {
@@ -909,7 +908,6 @@
method public androidx.compose.runtime.snapshots.Snapshot getCurrent();
method public inline <T> T global(kotlin.jvm.functions.Function0<? extends T> block);
method public boolean isApplyObserverNotificationPending();
- method public boolean isInSnapshot();
method @kotlin.PublishedApi internal androidx.compose.runtime.snapshots.Snapshot makeCurrentNonObservable(androidx.compose.runtime.snapshots.Snapshot? previous);
method public void notifyObjectsInitialized();
method public <T> T observe(optional kotlin.jvm.functions.Function1<java.lang.Object,kotlin.Unit>? readObserver, optional kotlin.jvm.functions.Function1<java.lang.Object,kotlin.Unit>? writeObserver, kotlin.jvm.functions.Function0<? extends T> block);
@@ -927,7 +925,6 @@
property public final androidx.compose.runtime.snapshots.Snapshot current;
property @kotlin.PublishedApi internal final androidx.compose.runtime.snapshots.Snapshot? currentThreadSnapshot;
property public final boolean isApplyObserverNotificationPending;
- property public final boolean isInSnapshot;
}
public final class SnapshotApplyConflictException extends java.lang.Exception {
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotDoubleState.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotDoubleState.kt
index 3354ba0..cdea706 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotDoubleState.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotDoubleState.kt
@@ -130,13 +130,7 @@
value: Double
) : StateObjectImpl(), MutableDoubleState, SnapshotMutableState<Double> {
- private var next = DoubleStateStateRecord(value).also {
- if (Snapshot.isInSnapshot) {
- it.next = DoubleStateStateRecord(value).also { next ->
- next.snapshotId = Snapshot.PreexistingSnapshotId
- }
- }
- }
+ private var next = DoubleStateStateRecord(value)
override val firstStateRecord: StateRecord
get() = next
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotFloatState.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotFloatState.kt
index ff9a419..e5212f1 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotFloatState.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotFloatState.kt
@@ -126,13 +126,7 @@
value: Float
) : StateObjectImpl(), MutableFloatState, SnapshotMutableState<Float> {
- private var next = FloatStateStateRecord(value).also {
- if (Snapshot.isInSnapshot) {
- it.next = FloatStateStateRecord(value).also { next ->
- next.snapshotId = Snapshot.PreexistingSnapshotId
- }
- }
- }
+ private var next = FloatStateStateRecord(value)
override val firstStateRecord: StateRecord
get() = next
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotIntState.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotIntState.kt
index b01dc0d..75a7db0 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotIntState.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotIntState.kt
@@ -129,13 +129,7 @@
value: Int
) : StateObjectImpl(), MutableIntState, SnapshotMutableState<Int> {
- private var next = IntStateStateRecord(value).also {
- if (Snapshot.isInSnapshot) {
- it.next = IntStateStateRecord(value).also { next ->
- next.snapshotId = Snapshot.PreexistingSnapshotId
- }
- }
- }
+ private var next = IntStateStateRecord(value)
override val firstStateRecord: StateRecord
get() = next
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotLongState.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotLongState.kt
index f1270a2..f3b3678 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotLongState.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotLongState.kt
@@ -126,13 +126,7 @@
value: Long
) : StateObjectImpl(), MutableLongState, SnapshotMutableState<Long> {
- private var next = LongStateStateRecord(value).also {
- if (Snapshot.isInSnapshot) {
- it.next = LongStateStateRecord(value).also { next ->
- next.snapshotId = Snapshot.PreexistingSnapshotId
- }
- }
- }
+ private var next = LongStateStateRecord(value)
override val firstStateRecord: StateRecord
get() = next
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotState.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotState.kt
index 9fab395..0bd7bd7 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotState.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotState.kt
@@ -139,13 +139,7 @@
}
}
- private var next: StateStateRecord<T> = StateStateRecord(value).also {
- if (Snapshot.isInSnapshot) {
- it.next = StateStateRecord(value).also { next ->
- next.snapshotId = Snapshot.PreexistingSnapshotId
- }
- }
- }
+ private var next: StateStateRecord<T> = StateStateRecord(value)
override val firstStateRecord: StateRecord
get() = next
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/Snapshot.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/Snapshot.kt
index 5383c37..23e538b 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/Snapshot.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/Snapshot.kt
@@ -298,11 +298,6 @@
val current get() = currentSnapshot()
/**
- * Return `true` if the thread is currently in the context of a snapshot.
- */
- val isInSnapshot: Boolean get() = threadSnapshot.get() != null
-
- /**
* Returns whether any threads are currently in the process of notifying observers about
* changes to the global snapshot.
*/
@@ -310,14 +305,6 @@
get() = pendingApplyObserverCount.get() > 0
/**
- * All new state objects initial state records should be [PreexistingSnapshotId] which then
- * allows snapshots outside the creating snapshot to access the object with its initial
- * state.
- */
- @Suppress("ConstPropertyName")
- const val PreexistingSnapshotId = 1
-
- /**
* Take a snapshot of the current value of all state objects. The values are preserved until
* [Snapshot.dispose] is called on the result.
*
@@ -1021,15 +1008,6 @@
// in a nested snapshot that was committed then changed.
val current = readable(first, snapshotId, invalidSnapshots) ?: return@forEach
val previous = readable(first, id, start) ?: return@forEach
- if (previous.snapshotId == PreexistingSnapshotId) {
- // A previous record might not be found if the state object was created in a
- // nested snapshot that didn't have any other modifications. The `apply()` for
- // a nested snapshot considers such snapshots no-op snapshots and just closes them
- // which allows this object's previous record to be missing or be the record created
- // during initial construction. In these cases taking applied is the right choice
- // this indicates there was no conflicting writes.
- return@forEach
- }
if (current != previous) {
val applied = readable(first, id, this.invalid) ?: readError()
val merged = optimisticMerges?.get(current) ?: run {
@@ -1846,9 +1824,8 @@
*/
private var openSnapshots = SnapshotIdSet.EMPTY
-/** The first snapshot created must be at least on more than the
- * [Snapshot.PreexistingSnapshotId] */
-private var nextSnapshotId = Snapshot.PreexistingSnapshotId + 1
+/** The first snapshot created must be at least on more than the INVALID_SNAPSHOT */
+private var nextSnapshotId = INVALID_SNAPSHOT + 1
/**
* A tracking table for pinned snapshots. A pinned snapshot is the lowest snapshot id that the
@@ -2192,16 +2169,9 @@
// Otherwise, make a copy of the readable data and mark it as born in this snapshot, making it
// writable.
- @Suppress("UNCHECKED_CAST")
- val newData = sync {
- // Verify that some other thread didn't already create this.
- val newReadData = readable(state.firstStateRecord, id, snapshot.invalid) ?: readError()
- if (newReadData.snapshotId == id)
- newReadData
- else newReadData.newWritableRecordLocked(state, snapshot)
- } as T
+ val newData = readData.newWritableRecord(state, snapshot)
- if (readData.snapshotId != Snapshot.PreexistingSnapshotId) snapshot.recordModified(state)
+ snapshot.recordModified(state)
return newData
}
@@ -2222,7 +2192,7 @@
val newData = sync { newOverwritableRecordLocked(state) }
newData.snapshotId = id
- if (candidate.snapshotId != Snapshot.PreexistingSnapshotId) snapshot.recordModified(state)
+ snapshot.recordModified(state)
return newData
}
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateList.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateList.kt
index c661d37..22fda4b 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateList.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateList.kt
@@ -33,15 +33,7 @@
@Stable
class SnapshotStateList<T> : StateObject, MutableList<T>, RandomAccess {
override var firstStateRecord: StateRecord =
- persistentListOf<T>().let { list ->
- StateListStateRecord(list).also {
- if (Snapshot.isInSnapshot) {
- it.next = StateListStateRecord(list).also { next ->
- next.snapshotId = Snapshot.PreexistingSnapshotId
- }
- }
- }
- }
+ StateListStateRecord<T>(persistentListOf())
private set
override fun prependStateRecord(value: StateRecord) {
@@ -221,7 +213,7 @@
oldList = current.list
}
val newList = block(oldList!!)
- if (newList === oldList) {
+ if (newList == oldList) {
result = false
break
}
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateMap.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateMap.kt
index 42701db..0f015a4 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateMap.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateMap.kt
@@ -33,15 +33,7 @@
@Stable
class SnapshotStateMap<K, V> : StateObject, MutableMap<K, V> {
override var firstStateRecord: StateRecord =
- persistentHashMapOf<K, V>().let { map ->
- StateMapStateRecord(map).also {
- if (Snapshot.isInSnapshot) {
- it.next = StateMapStateRecord(map).also { next ->
- next.snapshotId = Snapshot.PreexistingSnapshotId
- }
- }
- }
- }
+ StateMapStateRecord<K, V>(persistentHashMapOf())
private set
override fun prependStateRecord(value: StateRecord) {
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateListTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateListTests.kt
index c662be9..9d6add5 100644
--- a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateListTests.kt
+++ b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateListTests.kt
@@ -587,7 +587,7 @@
repeat(100) { index ->
repeat(10) {
- assertTrue(list.contains(index * 100 + it), "Missing ${index * 100 + it}")
+ assertTrue(list.contains(index * 100 + it))
}
}
}
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotTests.kt
index 687e8d3..340b2b7 100644
--- a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotTests.kt
+++ b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotTests.kt
@@ -22,12 +22,6 @@
import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableDoubleStateOf
-import androidx.compose.runtime.mutableFloatStateOf
-import androidx.compose.runtime.mutableIntStateOf
-import androidx.compose.runtime.mutableLongStateOf
-import androidx.compose.runtime.mutableStateListOf
-import androidx.compose.runtime.mutableStateMapOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.neverEqualPolicy
import androidx.compose.runtime.referentialEqualityPolicy
@@ -43,7 +37,6 @@
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertFalse
-import kotlin.test.assertNotEquals
import kotlin.test.assertNotSame
import kotlin.test.assertSame
import kotlin.test.assertTrue
@@ -1272,47 +1265,6 @@
assertEquals(1, current.writeCount)
}
- @Test
- fun testSnapshotStateIsBornAccessible() {
- fun <T, V> test(create: () -> T, read: (T) -> V, update: (T) -> V) {
- val snapshot = takeMutableSnapshot()
- val created: Any? = null
- val modified = observeChanges(snapshot) {
- val (state, initial) = snapshot.enter {
- val state = create()
- state to read(state)
- }
-
- // Ensure the value is accessible and has its initial state
- assertEquals(initial, read(state))
- val newValue = snapshot.enter {
- update(state)
- }
-
- // Ensure the test actually modified it
- assertNotEquals(initial, newValue)
-
- // Ensure the value still has its initial state
- assertEquals(initial, read(state))
- snapshot.apply().check()
-
- // Ensure the value now has the modified state
- assertEquals(newValue, read(state))
- }
-
- // The object is not considered modified
- assertFalse(created in modified)
- }
-
- test({ mutableStateOf("A") }, { it.value }) { it.value = "B"; "B" }
- test({ mutableIntStateOf(1) }, { it.value }, { it.value = 2; 2 })
- test({ mutableLongStateOf(1L) }, { it.value }, { it.value = 2L; 2L })
- test({ mutableFloatStateOf(1f) }, { it.value }, { it.value = 2f; 2f })
- test({ mutableDoubleStateOf(1.0) }, { it.value }, { it.value = 2.0; 2.0 })
- test({ mutableStateListOf<Int>() }, { it.isEmpty() }, { it.add(1); it.isEmpty() })
- test({ mutableStateMapOf<Int, Int>() }, { it.isEmpty() }, { it[23] = 42; it.isEmpty() })
- }
-
private fun usedRecords(state: StateObject): Int {
var used = 0
var current: StateRecord? = state.firstStateRecord
@@ -1353,20 +1305,6 @@
return changes
}
-internal fun observeChanges(snapshot: Snapshot, block: () -> Unit): Set<Any> {
- var changes = setOf<Any>()
- val removeObserver = Snapshot.registerApplyObserver { states, changedSnapshot ->
- if (changedSnapshot == snapshot) changes = states
- }
- try {
- block()
- Snapshot.sendApplyNotifications()
- } finally {
- removeObserver.dispose()
- }
- return changes
-}
-
internal fun readsOf(block: () -> Unit): Int {
var reads = 0
val snapshot = takeSnapshot(readObserver = { reads++ })