1 /*******************************************************************************
\r
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
\r
3 * in Industry THTH ry.
\r
4 * All rights reserved. This program and the accompanying materials
\r
5 * are made available under the terms of the Eclipse Public License v1.0
\r
6 * which accompanies this distribution, and is available at
\r
7 * http://www.eclipse.org/legal/epl-v10.html
\r
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.utils.datastructures.cache;
\r
14 import java.lang.ref.SoftReference;
\r
15 import java.util.ArrayList;
\r
16 import java.util.Collection;
\r
17 import java.util.Collections;
\r
18 import java.util.HashMap;
\r
19 import java.util.Map;
\r
20 import java.util.Timer;
\r
21 import java.util.concurrent.ScheduledFuture;
\r
22 import java.util.concurrent.TimeUnit;
\r
24 import org.simantics.utils.threads.ThreadUtils;
\r
27 * A timed (Key, Value) cache which disposes the cached entry after the
\r
28 * specified amount of time if it is not removed from the cache. The hold time
\r
29 * is given to the {@link #put(Object, Object, long)} method, separately for
\r
33 * The cached values are held as soft references, which makes them collectible
\r
34 * under memory pressure, even before their hold time has ran out.
\r
36 * @author Tuukka Lehtonen
\r
38 * @param <K> key type, held by strong references
\r
39 * @param <V> value type, held by soft references to allow collection of the
\r
40 * cached elements when under memory pressure
\r
42 public class SoftTimedCache<K, V> implements ITimedCache<K, V> {
\r
44 protected class Entry {
\r
46 final SoftReference<V> ref;
\r
50 ScheduledFuture<?> future;
\r
52 Entry(K k, V v, long holdTime, TimeUnit unit) {
\r
57 this.ref = new SoftReference<V>(v);
\r
58 this.holdTime = holdTime;
\r
63 private final Map<K, Entry> cache = Collections.synchronizedMap(new HashMap<K, Entry>());
\r
65 @SuppressWarnings("unused")
\r
66 private String name;
\r
68 private Timer timer;
\r
70 public SoftTimedCache() {
\r
71 this("Cache Timer");
\r
74 public SoftTimedCache(String name) {
\r
79 return cache.size();
\r
83 protected void finalize() throws Throwable {
\r
84 if (timer != null) {
\r
91 public synchronized void clear() {
\r
93 synchronized (this) {
\r
94 entries = cache.values().toArray();
\r
97 for (Object o : entries) {
\r
98 @SuppressWarnings("unchecked")
\r
99 Entry e = (Entry) o;
\r
108 public void put(final K k, V v, long holdTime, TimeUnit unit) {
\r
109 Entry e = new Entry(k, v, holdTime, unit);
\r
110 synchronized (this) {
\r
111 // First dispose of a previous entry.
\r
113 // Then cache the new one.
\r
116 if (unit != null && holdTime > 0) {
\r
123 public V release(K k) {
\r
125 synchronized (this) {
\r
126 e = cache.remove(k);
\r
133 private V cleanup(Entry e) {
\r
134 if (e.future != null) {
\r
135 if (!e.future.isCancelled()) {
\r
136 boolean ret = e.future.cancel(false);
\r
138 // Already disposing of this cached entry, let it be.
\r
142 return e.ref.get();
\r
145 private void dispose(K k) {
\r
147 synchronized (this) {
\r
148 e = cache.remove(k);
\r
151 // Has been released.
\r
156 if (e.future != null)
\r
157 e.future.cancel(false);
\r
163 void schedule(final Entry e) {
\r
164 e.future = ThreadUtils.getNonBlockingWorkExecutor().schedule(new Runnable() {
\r
166 public void run() {
\r
167 // Disposal may block, must transfer to blocking work executor.
\r
168 ThreadUtils.getBlockingWorkExecutor().execute(new Runnable() {
\r
170 public void run() {
\r
175 }, e.holdTime, e.unit);
\r
180 * Override to customize disposal of values when their timer elapses.
\r
182 * @param v the value to dispose of
\r
184 protected void disposeValue(V v) {
\r
185 // Do nothing by default.
\r
188 public class CacheEntry {
\r
189 private final Entry e;
\r
190 private final V value;
\r
191 public CacheEntry(Entry e) {
\r
193 this.value = e.ref.get();
\r
195 public K getKey() {
\r
198 public V getValue() {
\r
201 public boolean disposeScheduled() {
\r
202 return e.future != null;
\r
204 public void schedule(long holdTime, TimeUnit unit) {
\r
205 if (e.future == null) {
\r
206 e.holdTime = holdTime;
\r
208 SoftTimedCache.this.schedule(e);
\r
213 public Collection<CacheEntry> getEntries() {
\r
214 ArrayList<CacheEntry> result = new ArrayList<CacheEntry>();
\r
215 synchronized (this) {
\r
216 for (Entry e : cache.values()) {
\r
217 result.add(new CacheEntry(e));
\r