diff options
-rw-r--r-- | src/main/java/org/bukkit/craftbukkit/CraftChunk.java | 13 | ||||
-rw-r--r-- | src/main/java/org/bukkit/craftbukkit/util/ConcurrentSoftMap.java (renamed from src/main/java/org/bukkit/craftbukkit/util/SoftMap.java) | 112 |
2 files changed, 97 insertions, 28 deletions
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java index 9c19a218..65b56472 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java @@ -13,11 +13,11 @@ import org.bukkit.block.Block; import org.bukkit.block.BlockState; import org.bukkit.craftbukkit.block.CraftBlock; import org.bukkit.entity.Entity; -import org.bukkit.craftbukkit.util.SoftMap; +import org.bukkit.craftbukkit.util.ConcurrentSoftMap; public class CraftChunk implements Chunk { private WeakReference<net.minecraft.server.Chunk> weakChunk; - private final SoftMap<Integer, Block> cache = new SoftMap<Integer, Block>(); + private final ConcurrentSoftMap<Integer, Block> cache = new ConcurrentSoftMap<Integer, Block>(); private WorldServer worldServer; private int x; private int z; @@ -63,8 +63,13 @@ public class CraftChunk implements Chunk { int pos = (x & 0xF) << 11 | (z & 0xF) << 7 | (y & 0x7F); Block block = this.cache.get( pos ); if (block == null) { - block = new CraftBlock( this, (getX() << 4) | (x & 0xF), y & 0x7F, (getZ() << 4) | (z & 0xF) ); - this.cache.put( pos, block ); + Block newBlock = new CraftBlock( this, (getX() << 4) | (x & 0xF), y & 0x7F, (getZ() << 4) | (z & 0xF) ); + Block oldBlock = this.cache.put( pos, newBlock ); + if(oldBlock == null) { + block = newBlock; + } else { + block = oldBlock; + } } return block; } diff --git a/src/main/java/org/bukkit/craftbukkit/util/SoftMap.java b/src/main/java/org/bukkit/craftbukkit/util/ConcurrentSoftMap.java index 29d6f76f..0099ae92 100644 --- a/src/main/java/org/bukkit/craftbukkit/util/SoftMap.java +++ b/src/main/java/org/bukkit/craftbukkit/util/ConcurrentSoftMap.java @@ -3,7 +3,7 @@ package org.bukkit.craftbukkit.util; import java.util.Map; import java.util.AbstractMap; import java.util.LinkedList; -import java.util.HashMap; +import java.util.concurrent.ConcurrentHashMap; import java.util.Set; import java.util.Collection; import java.util.Iterator; @@ -13,52 +13,54 @@ 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 + * that they can be removed if necessary * - * A minimum number of strong references can be set. These most recent N objects added + * 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 + * 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 * * @author raphfrk */ -public class SoftMap<K,V> { +public class ConcurrentSoftMap<K,V> { - private final HashMap<K,SoftMapReference<K,V>> map = new HashMap<K,SoftMapReference<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 SoftMap() { + public ConcurrentSoftMap() { this(20); } - public SoftMap(int size) { + 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 HashMap as keys. This method removes these keys. - // + // 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) { + while ((ref=(SoftMapReference) queue.poll()) != null) { map.remove(ref.key); } } public void clear() { - strongReferenceQueue.clear(); + synchronized(strongReferenceQueue) { + strongReferenceQueue.clear(); + } map.clear(); emptyQueue(); } @@ -105,9 +107,11 @@ public class SoftMap<K,V> { } V value = ref.get(); if (value!=null) { - strongReferenceQueue.addFirst(value); - if (strongReferenceQueue.size() > strongReferenceSize) { - strongReferenceQueue.removeLast(); + synchronized(strongReferenceQueue) { + strongReferenceQueue.addFirst(value); + if (strongReferenceQueue.size() > strongReferenceSize) { + strongReferenceQueue.removeLast(); + } } } return value; @@ -125,7 +129,7 @@ public class SoftMap<K,V> { public boolean isEmpty() { emptyQueue(); return map.isEmpty(); - } + } // Return all the keys, again could go out of date @@ -138,27 +142,76 @@ public class SoftMap<K,V> { public V put(K key, V value) { emptyQueue(); - V old = fastGet(key); + 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)); - strongReferenceQueue.addFirst(value); - if (strongReferenceQueue.size() > strongReferenceSize) { - strongReferenceQueue.removeLast(); + 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()) { + while (itr.hasNext()) { K key = itr.next(); - fastPut(key, (V)other.get(key)); + fastPut(key, (V) other.get(key)); } } @@ -178,7 +231,7 @@ public class SoftMap<K,V> { public int size() { emptyQueue(); return map.size(); - } + } // Shouldn't support this since it would create strong references to all the entries @@ -195,6 +248,17 @@ public class SoftMap<K,V> { 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(); + } + } } |