/* * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ package com.facebook.imagepipeline.cache; import javax.annotation.concurrent.GuardedBy; import java.util.Map; import com.facebook.common.internal.Maps; import com.facebook.common.internal.Preconditions; import com.facebook.common.logging.FLog; import com.facebook.common.references.CloseableReference; import com.facebook.imagepipeline.memory.PooledByteBuffer; import com.facebook.cache.common.CacheKey; /** * This is class encapsulates Map that maps ImageCacheKeys to SharedReferences pointing to * PooledByteBuffers. It is used by SimpleImageCache to store values that are being written * to disk cache, so that they can be returned by parallel cache get operations. */ public class StagingArea { private static final Class<?> TAG = StagingArea.class; @GuardedBy("this") private Map<CacheKey, CloseableReference<PooledByteBuffer>> mMap; private StagingArea() { mMap = Maps.newHashMap(); } public static StagingArea getInstance() { return new StagingArea(); } /** * Stores key-value in this StagingArea. This call overrides previous value * of stored reference if * @param key * @param bufferRef reference to be associated with key */ public synchronized void put( final CacheKey key, final CloseableReference<PooledByteBuffer> bufferRef) { Preconditions.checkNotNull(key); Preconditions.checkArgument(CloseableReference.isValid(bufferRef)); // we're making a 'copy' of this reference - so duplicate it final CloseableReference<?> oldEntry = mMap.put(key, bufferRef.clone()); if (oldEntry != null) { oldEntry.close(); } logStats(); } /** * Removes key-value from the StagingArea. Both key and value must match. * @param key * @param bufferRef value corresponding to key * @return true if item was removed */ public synchronized boolean remove( final CacheKey key, final CloseableReference<PooledByteBuffer> bufferRef) { Preconditions.checkNotNull(key); Preconditions.checkNotNull(bufferRef); Preconditions.checkArgument(CloseableReference.isValid(bufferRef)); final CloseableReference<?> oldValue = mMap.get(key); if (oldValue == null || oldValue.get() != bufferRef.get()) { return false; } mMap.remove(key); oldValue.close(); logStats(); return true; } /** * @param key * @return value associated with given key or null if no value is associated */ public synchronized CloseableReference<PooledByteBuffer> get(final CacheKey key) { Preconditions.checkNotNull(key); CloseableReference<PooledByteBuffer> storedRef = mMap.get(key); if (storedRef != null) { synchronized (storedRef) { if (!CloseableReference.isValid(storedRef)) { // Reference is not valid, this means that someone cleared reference while it was still in // use. Log error // TODO: 3697790 mMap.remove(key); FLog.w( TAG, "Found closed reference %d for key %s (%d)", System.identityHashCode(storedRef), key.toString(), System.identityHashCode(key)); return null; } storedRef = storedRef.clone(); } } return storedRef; } /** * Simple 'debug' logging of stats. */ private synchronized void logStats() { FLog.v(TAG, "Count = %d", mMap.size()); } }