Skip to content

Commit

Permalink
fix: compose ios concurrent modification fix for 1.9 (#249)
Browse files Browse the repository at this point in the history
  • Loading branch information
DevSrSouza authored Nov 8, 2023
1 parent acad7f0 commit e78f6a6
Show file tree
Hide file tree
Showing 7 changed files with 110 additions and 100 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ public object ScreenModelStore : ScreenDisposable {

private fun Map<String, *>.onEachHolder(holderKey: String, block: (String) -> Unit) =
toMap() // copy
.asSequence()
.filter { it.key.startsWith(holderKey) }
.map { it.key }
.forEach(block)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,12 @@ package cafe.adriel.voyager.core.concurrent
import kotlinx.atomicfu.locks.SynchronizedObject
import kotlinx.atomicfu.locks.synchronized

public actual class ThreadSafeList<T>(
public actual class ThreadSafeList<T> internal constructor(
private val syncObject: SynchronizedObject,
private val delegate: MutableList<T>
) : MutableList<T> {
) : MutableList<T>, ThreadSafeMutableCollection<T>(syncObject, delegate) {
public actual constructor() : this(delegate = mutableListOf())
private val syncObject = SynchronizedObject()

override val size: Int
get() = delegate.size

override fun contains(element: T): Boolean {
return synchronized(syncObject) { delegate.contains(element) }
}

override fun containsAll(elements: Collection<T>): Boolean {
return synchronized(syncObject) { delegate.containsAll(elements) }
}
public constructor(delegate: MutableList<T>) : this(SynchronizedObject(), delegate)

override fun get(index: Int): T {
return synchronized(syncObject) { delegate.get(index) }
Expand All @@ -28,22 +18,10 @@ public actual class ThreadSafeList<T>(
return synchronized(syncObject) { delegate.indexOf(element) }
}

override fun isEmpty(): Boolean {
return synchronized(syncObject) { delegate.isEmpty() }
}

override fun iterator(): MutableIterator<T> {
return synchronized(syncObject) { delegate.iterator() }
}

override fun lastIndexOf(element: T): Int {
return synchronized(syncObject) { delegate.lastIndexOf(element) }
}

override fun add(element: T): Boolean {
return synchronized(syncObject) { delegate.add(element) }
}

override fun add(index: Int, element: T) {
return synchronized(syncObject) { delegate.add(index, element) }
}
Expand All @@ -52,43 +30,23 @@ public actual class ThreadSafeList<T>(
return synchronized(syncObject) { delegate.addAll(index, elements) }
}

override fun addAll(elements: Collection<T>): Boolean {
return synchronized(syncObject) { delegate.addAll(elements) }
}

override fun clear() {
return synchronized(syncObject) { delegate.clear() }
}

override fun listIterator(): MutableListIterator<T> {
return synchronized(syncObject) { delegate.listIterator() }
return synchronized(syncObject) { ThreadSafeMutableListIterator(syncObject, delegate.listIterator()) }
}

override fun listIterator(index: Int): MutableListIterator<T> {
return synchronized(syncObject) { delegate.listIterator(index) }
}

override fun remove(element: T): Boolean {
return synchronized(syncObject) { delegate.remove(element) }
}

override fun removeAll(elements: Collection<T>): Boolean {
return synchronized(syncObject) { delegate.removeAll(elements) }
return synchronized(syncObject) { ThreadSafeMutableListIterator(syncObject, delegate.listIterator(index)) }
}

override fun removeAt(index: Int): T {
return synchronized(syncObject) { delegate.removeAt(index) }
}

override fun retainAll(elements: Collection<T>): Boolean {
return synchronized(syncObject) { delegate.retainAll(elements) }
}

override fun set(index: Int, element: T): T {
return synchronized(syncObject) { delegate.set(index, element) }
}

override fun subList(fromIndex: Int, toIndex: Int): MutableList<T> {
return synchronized(syncObject) { delegate.subList(fromIndex, toIndex) }
return synchronized(syncObject) { ThreadSafeList(syncObject, delegate.subList(fromIndex, toIndex)) }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ public actual class ThreadSafeMap<K, V>(
}

override val entries: MutableSet<MutableMap.MutableEntry<K, V>>
get() = synchronized(syncObject) { delegate.entries.toMutableSet() }
get() = synchronized(syncObject) { ThreadSafeSet(syncObject, delegate.entries) }
override val keys: MutableSet<K>
get() = synchronized(syncObject) { delegate.keys.toMutableSet() }
get() = synchronized(syncObject) { ThreadSafeSet(syncObject, delegate.keys) }
override val values: MutableCollection<V>
get() = synchronized(syncObject) { delegate.values.toMutableList() }
get() = synchronized(syncObject) { ThreadSafeMutableCollection(syncObject, delegate.values) }

override fun clear() {
synchronized(syncObject) { delegate.clear() }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package cafe.adriel.voyager.core.concurrent

import kotlinx.atomicfu.locks.SynchronizedObject
import kotlinx.atomicfu.locks.synchronized

public open class ThreadSafeMutableCollection<T>internal constructor(
private val syncObject: SynchronizedObject,
private val delegate: MutableCollection<T>
) : MutableCollection<T> {

override val size: Int
get() = synchronized(syncObject) { delegate.size }

override fun contains(element: T): Boolean {
return synchronized(syncObject) { delegate.contains(element) }
}

override fun containsAll(elements: Collection<T>): Boolean {
return synchronized(syncObject) { delegate.containsAll(elements) }
}

override fun isEmpty(): Boolean {
return synchronized(syncObject) { delegate.isEmpty() }
}

override fun iterator(): MutableIterator<T> {
return synchronized(syncObject) { ThreadSafeMutableIterator(syncObject, delegate.iterator()) }
}

override fun add(element: T): Boolean {
return synchronized(syncObject) { delegate.add(element) }
}

override fun addAll(elements: Collection<T>): Boolean {
return synchronized(syncObject) { delegate.addAll(elements) }
}

override fun clear() {
return synchronized(syncObject) { delegate.clear() }
}

override fun remove(element: T): Boolean {
return synchronized(syncObject) { delegate.remove(element) }
}

override fun removeAll(elements: Collection<T>): Boolean {
return synchronized(syncObject) { delegate.removeAll(elements) }
}

override fun retainAll(elements: Collection<T>): Boolean {
return synchronized(syncObject) { delegate.retainAll(elements) }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package cafe.adriel.voyager.core.concurrent

import kotlinx.atomicfu.locks.SynchronizedObject
import kotlinx.atomicfu.locks.synchronized

internal open class ThreadSafeMutableIterator<E>(
private val syncObject: SynchronizedObject,
private val delegate: MutableIterator<E>
) : MutableIterator<E> {
override fun hasNext(): Boolean = synchronized(syncObject) { delegate.hasNext() }

override fun next(): E = synchronized(syncObject) { delegate.next() }

override fun remove() {
synchronized(syncObject) { delegate.remove() }
}
}

internal class ThreadSafeMutableListIterator<E>(
private val syncObject: SynchronizedObject,
private val delegate: MutableListIterator<E>
) : ThreadSafeMutableIterator<E>(syncObject, delegate),
MutableListIterator<E> {

override fun hasPrevious(): Boolean = synchronized(syncObject) { delegate.hasPrevious() }

override fun nextIndex(): Int = synchronized(syncObject) { delegate.nextIndex() }

override fun previous(): E = synchronized(syncObject) { delegate.previous() }

override fun previousIndex(): Int = synchronized(syncObject) { delegate.previousIndex() }

override fun add(element: E) {
synchronized(syncObject) { delegate.add(element) }
}

override fun set(element: E) {
synchronized(syncObject) { delegate.set(element) }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,51 +4,9 @@ import kotlinx.atomicfu.locks.SynchronizedObject
import kotlinx.atomicfu.locks.synchronized

public actual class ThreadSafeSet<T>(
private val delegate: MutableSet<T>
) : MutableSet<T> {
syncObject: SynchronizedObject,
delegate: MutableSet<T>
) : MutableSet<T>, ThreadSafeMutableCollection<T>(syncObject, delegate) {
public actual constructor() : this(delegate = mutableSetOf())
private val syncObject = SynchronizedObject()

override val size: Int
get() = delegate.size

override fun contains(element: T): Boolean {
return synchronized(syncObject) { delegate.contains(element) }
}

override fun containsAll(elements: Collection<T>): Boolean {
return synchronized(syncObject) { delegate.containsAll(elements) }
}

override fun isEmpty(): Boolean {
return synchronized(syncObject) { delegate.isEmpty() }
}

override fun iterator(): MutableIterator<T> {
return synchronized(syncObject) { delegate.iterator() }
}

override fun add(element: T): Boolean {
return synchronized(syncObject) { delegate.add(element) }
}

override fun addAll(elements: Collection<T>): Boolean {
return synchronized(syncObject) { delegate.addAll(elements) }
}

override fun clear() {
return synchronized(syncObject) { delegate.clear() }
}

override fun remove(element: T): Boolean {
return synchronized(syncObject) { delegate.remove(element) }
}

override fun removeAll(elements: Collection<T>): Boolean {
return synchronized(syncObject) { delegate.removeAll(elements) }
}

override fun retainAll(elements: Collection<T>): Boolean {
return synchronized(syncObject) { delegate.retainAll(elements) }
}
public constructor(delegate: MutableSet<T>) : this(SynchronizedObject(), delegate)
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import cafe.adriel.voyager.core.lifecycle.MultipleProvideBeforeScreenContent
import cafe.adriel.voyager.core.lifecycle.ScreenLifecycleStore
import cafe.adriel.voyager.core.lifecycle.getNavigatorScreenLifecycleProvider
import cafe.adriel.voyager.core.lifecycle.rememberScreenLifecycleOwner
import cafe.adriel.voyager.core.model.ScreenModelStore
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.core.stack.Stack
import cafe.adriel.voyager.core.stack.toMutableStateStack
Expand Down Expand Up @@ -179,7 +178,8 @@ public class Navigator @InternalVoyagerApi constructor(
) {
ScreenLifecycleStore.remove(screen)
stateKeys
.toMutableSet() // Copy
.toSet() // Copy
.asSequence()
.filter { it.startsWith(screen.key) }
.forEach { key ->
stateHolder.removeState(key)
Expand Down

0 comments on commit e78f6a6

Please sign in to comment.