summaryrefslogtreecommitdiffstats
path: root/src/main/java/org
diff options
context:
space:
mode:
authorDinnerbone <dinnerbone@dinnerbone.com>2011-07-15 21:49:53 +0100
committerDinnerbone <dinnerbone@dinnerbone.com>2011-07-15 21:49:53 +0100
commitc101c3553bb668ce32c8e4a1e976c93d516815af (patch)
treea6786bed9cf4446d6cc42b680752a341ad3252ca /src/main/java/org
parenta507add4ea94d2000f67285029eb8c3f9298215c (diff)
downloadcraftbukkit-c101c3553bb668ce32c8e4a1e976c93d516815af.tar
craftbukkit-c101c3553bb668ce32c8e4a1e976c93d516815af.tar.gz
craftbukkit-c101c3553bb668ce32c8e4a1e976c93d516815af.tar.lz
craftbukkit-c101c3553bb668ce32c8e4a1e976c93d516815af.tar.xz
craftbukkit-c101c3553bb668ce32c8e4a1e976c93d516815af.zip
Readded ConcurrentSoftMap because apparently some plugins need this. (They really, really shouldn't be using it. At all.)
Diffstat (limited to 'src/main/java/org')
-rw-r--r--src/main/java/org/bukkit/craftbukkit/util/ConcurrentSoftMap.java271
1 files changed, 271 insertions, 0 deletions
diff --git a/src/main/java/org/bukkit/craftbukkit/util/ConcurrentSoftMap.java b/src/main/java/org/bukkit/craftbukkit/util/ConcurrentSoftMap.java
new file mode 100644
index 00000000..40a9934f
--- /dev/null
+++ b/src/main/java/org/bukkit/craftbukkit/util/ConcurrentSoftMap.java
@@ -0,0 +1,271 @@
+package org.bukkit.craftbukkit.util;
+
+import com.google.common.collect.MapMaker;
+import java.util.Map;
+import java.util.LinkedList;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.Set;
+import java.util.Collection;
+import java.util.Iterator;
+
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.SoftReference;
+
+/**
+ * Creates a map that uses soft reference. This indicates to the garbage collector
+ * that they can be removed if necessary
+ *
+ * A minimum number of strong references can be set. These most recent N objects added
+ * to the map will not be removed by the garbage collector.
+ *
+ * Objects will never be removed if they are referenced strongly from somewhere else
+
+ * Note: While data corruption won't happen, the garbage collector is potentially async
+ * This could lead to the return values from containsKey() and similar methods being
+ * out of date by the time they are used. The class could return null when the object
+ * is retrieved by a .get() call directly after a .containsKey() call returned true
+ *
+ * @deprecated Use {@link MapMaker} to create a concurrent soft-reference map, this class is inefficient and will be removed
+ * @author raphfrk
+ */
+
+@Deprecated
+public class ConcurrentSoftMap<K, V> {
+
+ private final ConcurrentHashMap<K, SoftMapReference<K, V>> map = new ConcurrentHashMap<K, SoftMapReference<K, V>>();
+ private final ReferenceQueue<SoftMapReference> queue = new ReferenceQueue<SoftMapReference>();
+ private final LinkedList<V> strongReferenceQueue = new LinkedList<V>();
+ private final int strongReferenceSize;
+
+ public ConcurrentSoftMap() {
+ this(20);
+ }
+
+ public ConcurrentSoftMap(int size) {
+ strongReferenceSize = size;
+ }
+
+ // When a soft reference is deleted by the garbage collector, it is set to reference null
+ // and added to the queue
+ //
+ // However, these null references still exist in the ConcurrentHashMap as keys. This method removes these keys.
+ //
+ // It is called whenever there is a method call of the map.
+
+ private void emptyQueue() {
+ SoftMapReference ref;
+
+ while ((ref = (SoftMapReference) queue.poll()) != null) {
+ map.remove(ref.key);
+ }
+ }
+
+ public void clear() {
+ synchronized (strongReferenceQueue) {
+ strongReferenceQueue.clear();
+ }
+ map.clear();
+ emptyQueue();
+ }
+
+ // Shouldn't support this, since the garbage collection is async
+
+ public boolean containsKey(K key) {
+ emptyQueue();
+ return map.containsKey(key);
+ }
+
+ // Shouldn't support this, since the garbage collection is async
+
+ public boolean containsValue(V value) {
+ emptyQueue();
+ return map.containsValue(value);
+ }
+
+ // Shouldn't support this since it would create strong references to all the entries
+
+ public Set entrySet() {
+ emptyQueue();
+ throw new UnsupportedOperationException("SoftMap does not support this operation, since it creates potentially stong references");
+ }
+
+ // Doesn't support these either
+
+ public boolean equals(Object o) {
+ emptyQueue();
+ throw new UnsupportedOperationException("SoftMap doesn't support equals checks");
+ }
+
+ // This operation returns null if the entry is not in the map
+
+ public V get(K key) {
+ emptyQueue();
+ return fastGet(key);
+ }
+
+ private V fastGet(K key) {
+ SoftMapReference<K, V> ref = map.get(key);
+
+ if (ref == null) {
+ return null;
+ }
+ V value = ref.get();
+
+ if (value != null) {
+ synchronized (strongReferenceQueue) {
+ strongReferenceQueue.addFirst(value);
+ if (strongReferenceQueue.size() > strongReferenceSize) {
+ strongReferenceQueue.removeLast();
+ }
+ }
+ }
+ return value;
+ }
+
+ // Doesn't support this either
+
+ public int hashCode() {
+ emptyQueue();
+ throw new UnsupportedOperationException("SoftMap doesn't support hashCode");
+ }
+
+ // This is another risky method, since again, garbage collection is async
+
+ public boolean isEmpty() {
+ emptyQueue();
+ return map.isEmpty();
+ }
+
+ // Return all the keys, again could go out of date
+
+ public Set keySet() {
+ emptyQueue();
+ return map.keySet();
+ }
+
+ // Adds the mapping to the map
+
+ public V put(K key, V value) {
+ emptyQueue();
+ V old = fastGet(key);
+ fastPut(key, value);
+ return old;
+ }
+
+ private void fastPut(K key, V value) {
+ map.put(key, new SoftMapReference<K, V>(key, value, queue));
+ synchronized (strongReferenceQueue) {
+ strongReferenceQueue.addFirst(value);
+ if (strongReferenceQueue.size() > strongReferenceSize) {
+ strongReferenceQueue.removeLast();
+ }
+ }
+ }
+
+ public V putIfAbsent(K key, V value) {
+ emptyQueue();
+ return fastPutIfAbsent(key, value);
+ }
+
+ private V fastPutIfAbsent(K key, V value) {
+ V ret = null;
+
+ if (map.containsKey(key)) {
+ SoftMapReference<K, V> current = map.get(key);
+
+ if (current != null) {
+ ret = current.get();
+ }
+ }
+
+ if (ret == null) {
+ SoftMapReference<K, V> newValue = new SoftMapReference<K, V>(key, value, queue);
+ boolean success = false;
+
+ while (!success) {
+ SoftMapReference<K, V> oldValue = map.putIfAbsent(key, newValue);
+
+ if (oldValue == null) { // put was successful (key didn't exist)
+ ret = null;
+ success = true;
+ } else {
+ ret = oldValue.get();
+ if (ret == null) { // key existed, but referenced null
+ success = map.replace(key, oldValue, newValue); // try to swap old for new
+ } else { // key existed, and referenced a valid object
+ success = true;
+ }
+ }
+ }
+ }
+
+ if (ret == null) {
+ synchronized (strongReferenceQueue) {
+ strongReferenceQueue.addFirst(value);
+ if (strongReferenceQueue.size() > strongReferenceSize) {
+ strongReferenceQueue.removeLast();
+ }
+ }
+ }
+
+ return ret;
+ }
+
+ // Adds the mappings to the map
+
+ public void putAll(Map other) {
+ emptyQueue();
+ Iterator<K> itr = other.keySet().iterator();
+ while (itr.hasNext()) {
+ K key = itr.next();
+ fastPut(key, (V) other.get(key));
+ }
+ }
+
+ // Remove object
+
+ public V remove(K key) {
+ emptyQueue();
+ SoftMapReference<K, V> ref = map.remove(key);
+
+ if (ref != null) {
+ return ref.get();
+ }
+ return null;
+ }
+
+ // Returns size, could go out of date
+
+ public int size() {
+ emptyQueue();
+ return map.size();
+ }
+
+ // Shouldn't support this since it would create strong references to all the entries
+
+ public Collection values() {
+ emptyQueue();
+ throw new UnsupportedOperationException("SoftMap does not support this operation, since it creates potentially stong references");
+ }
+
+ private static class SoftMapReference<K, V> extends SoftReference<V> {
+ K key;
+
+ SoftMapReference(K key, V value, ReferenceQueue queue) {
+ super(value, queue);
+ this.key = key;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null) {
+ return false;
+ }
+ if (!(o instanceof SoftMapReference)) {
+ return false;
+ }
+ SoftMapReference other = (SoftMapReference) o;
+ return other.get() == get();
+ }
+ }
+}