/*
* 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.memory;
import javax.annotation.Nullable;
import javax.annotation.concurrent.NotThreadSafe;
import java.util.concurrent.ConcurrentLinkedQueue;
import com.facebook.common.internal.Preconditions;
import com.facebook.common.internal.VisibleForTesting;
import com.facebook.common.logging.FLog;
/**
* The Bucket is a constituent class of {@link BasePool}. The pool maintains its free values
* in a set of buckets, where each bucket represents a set of values of the same 'size'.
* <p>
* Each bucket maintains a freelist of values.
* When the pool receives a {@link BasePool#get(Object)} request for a particular size, it finds the
* appropriate bucket, and delegates the request to the bucket ({@link #get()}.
* If the bucket's freelist is non-empty, then one of the entries on the freelist is returned (and
* removed from the freelist).
* Similarly, when a value is released to the pool via a call to {@link BasePool#release(Object)},
* the pool locates the appropriate bucket and returns the value to the bucket's freelist - see
* ({@link #release(Object)}
* <p>
* The bucket also maintains the current number of items (from this bucket) that are "in use" i.e.
* values that came from this bucket, but are now in use by the caller, and no longer on the
* freelist.
* The 'length' of the bucket is the number of values from this bucket that are currently in use
* (mInUseCount), plus the size of the freeList. The maxLength of the bucket is that maximum length
* that this bucket should grow to - and is used by the pool to determine whether values should
* be released to the bucket ot freed.
* @param <V> type of values to be 'stored' in the bucket
*/
@NotThreadSafe
@VisibleForTesting
class Bucket<V> {
private static final String TAG = "com.facebook.imagepipeline.common.Bucket";
public final int mItemSize; // size in bytes of items in this bucket
public final int mMaxLength; // 'max' length for this bucket
public final ConcurrentLinkedQueue<V> mFreeList; // the free list for this bucket
@VisibleForTesting
int mInUseLength; // current number of entries 'in use' (i.e.) not in the free list
/**
* Constructs a new Bucket instance. The constructed bucket will have an empty freelist
* @param itemSize size in bytes of each item in this bucket
* @param maxLength max length for the bucket (used + free)
* @param inUseLength current in-use-length for the bucket
*/
public Bucket(int itemSize, int maxLength, int inUseLength) {
Preconditions.checkState(itemSize > 0);
Preconditions.checkState(maxLength >= 0);
Preconditions.checkState(inUseLength >= 0);
mItemSize = itemSize;
mMaxLength = maxLength;
mFreeList = new ConcurrentLinkedQueue<V>();
mInUseLength = inUseLength;
}
/**
* Determines if the current length of the bucket (free + used) exceeds the max length
* specified
*/
public boolean isMaxLengthExceeded() {
return (mInUseLength + mFreeList.size() > mMaxLength);
}
/**
* Gets a free item if possible from the freelist. Returns null if the free list is empty
* Updates the bucket inUse count
* @return an item from the free list, if available
*/
@Nullable
public V get() {
V value = pop();
if (value != null) {
mInUseLength ++;
}
return value;
}
/**
* Remove the first item (if any) from the freelist. Returns null if the free list is empty
* Does not update the bucket inUse count
* @return the first value (if any) from the free list
*/
@Nullable
public V pop() {
return mFreeList.poll();
}
/**
* Increment the mInUseCount field.
* Used by the pool to update the bucket info when a value was 'alloc'ed (because no free value
* was available)
*/
public void incrementInUseCount() {
mInUseLength ++;
}
/**
* Releases a value to this bucket and decrements the inUse count
* @param value the value to release
*/
public void release(V value) {
Preconditions.checkNotNull(value);
if (mInUseLength > 0) {
mInUseLength --;
} else {
FLog.wtf(TAG, "Bucket inUseLength currently at %d", mInUseLength);
}
mFreeList.add(value);
}
/**
* Decrement the mInUseCount field.
* Used by the pool to update the bucket info when a value was freed, instead of being returned
* to the bucket's free list
*/
public void decrementInUseCount() {
mInUseLength --;
}
}