summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/main/java/org/bukkit/craftbukkit/CraftChunk.java13
-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();
+ }
+ }
}