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