Skip to content

Commit

Permalink
Expose MutableState directly
Browse files Browse the repository at this point in the history
  • Loading branch information
serras committed Nov 26, 2023
1 parent daed4c9 commit bef48fe
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 68 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,52 +11,8 @@ public interface STM {
if (!condition) retry()
}

public fun <A> TVar<A>.read(): A = variable.value
public fun <A> TVar<A>.write(value: A) { variable.value = value }
public fun <A> TVar<A>.modify(f: (A) -> A) { variable.value = f(variable.value) }

public operator fun <K, V> TMap<K, V>.get(k: K): V? = map[k]
public fun <K, V> TMap<K, V>.lookup(k: K): V? = this[k]

public operator fun <K, V> TMap<K, V>.set(k: K, v: V) { map[k] = v }
public operator fun <K, V> TMap<K, V>.plusAssign(kv: Pair<K, V>) { this[kv.first] = kv.second }
public fun <K, V> TMap<K, V>.insert(k: K, v: V) { this[k] = v }
public fun <K, V> TMap<K, V>.remove(k: K) { map.remove(k) }
public operator fun <K, V> TMap<K, V>.contains(k: K): Boolean = k in map
public fun <K, V> TMap<K, V>.member(k: K): Boolean = k in this
public fun <K, V> TMap<K, V>.update(k: K, f: (V) -> V) {
when (val v = map[k]) {
null -> {}
else -> map[k] = f(v)
}
}

public operator fun <A> TSet<A>.plusAssign(a: A) { map[a] = true }
public fun <A> TSet<A>.insert(a: A) { this += a }
public fun <A> TSet<A>.remove(a: A) { map.remove(a) }
public operator fun <A> TSet<A>.contains(a: A): Boolean = a in map
public fun <A> TSet<A>.member(a: A): Boolean = a in this

public fun <A> TQueue<A>.isEmpty(): Boolean = list.isEmpty()
public fun <A> TQueue<A>.isNotEmpty(): Boolean = !isEmpty()
public fun <A> TQueue<A>.size(): Int = list.size

public fun <A> TQueue<A>.peek(): A = if (list.isEmpty()) retry() else list.first()
public fun <A> TQueue<A>.tryPeek(): A? = list.firstOrNull()
public fun <A> TQueue<A>.read(): A = if (list.isEmpty()) retry() else list.removeFirst()
public fun <A> TQueue<A>.tryRead(): A? = if (list.isEmpty()) null else list.removeFirst()

public operator fun <A> TQueue<A>.plusAssign(a: A) { list.add(a) }
public fun <A> TQueue<A>.write(a: A) { this += a }
public fun <A> TQueue<A>.writeFront(a: A) { list.add(0, a) }

public fun <A> TQueue<A>.removeAll(predicate: (A) -> Boolean) { list.removeAll { !predicate(it) } }

public fun <A> TQueue<A>.flush(): List<A> {
val current = mutableListOf<A>().also { it.addAll(list) }
list.clear()
return current
}
}

public class BlockedIndefinitely : Throwable("Transaction blocked indefinitely")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,39 +8,92 @@ import androidx.compose.runtime.snapshots.SnapshotStateList
import androidx.compose.runtime.snapshots.SnapshotStateMap
import kotlin.jvm.JvmInline

@JvmInline
public value class TVar<A>(internal val variable: MutableState<A>) {
public fun unsafeRead(): A = variable.value
public object TVar {
public fun <A> new(value: A): MutableState<A> = mutableStateOf(value)
}

public companion object {
public fun <A> new(value: A): TVar<A> = TVar(mutableStateOf(value))
}
public fun <A> MutableState<A>.read(): A = value
public fun <A> MutableState<A>.unsafeRead(): A = value
public fun <A> MutableState<A>.write(newValue: A) { value = newValue }
public fun <A> MutableState<A>.modify(transform: (A) -> A) { value = transform(value) }

public object TMap {
public fun <K, V> new(): SnapshotStateMap<K, V> = mutableStateMapOf()
}

@JvmInline
public value class TMap<K, V>(internal val map: SnapshotStateMap<K, V>) {
public companion object {
public fun <K, V> new(): TMap<K, V> = TMap(mutableStateMapOf())
public fun <K, V> SnapshotStateMap<K, V>.insert(key: K, value: V) { this[key] = value }
public fun <K, V> SnapshotStateMap<K, V>.update(key: K, transform: (V) -> V): Unit =
when (val v = this[key]) {
null -> { }
else -> { this[key] = transform(v) }
}

public object TList {
public fun <A> new(): SnapshotStateList<A> = mutableStateListOf()
}

@JvmInline
public value class TSet<A>(internal val map: SnapshotStateMap<A, Boolean>) {
public value class TSet<A>(internal val map: SnapshotStateMap<A, Boolean>): MutableSet<A> {
public companion object {
public fun <A> new(): TSet<A> = TSet(mutableStateMapOf())
}
}

@JvmInline
public value class TList<A>(internal val list: SnapshotStateList<A>) {
public companion object {
public fun <A> new(): TList<A> = TList(mutableStateListOf())
override fun clear() { map.clear() }
override fun add(element: A): Boolean = map.put(element, true) ?: false
override fun addAll(elements: Collection<A>): Boolean {
var added = false
for (element in elements) {
added = added || add(element)
}
return added
}

override fun remove(element: A): Boolean =
map.remove(element) ?: false
override fun retainAll(elements: Collection<A>): Boolean =
remove { it !in elements }
override fun removeAll(elements: Collection<A>): Boolean =
remove { it in elements }
private fun remove(predicate: (A) -> Boolean): Boolean {
var modified = false
for (k in map.keys.filter(predicate)) {
modified = true
map.remove(k)
}
return modified
}

override val size: Int get() = map.size
override fun isEmpty(): Boolean = map.isEmpty()

override fun contains(element: A): Boolean = element in map
override fun containsAll(elements: Collection<A>): Boolean = elements.all { it in map }

override fun iterator(): MutableIterator<A> = map.keys.iterator()
}

@JvmInline
public value class TQueue<A>(internal val list: SnapshotStateList<A>) {
public companion object {
public fun <A> new(): TQueue<A> = TQueue(mutableStateListOf())
}

public fun isEmpty(): Boolean = list.isEmpty()
public fun isNotEmpty(): Boolean = !isEmpty()
public fun size(): Int = list.size

public fun tryPeek(): A? = list.firstOrNull()
public fun tryRead(): A? = if (list.isEmpty()) null else list.removeFirst()

public operator fun plusAssign(a: A) { list.add(a) }
public fun write(a: A) { this += a }
public fun writeFront(a: A) { list.add(0, a) }

public fun removeAll(predicate: (A) -> Boolean) { list.removeAll { !predicate(it) } }

public fun flush(): List<A> {
val current = mutableListOf<A>().also { it.addAll(list) }
list.clear()
return current
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ class STMTest {
delay(20.milliseconds)
atomically { acc1.modify { it + 60 } }
},
{ _, _ -> Unit }
{ _, _ -> }
)
acc1.unsafeRead() shouldBeExactly 50
acc2.unsafeRead() shouldBeExactly 250
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class TMapTest {
checkAll(Arb.int(), Arb.int()) { k, v ->
val map = TMap.new<Int, Int>()
atomically { map.insert(k, v) }
atomically { map.lookup(k) } shouldBe v
atomically { map[k] } shouldBe v
}
}

Expand All @@ -25,7 +25,7 @@ class TMapTest {
for ((k, v) in pairs) map.insert(k, v)
}
atomically {
for ((k, v) in pairs) map.lookup(k) shouldBe v
for ((k, v) in pairs) map[k] shouldBe v
}
}
}
Expand All @@ -37,7 +37,7 @@ class TMapTest {
for ((k, v) in pairs) map.insert(k, v)
}
atomically {
for ((k, v) in pairs) map.lookup(k) shouldBe v
for ((k, v) in pairs) map[k] shouldBe v
}
}
}
Expand All @@ -46,19 +46,19 @@ class TMapTest {
checkAll(Arb.int(), Arb.int()) { k, v ->
val map = TMap.new<Int, Int>()
atomically { map.insert(k, v) }
atomically { map.lookup(k) } shouldBe v
atomically { map[k] } shouldBe v
atomically { map.remove(k) }
atomically { map.lookup(k) } shouldBe null
atomically { map[k] } shouldBe null
}
}

@Test fun update() = runTest {
checkAll(Arb.int(), Arb.int(), Arb.int()) { k, v, g ->
val map = TMap.new<Int, Int>()
atomically { map.insert(k, v) }
atomically { map.lookup(k) } shouldBe v
atomically { map[k] } shouldBe v
atomically { map.update(k) { v + g } }
atomically { map.lookup(k) } shouldBe v + g
atomically { map[k] } shouldBe v + g
}
}
}

0 comments on commit bef48fe

Please sign in to comment.