// ********************************************************************** // // Copyright (c) 2003-2007 ZeroC, Inc. All rights reserved. // // This copy of Ice is licensed to you under the terms described in the // ICE_LICENSE file included in this distribution. // // ********************************************************************** package IceUtil; // // An abstraction to efficiently populate a Cache, without holding // a lock while loading from a potentially slow store. // public class Cache { public Cache(Store store) { _store = store; } public Object getIfPinned(Object key) { synchronized(_map) { CacheValue val = (CacheValue) _map.get(key); return val == null ? null : val.obj; } } public Object unpin(Object key) { synchronized(_map) { CacheValue val = (CacheValue) _map.remove(key); return val == null ? null : val.obj; } } public void clear() { synchronized(_map) { _map.clear(); } } public int size() { synchronized(_map) { return _map.size(); } } // // Add an object to the cache without checking store // If there is already an object associated with this // key, the existing value remains in the map and is // returned. // public Object pin(Object key, Object o) { synchronized(_map) { CacheValue existingVal = (CacheValue) _map.put(key, new CacheValue(o)); if(existingVal != null) { _map.put(key, existingVal); return existingVal.obj; } else { return null; } } } // // Return an object from the cache, loading it from the // store if necessary. // public Object pin(Object key) { return pinImpl(key, null); } // // Puts this key/value pair in the cache. // If this key is already in the cache or store, the given // key/value pair is not inserted and the existing value // is returned. // public Object putIfAbsent(Object key, Object newObj) { return pinImpl(key, newObj); } static private class CacheValue { CacheValue() { } CacheValue(Object obj) { this.obj = obj; } Object obj = null; CountDownLatch latch = null; } private Object pinImpl(Object key, Object newObj) { for(;;) { CacheValue val = null; CountDownLatch latch = null; synchronized(_map) { val = (CacheValue) _map.get(key); if(val == null) { val = new CacheValue(); _map.put(key, val); } else { if(val.obj != null) { return val.obj; } if(val.latch == null) { // // The first queued thread creates the latch // val.latch = new CountDownLatch(1); } latch = val.latch; } } if(latch != null) { try { latch.await(); } catch(InterruptedException e) { // Ignored } // // val could be stale now, e.g. some other thread pinned and unpinned the // object while we were waiting. // So start over. // continue; } else { Object obj; try { obj = _store.load(key); } catch(RuntimeException e) { synchronized(_map) { _map.remove(key); latch = val.latch; val.latch = null; } if(latch != null) { latch.countDown(); assert latch.getCount() == 0; } throw e; } synchronized(_map) { if(obj != null) { val.obj = obj; } else { if(newObj == null) { // // pin() did not find the object // // // The waiting threads will have to call load() to see by themselves. // _map.remove(key); } else { // // putIfAbsent() inserts key/newObj // val.obj = newObj; } } latch = val.latch; val.latch = null; } if(latch != null) { latch.countDown(); assert latch.getCount() == 0; } return obj; } } } private final java.util.Map _map = new java.util.HashMap(); private final Store _store; }