package org.simantics.db.layer0.variable; import java.util.HashMap; import java.util.Map; import java.util.TreeMap; import gnu.trove.map.hash.TObjectLongHashMap; /** * @author Antti Villberg * * @param * @param * * @since 1.23 */ public class NodeCache { // Expiration time for items in this cache private long defaultExpirationTimeInNs; // Here we hold all nodes with finite expiration times private TreeMap expirationTimes = new TreeMap(); // Finite expiration times for nodes private TObjectLongHashMap exp = new TObjectLongHashMap(10, 0.5f, -1L); // All node values private Map map = new HashMap(); private boolean disposed; public NodeCache() { this(1_000_000_000L); } public NodeCache(long defaultExpirationTimeInNs) { this.defaultExpirationTimeInNs = defaultExpirationTimeInNs; } public synchronized Value get(Node node) { return map.get(node); } public synchronized void clearExpired() { long now = System.nanoTime(); while(!expirationTimes.isEmpty()) { Long first = expirationTimes.firstKey(); if(first < now) { Node node = expirationTimes.remove(first); exp.remove(node); map.remove(node); } else { return; } } } private long scheduleExpiration(Node node, long expiration) { while(expirationTimes.containsKey(expiration)) expiration++; expirationTimes.put(expiration, node); exp.put(node, expiration); return expiration; } private void refreshExpiration(Node node, long newExpiration, boolean existing) { long current = exp.get(node); if(current == -1) { if(existing) { // We have infinite expiration => do nothing } else { // This is a new value if(newExpiration == 0) { // We require infinite expiration => do nothing } else { scheduleExpiration(node, newExpiration); } } return; } // This node is already under expiration tracking if(newExpiration == 0) { // We now want infinite expiration => remove expiration time info expirationTimes.remove(current); exp.remove(node); } else { if(newExpiration > current) { // Update expiration time expirationTimes.remove(current); scheduleExpiration(node, newExpiration); } } } public synchronized void put(Node node, Value value) { if (disposed) return; Value existing = map.put(node, value); refreshExpiration(node, 0, existing != null); } public synchronized void put(Node node, Value value, long expiration) { if (disposed) return; Value existing = map.put(node, value); refreshExpiration(node, System.nanoTime() + expiration, existing != null); } public synchronized void removeListening(Node node) { if (disposed) return; scheduleExpiration(node, System.nanoTime() + defaultExpirationTimeInNs); } public synchronized void remove(Node node) { if (disposed) return; Long expTime = exp.get(node); if (expTime != null) { expirationTimes.remove(expTime); } map.remove(node); } public synchronized void clear() { if (disposed) return; expirationTimes.clear(); exp.clear(); map.clear(); } public synchronized void dispose() { disposed = true; expirationTimes.clear(); exp.clear(); map.clear(); } }