Skip to content

Instantly share code, notes, and snippets.

@SecretX33
Last active February 27, 2021 20:13
Show Gist options
  • Save SecretX33/0c4da3bf3090ab0ad1f29fb9e148c680 to your computer and use it in GitHub Desktop.
Save SecretX33/0c4da3bf3090ab0ad1f29fb9e148c680 to your computer and use it in GitHub Desktop.
Observable Synchronized List
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
}
}
@SecretX33
Copy link
Author

SecretX33 commented Feb 22, 2021

Do one of those two things before using the ObservableSyncList:

  1. Lock it with list.lock() beforehand, do your stuff with it, and when you're done unblock it using list.unlock(). When iterating through it, use list.getAll() to get a copy of the current list.

  2. 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment