]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.utils.datastructures/src/org/simantics/utils/datastructures/cache/SoftTimedCache.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.utils.datastructures / src / org / simantics / utils / datastructures / cache / SoftTimedCache.java
index 40f47526417eb6fe592d975e3a65f271b472a1c3..6e9ed074c6317931ab7609e91a088f62b5565e4b 100644 (file)
-/*******************************************************************************\r
- * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
- * in Industry THTH ry.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- *     VTT Technical Research Centre of Finland - initial API and implementation\r
- *******************************************************************************/\r
-package org.simantics.utils.datastructures.cache;\r
-\r
-import java.lang.ref.SoftReference;\r
-import java.util.ArrayList;\r
-import java.util.Collection;\r
-import java.util.Collections;\r
-import java.util.HashMap;\r
-import java.util.Map;\r
-import java.util.Timer;\r
-import java.util.concurrent.ScheduledFuture;\r
-import java.util.concurrent.TimeUnit;\r
-\r
-import org.simantics.utils.threads.ThreadUtils;\r
-\r
-/**\r
- * A timed (Key, Value) cache which disposes the cached entry after the\r
- * specified amount of time if it is not removed from the cache. The hold time\r
- * is given to the {@link #put(Object, Object, long)} method, separately for\r
- * each value.\r
- * \r
- * <p>\r
- * The cached values are held as soft references, which makes them collectible\r
- * under memory pressure, even before their hold time has ran out.\r
- * \r
- * @author Tuukka Lehtonen\r
- * \r
- * @param <K> key type, held by strong references\r
- * @param <V> value type, held by soft references to allow collection of the\r
- *        cached elements when under memory pressure\r
- */\r
-public class SoftTimedCache<K, V> implements ITimedCache<K, V> {\r
-\r
-    protected class Entry {\r
-        final K key;\r
-        final SoftReference<V> ref;\r
-        long holdTime;\r
-        TimeUnit unit;\r
-\r
-        ScheduledFuture<?> future;\r
-\r
-        Entry(K k, V v, long holdTime, TimeUnit unit) {\r
-            assert k != null;\r
-            assert v != null;\r
-\r
-            this.key = k;\r
-            this.ref = new SoftReference<V>(v);\r
-            this.holdTime = holdTime;\r
-            this.unit = unit;\r
-        }\r
-    }\r
-\r
-    private final Map<K, Entry> cache = Collections.synchronizedMap(new HashMap<K, Entry>());\r
-\r
-    @SuppressWarnings("unused")\r
-    private String name;\r
-\r
-    private Timer timer;\r
-\r
-    public SoftTimedCache() {\r
-        this("Cache Timer");\r
-    }\r
-\r
-    public SoftTimedCache(String name) {\r
-        this.name = name;\r
-    }\r
-\r
-    public int size() {\r
-        return cache.size();\r
-    }\r
-\r
-    @Override\r
-    protected void finalize() throws Throwable {\r
-        if (timer != null) {\r
-            timer.cancel();\r
-        }\r
-        clear();\r
-        super.finalize();\r
-    }\r
-\r
-    public synchronized void clear() {\r
-        Object[] entries;\r
-        synchronized (this) {\r
-            entries = cache.values().toArray();\r
-            cache.clear();\r
-        }\r
-        for (Object o : entries) {\r
-            @SuppressWarnings("unchecked")\r
-            Entry e = (Entry) o;\r
-            V v = e.ref.get();\r
-            e.ref.clear();\r
-            cleanup(e);\r
-            disposeValue(v);\r
-        }\r
-    }\r
-\r
-    @Override\r
-    public void put(final K k, V v, long holdTime, TimeUnit unit) {\r
-        Entry e = new Entry(k, v, holdTime, unit);\r
-        synchronized (this) {\r
-            // First dispose of a previous entry.\r
-            dispose(k);\r
-            // Then cache the new one.\r
-            cache.put(k, e);\r
-\r
-            if (unit != null && holdTime > 0) {\r
-                schedule(e);\r
-            }\r
-        }\r
-    }\r
-\r
-    @Override\r
-    public V release(K k) {\r
-        Entry e;\r
-        synchronized (this) {\r
-            e = cache.remove(k);\r
-        }\r
-        if (e == null)\r
-            return null;\r
-        return cleanup(e);\r
-    }\r
-\r
-    private V cleanup(Entry e) {\r
-        if (e.future != null) {\r
-            if (!e.future.isCancelled()) {\r
-                boolean ret = e.future.cancel(false);\r
-                if (ret == false)\r
-                    // Already disposing of this cached entry, let it be.\r
-                    return null;\r
-            }\r
-        }\r
-        return e.ref.get();\r
-    }\r
-\r
-    private void dispose(K k) {\r
-        Entry e;\r
-        synchronized (this) {\r
-            e = cache.remove(k);\r
-        }\r
-        if (e == null)\r
-            // Has been released.\r
-            return;\r
-\r
-        V v = e.ref.get();\r
-        if (v != null) {\r
-            if (e.future != null)\r
-                e.future.cancel(false);\r
-            e.ref.clear();\r
-            disposeValue(v);\r
-        }\r
-    }\r
-\r
-    void schedule(final Entry e) {\r
-        e.future = ThreadUtils.getNonBlockingWorkExecutor().schedule(new Runnable() {\r
-            @Override\r
-            public void run() {\r
-                // Disposal may block, must transfer to blocking work executor.\r
-                ThreadUtils.getBlockingWorkExecutor().execute(new Runnable() {\r
-                    @Override\r
-                    public void run() {\r
-                        dispose(e.key);\r
-                    }\r
-                });\r
-            }\r
-        }, e.holdTime, e.unit);\r
-    }\r
-\r
-\r
-    /**\r
-     * Override to customize disposal of values when their timer elapses.\r
-     * \r
-     * @param v the value to dispose of\r
-     */\r
-    protected void disposeValue(V v) {\r
-        // Do nothing by default.\r
-    }\r
-\r
-    public class CacheEntry {\r
-        private final Entry e;\r
-        private final V value;\r
-        public CacheEntry(Entry e) {\r
-            this.e = e;\r
-            this.value = e.ref.get();\r
-        }\r
-        public K getKey() {\r
-            return e.key;\r
-        }\r
-        public V getValue() {\r
-            return value;\r
-        }\r
-        public boolean disposeScheduled() {\r
-            return e.future != null;\r
-        }\r
-        public void schedule(long holdTime, TimeUnit unit) {\r
-            if (e.future == null) {\r
-                e.holdTime = holdTime;\r
-                e.unit = unit;\r
-                SoftTimedCache.this.schedule(e);\r
-            }\r
-        }\r
-    }\r
-\r
-    public Collection<CacheEntry> getEntries() {\r
-        ArrayList<CacheEntry> result = new ArrayList<CacheEntry>();\r
-        synchronized (this) {\r
-            for (Entry e : cache.values()) {\r
-                result.add(new CacheEntry(e));\r
-            }\r
-        }\r
-        return result;\r
-    }\r
-\r
-}\r
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management
+ * in Industry THTH ry.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     VTT Technical Research Centre of Finland - initial API and implementation
+ *******************************************************************************/
+package org.simantics.utils.datastructures.cache;
+
+import java.lang.ref.SoftReference;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Timer;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import org.simantics.utils.threads.ThreadUtils;
+
+/**
+ * A timed (Key, Value) cache which disposes the cached entry after the
+ * specified amount of time if it is not removed from the cache. The hold time
+ * is given to the {@link #put(Object, Object, long)} method, separately for
+ * each value.
+ * 
+ * <p>
+ * The cached values are held as soft references, which makes them collectible
+ * under memory pressure, even before their hold time has ran out.
+ * 
+ * @author Tuukka Lehtonen
+ * 
+ * @param <K> key type, held by strong references
+ * @param <V> value type, held by soft references to allow collection of the
+ *        cached elements when under memory pressure
+ */
+public class SoftTimedCache<K, V> implements ITimedCache<K, V> {
+
+    protected class Entry {
+        final K key;
+        final SoftReference<V> ref;
+        long holdTime;
+        TimeUnit unit;
+
+        ScheduledFuture<?> future;
+
+        Entry(K k, V v, long holdTime, TimeUnit unit) {
+            assert k != null;
+            assert v != null;
+
+            this.key = k;
+            this.ref = new SoftReference<V>(v);
+            this.holdTime = holdTime;
+            this.unit = unit;
+        }
+    }
+
+    private final Map<K, Entry> cache = Collections.synchronizedMap(new HashMap<K, Entry>());
+
+    @SuppressWarnings("unused")
+    private String name;
+
+    private Timer timer;
+
+    public SoftTimedCache() {
+        this("Cache Timer");
+    }
+
+    public SoftTimedCache(String name) {
+        this.name = name;
+    }
+
+    public int size() {
+        return cache.size();
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        if (timer != null) {
+            timer.cancel();
+        }
+        clear();
+        super.finalize();
+    }
+
+    public synchronized void clear() {
+        Object[] entries;
+        synchronized (this) {
+            entries = cache.values().toArray();
+            cache.clear();
+        }
+        for (Object o : entries) {
+            @SuppressWarnings("unchecked")
+            Entry e = (Entry) o;
+            V v = e.ref.get();
+            e.ref.clear();
+            cleanup(e);
+            disposeValue(v);
+        }
+    }
+
+    @Override
+    public void put(final K k, V v, long holdTime, TimeUnit unit) {
+        Entry e = new Entry(k, v, holdTime, unit);
+        synchronized (this) {
+            // First dispose of a previous entry.
+            dispose(k);
+            // Then cache the new one.
+            cache.put(k, e);
+
+            if (unit != null && holdTime > 0) {
+                schedule(e);
+            }
+        }
+    }
+
+    @Override
+    public V release(K k) {
+        Entry e;
+        synchronized (this) {
+            e = cache.remove(k);
+        }
+        if (e == null)
+            return null;
+        return cleanup(e);
+    }
+
+    private V cleanup(Entry e) {
+        if (e.future != null) {
+            if (!e.future.isCancelled()) {
+                boolean ret = e.future.cancel(false);
+                if (ret == false)
+                    // Already disposing of this cached entry, let it be.
+                    return null;
+            }
+        }
+        return e.ref.get();
+    }
+
+    private void dispose(K k) {
+        Entry e;
+        synchronized (this) {
+            e = cache.remove(k);
+        }
+        if (e == null)
+            // Has been released.
+            return;
+
+        V v = e.ref.get();
+        if (v != null) {
+            if (e.future != null)
+                e.future.cancel(false);
+            e.ref.clear();
+            disposeValue(v);
+        }
+    }
+
+    void schedule(final Entry e) {
+        e.future = ThreadUtils.getNonBlockingWorkExecutor().schedule(new Runnable() {
+            @Override
+            public void run() {
+                // Disposal may block, must transfer to blocking work executor.
+                ThreadUtils.getBlockingWorkExecutor().execute(new Runnable() {
+                    @Override
+                    public void run() {
+                        dispose(e.key);
+                    }
+                });
+            }
+        }, e.holdTime, e.unit);
+    }
+
+
+    /**
+     * Override to customize disposal of values when their timer elapses.
+     * 
+     * @param v the value to dispose of
+     */
+    protected void disposeValue(V v) {
+        // Do nothing by default.
+    }
+
+    public class CacheEntry {
+        private final Entry e;
+        private final V value;
+        public CacheEntry(Entry e) {
+            this.e = e;
+            this.value = e.ref.get();
+        }
+        public K getKey() {
+            return e.key;
+        }
+        public V getValue() {
+            return value;
+        }
+        public boolean disposeScheduled() {
+            return e.future != null;
+        }
+        public void schedule(long holdTime, TimeUnit unit) {
+            if (e.future == null) {
+                e.holdTime = holdTime;
+                e.unit = unit;
+                SoftTimedCache.this.schedule(e);
+            }
+        }
+    }
+
+    public Collection<CacheEntry> getEntries() {
+        ArrayList<CacheEntry> result = new ArrayList<CacheEntry>();
+        synchronized (this) {
+            for (Entry e : cache.values()) {
+                result.add(new CacheEntry(e));
+            }
+        }
+        return result;
+    }
+
+}