![JAR search and dependency download from the Maven repository](/logo.png)
.kovenant.kovenant-ui.3.3.0.source-code.cache-jvm.kt Maven / Gradle / Ivy
/*
* Copyright (c) 2015 Mark Platvoet
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* THE SOFTWARE.
*/
package nl.komponents.kovenant.ui
import java.lang.ref.WeakReference
import java.util.concurrent.atomic.AtomicReference
/**
* A special kind of weak reference cache. The key is weakly referenced and if,
* during iteration, we come across a cleared key the *whole* cache gets invalidated.
*
* Key lookup is always done by iteration. So best performance is achieved when number of elements
* are kept to a minimum and it is expected that keys are reasonably long lived.
*
* Iteration is done without introducing garbage opposed to the standard JVM concurrent structures.
*
* Not fully documented yet as this might change in the future
*/
class WeakReferenceCache(private val factory: (K) -> V) {
private val head = AtomicReference>(null)
/**
* Gets the value for specified key. If the key is not present a value will be created and added
* to this cache. Does not guard against duplicate creation and also duplicate keys.
*
* @return the value for this key
*/
operator fun get(key: K): V {
iterate {
k, v ->
if (k == key) return v
}
val value = factory(key)
add(key, value)
return value
}
private class CacheNode(key: K, val value: V) {
val next = AtomicReference>(null)
private val keyRef = WeakReference(key)
val key: K? get() = keyRef.get()
}
// Let the Storm Troopers that call themselves Software Craftsmen go berserk
// over this next piece. So, for Jedi eyes only. ;-)
//
// This does not only iterate but also clears the cache if we hit
// a cleared reference.
private inline fun iterate(fn: (K, V) -> Unit) {
val headNode = head.get()
if (headNode != null) {
var node = headNode
while (true) {
val key = node.key
if (key == null) {
// one of the cache items is null
// discard the whole cache and rebuild
head.set(null)
break
}
fn(key, node.value)
val next = node.next.get()
if (next == null) break
node = next
}
}
}
private fun add(key: K, value: V) {
add(CacheNode(key, value))
}
private fun add(node: CacheNode) {
while (true) {
val headNode = head.get()
if (headNode == null) {
if (head.compareAndSet(null, node)) break
} else {
do {
val tail = tailNode(headNode)
} while (!tail.next.compareAndSet(null, node))
break
}
}
}
private fun tailNode(head: CacheNode): CacheNode {
var tail = head
while (true) {
val next = tail.next.get()
if (next == null) {
return tail
}
tail = next
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy