Fork of Poseidon providing Bukkit #1060 to older Beta versions (b1.0-b1.7.3)
at develop 267 lines 7.9 kB view raw
1package org.bukkit.craftbukkit.util; 2 3import com.google.common.collect.MapMaker; 4 5import java.lang.ref.ReferenceQueue; 6import java.lang.ref.SoftReference; 7import java.util.*; 8import java.util.concurrent.ConcurrentHashMap; 9 10/** 11 * Creates a map that uses soft reference. This indicates to the garbage collector 12 * that they can be removed if necessary 13 * <p> 14 * A minimum number of strong references can be set. These most recent N objects added 15 * to the map will not be removed by the garbage collector. 16 * <p> 17 * Objects will never be removed if they are referenced strongly from somewhere else 18 * <p> 19 * Note: While data corruption won't happen, the garbage collector is potentially async 20 * This could lead to the return values from containsKey() and similar methods being 21 * out of date by the time they are used. The class could return null when the object 22 * is retrieved by a .get() call directly after a .containsKey() call returned true 23 * 24 * @author raphfrk 25 * @deprecated Use {@link MapMaker} to create a concurrent soft-reference map, this class is inefficient and will be removed 26 */ 27 28@Deprecated 29public class ConcurrentSoftMap<K, V> { 30 31 private final ConcurrentHashMap<K, SoftMapReference<K, V>> map = new ConcurrentHashMap<K, SoftMapReference<K, V>>(); 32 private final ReferenceQueue<SoftMapReference> queue = new ReferenceQueue<SoftMapReference>(); 33 private final LinkedList<V> strongReferenceQueue = new LinkedList<V>(); 34 private final int strongReferenceSize; 35 36 public ConcurrentSoftMap() { 37 this(20); 38 } 39 40 public ConcurrentSoftMap(int size) { 41 strongReferenceSize = size; 42 } 43 44 // When a soft reference is deleted by the garbage collector, it is set to reference null 45 // and added to the queue 46 // 47 // However, these null references still exist in the ConcurrentHashMap as keys. This method removes these keys. 48 // 49 // It is called whenever there is a method call of the map. 50 51 private void emptyQueue() { 52 SoftMapReference ref; 53 54 while ((ref = (SoftMapReference) queue.poll()) != null) { 55 map.remove(ref.key); 56 } 57 } 58 59 public void clear() { 60 synchronized (strongReferenceQueue) { 61 strongReferenceQueue.clear(); 62 } 63 map.clear(); 64 emptyQueue(); 65 } 66 67 // Shouldn't support this, since the garbage collection is async 68 69 public boolean containsKey(K key) { 70 emptyQueue(); 71 return map.containsKey(key); 72 } 73 74 // Shouldn't support this, since the garbage collection is async 75 76 public boolean containsValue(V value) { 77 emptyQueue(); 78 return map.containsValue(value); 79 } 80 81 // Shouldn't support this since it would create strong references to all the entries 82 83 public Set entrySet() { 84 emptyQueue(); 85 throw new UnsupportedOperationException("SoftMap does not support this operation, since it creates potentially stong references"); 86 } 87 88 // Doesn't support these either 89 90 public boolean equals(Object o) { 91 emptyQueue(); 92 throw new UnsupportedOperationException("SoftMap doesn't support equals checks"); 93 } 94 95 // This operation returns null if the entry is not in the map 96 97 public V get(K key) { 98 emptyQueue(); 99 return fastGet(key); 100 } 101 102 private V fastGet(K key) { 103 SoftMapReference<K, V> ref = map.get(key); 104 105 if (ref == null) { 106 return null; 107 } 108 V value = ref.get(); 109 110 if (value != null) { 111 synchronized (strongReferenceQueue) { 112 strongReferenceQueue.addFirst(value); 113 if (strongReferenceQueue.size() > strongReferenceSize) { 114 strongReferenceQueue.removeLast(); 115 } 116 } 117 } 118 return value; 119 } 120 121 // Doesn't support this either 122 123 public int hashCode() { 124 emptyQueue(); 125 throw new UnsupportedOperationException("SoftMap doesn't support hashCode"); 126 } 127 128 // This is another risky method, since again, garbage collection is async 129 130 public boolean isEmpty() { 131 emptyQueue(); 132 return map.isEmpty(); 133 } 134 135 // Return all the keys, again could go out of date 136 137 public Set keySet() { 138 emptyQueue(); 139 return map.keySet(); 140 } 141 142 // Adds the mapping to the map 143 144 public V put(K key, V value) { 145 emptyQueue(); 146 V old = fastGet(key); 147 fastPut(key, value); 148 return old; 149 } 150 151 private void fastPut(K key, V value) { 152 map.put(key, new SoftMapReference<K, V>(key, value, queue)); 153 synchronized (strongReferenceQueue) { 154 strongReferenceQueue.addFirst(value); 155 if (strongReferenceQueue.size() > strongReferenceSize) { 156 strongReferenceQueue.removeLast(); 157 } 158 } 159 } 160 161 public V putIfAbsent(K key, V value) { 162 emptyQueue(); 163 return fastPutIfAbsent(key, value); 164 } 165 166 private V fastPutIfAbsent(K key, V value) { 167 V ret = null; 168 169 if (map.containsKey(key)) { 170 SoftMapReference<K, V> current = map.get(key); 171 172 if (current != null) { 173 ret = current.get(); 174 } 175 } 176 177 if (ret == null) { 178 SoftMapReference<K, V> newValue = new SoftMapReference<K, V>(key, value, queue); 179 boolean success = false; 180 181 while (!success) { 182 SoftMapReference<K, V> oldValue = map.putIfAbsent(key, newValue); 183 184 if (oldValue == null) { // put was successful (key didn't exist) 185 ret = null; 186 success = true; 187 } else { 188 ret = oldValue.get(); 189 if (ret == null) { // key existed, but referenced null 190 success = map.replace(key, oldValue, newValue); // try to swap old for new 191 } else { // key existed, and referenced a valid object 192 success = true; 193 } 194 } 195 } 196 } 197 198 if (ret == null) { 199 synchronized (strongReferenceQueue) { 200 strongReferenceQueue.addFirst(value); 201 if (strongReferenceQueue.size() > strongReferenceSize) { 202 strongReferenceQueue.removeLast(); 203 } 204 } 205 } 206 207 return ret; 208 } 209 210 // Adds the mappings to the map 211 212 public void putAll(Map other) { 213 emptyQueue(); 214 Iterator<K> itr = other.keySet().iterator(); 215 while (itr.hasNext()) { 216 K key = itr.next(); 217 fastPut(key, (V) other.get(key)); 218 } 219 } 220 221 // Remove object 222 223 public V remove(K key) { 224 emptyQueue(); 225 SoftMapReference<K, V> ref = map.remove(key); 226 227 if (ref != null) { 228 return ref.get(); 229 } 230 return null; 231 } 232 233 // Returns size, could go out of date 234 235 public int size() { 236 emptyQueue(); 237 return map.size(); 238 } 239 240 // Shouldn't support this since it would create strong references to all the entries 241 242 public Collection values() { 243 emptyQueue(); 244 throw new UnsupportedOperationException("SoftMap does not support this operation, since it creates potentially stong references"); 245 } 246 247 private static class SoftMapReference<K, V> extends SoftReference<V> { 248 K key; 249 250 SoftMapReference(K key, V value, ReferenceQueue queue) { 251 super(value, queue); 252 this.key = key; 253 } 254 255 @Override 256 public boolean equals(Object o) { 257 if (o == null) { 258 return false; 259 } 260 if (!(o instanceof SoftMapReference)) { 261 return false; 262 } 263 SoftMapReference other = (SoftMapReference) o; 264 return other.get() == get(); 265 } 266 } 267}