diff --git a/api/kweb-core.api b/api/kweb-core.api index 9ae282926..48f80355b 100644 --- a/api/kweb-core.api +++ b/api/kweb-core.api @@ -1850,7 +1850,7 @@ public class kweb/state/KVal : java/lang/AutoCloseable { public final fun close (Lkweb/state/CloseReason;)V protected final fun finalize ()V protected final fun getCloseReason ()Lkweb/state/CloseReason; - protected final fun getListeners ()Ljava/util/concurrent/ConcurrentHashMap; + protected final fun getListeners ()Lcom/google/common/cache/Cache; public fun getValue ()Ljava/lang/Object; public final fun map (Lkotlin/jvm/functions/Function1;)Lkweb/state/KVal; public final fun onClose (Lkotlin/jvm/functions/Function0;)V diff --git a/src/main/kotlin/kweb/state/KVal.kt b/src/main/kotlin/kweb/state/KVal.kt index 4d04c87a0..fc4a6ee91 100755 --- a/src/main/kotlin/kweb/state/KVal.kt +++ b/src/main/kotlin/kweb/state/KVal.kt @@ -1,5 +1,7 @@ package kweb.state +import com.google.common.cache.Cache +import com.google.common.cache.CacheBuilder import kweb.util.random import mu.two.KotlinLogging import java.util.concurrent.ConcurrentHashMap @@ -18,7 +20,11 @@ open class KVal(value: T) : AutoCloseable{ internal val isClosed get() = closeReason != null - protected val listeners = ConcurrentHashMap Unit>() + protected val listeners : Cache Unit> = CacheBuilder.newBuilder() + // A listener shouldn't cause data to be retained that wouldn't otherwise be + // retained + .softValues() + .build() private val closeHandlers = ConcurrentLinkedDeque<() -> Unit>() /** @@ -27,7 +33,7 @@ open class KVal(value: T) : AutoCloseable{ fun addListener(listener: (T, T) -> Unit): Long { verifyNotClosed("add a listener") val handle = random.nextLong() - listeners[handle] = listener + listeners.put(handle, listener) return handle } @@ -50,7 +56,7 @@ open class KVal(value: T) : AutoCloseable{ * changes. */ fun removeListener(handle: Long) { - listeners.remove(handle) + listeners.invalidate(handle) } /** @@ -70,7 +76,7 @@ open class KVal(value: T) : AutoCloseable{ logger.debug("Updating mapped $value to $new") val mappedValue = mapper(new) mappedKVal.pValue = mappedValue - mappedKVal.listeners.values.forEach { listener -> + mappedKVal.listeners.asMap().values.forEach { listener -> try { val mappedOld = mapper(old) if (mappedOld != mappedValue) { diff --git a/src/main/kotlin/kweb/state/KVar.kt b/src/main/kotlin/kweb/state/KVar.kt index cc28603ad..b98ab8148 100755 --- a/src/main/kotlin/kweb/state/KVar.kt +++ b/src/main/kotlin/kweb/state/KVar.kt @@ -28,7 +28,7 @@ class KVar(initialValue: T) : KVal(initialValue) { override var value: T by Delegates.observable(initialValue) { _, old, new -> if (old != new) { verifyNotClosed("modify KVar.value") - listeners.values.forEach { listener -> + listeners.asMap().values.forEach { listener -> try { listener(old, new) } catch (e: Exception) {