Last active
February 27, 2021 20:13
-
-
Save SecretX33/0c4da3bf3090ab0ad1f29fb9e148c680 to your computer and use it in GitHub Desktop.
Observable Synchronized List
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class ObservableSyncList<T>(list: MutableList<T> = ArrayList()) : SyncList<T>(list), ListListener { | |
override val observers: MutableMap<UUID, SilentObserver> = ConcurrentHashMap() | |
override fun add(element: T) = super.add(element).also { notifyObservers() } | |
override fun addAll(elements: Collection<T>) = super.addAll(elements).also { notifyObservers() } | |
override fun set(index: Int, element: T): T = super.set(index, element).also { notifyObservers() } | |
override fun setAll(list: List<T>) = super.setAll(list).also { notifyObservers() } | |
override fun removeAt(index: Int): T = super.removeAt(index).also { notifyObservers() } | |
override fun removeAll(elements: Collection<T>) = super.removeAll(elements).also { notifyObservers() } | |
} | |
open class SyncList<T>(private val list: MutableList<T> = ArrayList()): MutableList<T> by list { | |
private val lock = Semaphore(1, true) | |
fun lock() = lock.acquire() | |
fun unlock() = lock.release() | |
fun getAll(): List<T> = lockAndRun { toList() } | |
open fun setAll(list: List<T>) = lockAndRun { | |
this.list.clear() | |
this.list.addAll(list) | |
} | |
fun runSync(block: SyncList<T>.() -> Unit): SyncList<T> { | |
lockAndRun { block() } | |
return this | |
} | |
private fun <T> lockAndRun(func: () -> T): T { | |
try { | |
lock.acquire() | |
return func.invoke() | |
} finally { | |
lock.release() | |
} | |
} | |
} | |
interface ListListener { | |
val observers: MutableMap<UUID, SilentObserver> | |
fun notifyObservers() = observers.forEach { it.value.onChange() } | |
fun addListener(observer: SilentObserver): ListSubscription { | |
var uuid = UUID.randomUUID() | |
while (observers.putIfAbsent(uuid, observer) != null) { | |
Thread.sleep(1) | |
uuid = UUID.randomUUID() | |
} | |
return ListSubscription(uuid, this) | |
} | |
fun unsubscribeListener(uuid: UUID) = observers.remove(uuid) | |
fun unsubscribeAll() = observers.clear() | |
} | |
fun interface SilentObserver { | |
fun onChange() | |
} | |
class ListSubscription(private val uuid: UUID, listener: ListListener) { | |
private var listener: ListListener? = listener | |
fun dispose() { | |
listener?.unsubscribeListener(uuid) | |
listener = null | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Do one of those two things before using the ObservableSyncList:
Lock it with
list.lock()
beforehand, do your stuff with it, and when you're done unblock it usinglist.unlock()
. When iterating through it, uselist.getAll()
to get a copy of the current list.Use the provided lambda function
list.runSync{...}
that automatically locks the list, does the operations inside the lambda, and unlocks it automatically when they're done.