[jboss-cvs] JBossAS SVN: r112419 - projects/jboss-jca/trunk/adapters/src/main/java/org/jboss/jca/adapters/jdbc/util.
jboss-cvs-commits at lists.jboss.org
jboss-cvs-commits at lists.jboss.org
Fri Nov 4 12:03:23 EDT 2011
Author: jesper.pedersen
Date: 2011-11-04 12:03:22 -0400 (Fri, 04 Nov 2011)
New Revision: 112419
Modified:
projects/jboss-jca/trunk/adapters/src/main/java/org/jboss/jca/adapters/jdbc/util/BoundedConcurrentHashMap.java
Log:
Update LIRS implementation
Modified: projects/jboss-jca/trunk/adapters/src/main/java/org/jboss/jca/adapters/jdbc/util/BoundedConcurrentHashMap.java
===================================================================
--- projects/jboss-jca/trunk/adapters/src/main/java/org/jboss/jca/adapters/jdbc/util/BoundedConcurrentHashMap.java 2011-11-04 08:27:23 UTC (rev 112418)
+++ projects/jboss-jca/trunk/adapters/src/main/java/org/jboss/jca/adapters/jdbc/util/BoundedConcurrentHashMap.java 2011-11-04 16:03:22 UTC (rev 112419)
@@ -103,7 +103,7 @@
* Modified for https://jira.jboss.org/jira/browse/ISPN-299
* Includes ideas described in http://portal.acm.org/citation.cfm?id=1547428
*
- * Latest: https://github.com/infinispan/infinispan/commit/054e321d4ab1d2f33d2d920242bdac3d73745eec
+ * Latest: https://github.com/infinispan/infinispan/commit/c642be2c8a64c13e7e74283f38e2037cef9a362f
*
* @since 1.5
* @author Doug Lea
@@ -233,7 +233,7 @@
* backup in case a null (pre-initialized) value is ever seen in
* an unsynchronized access method.
*/
- static final class HashEntry<K, V>
+ private static class HashEntry<K, V>
{
/** Key */
final K key;
@@ -247,9 +247,6 @@
/** Entry */
final HashEntry<K, V> next;
- /** State */
- volatile Recency state;
-
/**
* Constructor
* @param key The key
@@ -263,7 +260,6 @@
this.hash = hash;
this.next = next;
this.value = value;
- this.state = Recency.HIR_RESIDENT;
}
/**
@@ -298,39 +294,6 @@
}
/**
- * State: LIR_RESIDENT
- */
- public void transitionToLIRResident()
- {
- state = Recency.LIR_RESIDENT;
- }
-
- /**
- * State: HIR_NONRESIDENT
- */
- public void transitionHIRResidentToHIRNonResident()
- {
- state = Recency.HIR_NONRESIDENT;
- }
-
- /**
- * State: HIR_RESIDENT
- */
- public void transitionLIRResidentToHIRResident()
- {
- state = Recency.HIR_RESIDENT;
- }
-
- /**
- * Recency
- * @return The value
- */
- public Recency recency()
- {
- return state;
- }
-
- /**
* New array
* @param i The index
* @return The entry
@@ -450,6 +413,18 @@
public static final int MAX_BATCH_SIZE = 64;
/**
+ * Create new entry
+ *
+ * @param key The key
+ * @param hash The hash
+ * @param next The next entry
+ * @param value The value
+ *
+ * @return the new entry
+ */
+ HashEntry<K, V> createNewEntry(K key, int hash, HashEntry<K, V> next, V value);
+
+ /**
* Invokes eviction policy algorithm and returns set of evicted entries.
*
* <p>
@@ -583,6 +558,15 @@
{
return Eviction.NONE;
}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public HashEntry<K, V> createNewEntry(K key, int hash, HashEntry<K, V> next, V value)
+ {
+ return new HashEntry<K, V>(key, hash, next, value);
+ }
}
/**
@@ -722,6 +706,15 @@
{
return Eviction.LRU_OLD;
}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public HashEntry<K, V> createNewEntry(K key, int hash, HashEntry<K, V> next, V value)
+ {
+ return new HashEntry<K, V>(key, hash, next, value);
+ }
}
/**
@@ -872,177 +865,632 @@
}
return aboveThreshold;
}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public HashEntry<K, V> createNewEntry(K key, int hash, HashEntry<K, V> next, V value)
+ {
+ return new HashEntry<K, V>(key, hash, next, value);
+ }
}
/**
- * LIRS
+ * Adapted to Infinispan BoundedConcurrentHashMap using LIRS implementation ideas from Charles Fry (fry at google.com)
+ * See
+ *
+ * http://code.google.com/p/concurrentlinkedhashmap/source/browse/trunk/src/test/java/com/
+ * googlecode/concurrentlinkedhashmap/caches/LirsMap.java
+ *
+ * for original sources
+ *
*/
- static final class LIRS<K, V> implements EvictionPolicy<K, V>
+ private static final class LIRSHashEntry<K, V> extends HashEntry<K, V>
{
- private static final int MIN_HIR_SIZE = 2;
- private final Segment<K, V> segment;
- private final ConcurrentLinkedQueue<HashEntry<K, V>> accessQueue;
- private final LinkedHashMap<K, HashEntry<K, V>> stack;
- private final LinkedList<HashEntry<K, V>> queue;
- private final int maxBatchQueueSize;
- private final int lirSizeLimit;
- private final int hirSizeLimit;
- private int currentLIRSize;
- private final float batchThresholdFactor;
+ // LIRS stack S
+ private LIRSHashEntry<K, V> previousInStack;
+ private LIRSHashEntry<K, V> nextInStack;
+
+ // LIRS queue Q
+ private LIRSHashEntry<K, V> previousInQueue;
+ private LIRSHashEntry<K, V> nextInQueue;
+ volatile Recency state;
+ LIRS<K, V> owner;
+
/**
- * LIRS
- * @param s The segment
- * @param capacity The capacity
- * @param maxBatchSize The max batch size
- * @param batchThresholdFactor The batch threshold factor
+ * Constructor
+ * @param owner The owner
+ * @param key The key
+ * @param hash The hash
+ * @param next The next entry
+ * @param value
*/
- public LIRS(Segment<K, V> s, int capacity, int maxBatchSize, float batchThresholdFactor)
+ LIRSHashEntry(LIRS<K, V> owner, K key, int hash, HashEntry<K, V> next, V value)
{
- this.segment = s;
- int tmpLirSize = (int) (capacity * 0.9);
- int tmpHirSizeLimit = capacity - tmpLirSize;
- if (tmpHirSizeLimit < MIN_HIR_SIZE)
+ super(key, hash, next, value);
+ this.owner = owner;
+ this.state = Recency.HIR_RESIDENT;
+
+ // initially point everything back to self
+ this.previousInStack = this;
+ this.nextInStack = this;
+ this.previousInQueue = this;
+ this.nextInQueue = this;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = 17;
+ result = result * 31 + hash;
+ result = result * 31 + key.hashCode();
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ // HashEntry is internal class, never leaks out of CHM, hence slight optimization
+ if (this == o)
{
- hirSizeLimit = MIN_HIR_SIZE;
- lirSizeLimit = capacity - hirSizeLimit;
+ return true;
}
+
+ if (o == null)
+ {
+ return false;
+ }
+
+ HashEntry<?, ?> other = (HashEntry<?, ?>) o;
+ return hash == other.hash && key.equals(other.key);
+ }
+
+ /**
+ * Returns true if this entry is in the stack, false otherwise.
+ */
+ public boolean inStack()
+ {
+ return (nextInStack != null);
+ }
+
+ /**
+ * Returns true if this entry is in the queue, false otherwise.
+ */
+ public boolean inQueue()
+ {
+ return (nextInQueue != null);
+ }
+
+ /**
+ * Records a cache hit.
+ */
+ public void hit(Set<HashEntry<K, V>> evicted)
+ {
+ switch (state)
+ {
+ case LIR_RESIDENT:
+ hotHit(evicted);
+ break;
+ case HIR_RESIDENT:
+ coldHit(evicted);
+ break;
+ case HIR_NONRESIDENT:
+ throw new IllegalStateException("Can't hit a non-resident entry!");
+ default:
+ throw new AssertionError("Hit with unknown status: " + state);
+ }
+ }
+
+ /**
+ * Records a cache hit on a hot block.
+ */
+ private void hotHit(Set<HashEntry<K, V>> evicted)
+ {
+ // See section 3.3 case 1:
+ // "Upon accessing an LIR block X:
+ // This access is guaranteed to be a hit in the cache."
+
+ // "We move it to the top of stack S."
+ boolean onBottom = (owner.stackBottom() == this);
+ moveToStackTop();
+
+ // "If the LIR block is originally located in the bottom of the stack,
+ // we conduct a stack pruning."
+ if (onBottom)
+ {
+ owner.pruneStack(evicted);
+ }
+ }
+
+ /**
+ * Records a cache hit on a cold block.
+ */
+ private void coldHit(Set<HashEntry<K, V>> evicted)
+ {
+ // See section 3.3 case 2:
+ // "Upon accessing an HIR resident block X:
+ // This is a hit in the cache."
+
+ // "We move it to the top of stack S."
+ boolean inStack = inStack();
+ moveToStackTop();
+
+ // "There are two cases for block X:"
+ if (inStack)
+ {
+ // "(1) If X is in the stack S, we change its status to LIR."
+ hot();
+
+ // "This block is also removed from list Q."
+ removeFromQueue();
+
+ // "The LIR block in the bottom of S is moved to the end of list Q
+ // with its status changed to HIR."
+ owner.stackBottom().migrateToQueue();
+
+ // "A stack pruning is then conducted."
+ owner.pruneStack(evicted);
+ }
else
{
- hirSizeLimit = tmpHirSizeLimit;
- lirSizeLimit = tmpLirSize;
+ // "(2) If X is not in stack S, we leave its status in HIR and move
+ // it to the end of list Q."
+ moveToQueueEnd();
}
- this.maxBatchQueueSize = maxBatchSize > MAX_BATCH_SIZE ? MAX_BATCH_SIZE : maxBatchSize;
- this.batchThresholdFactor = batchThresholdFactor;
- this.accessQueue = new ConcurrentLinkedQueue<HashEntry<K, V>>();
- this.stack = new LinkedHashMap<K, HashEntry<K, V>>();
- this.queue = new LinkedList<HashEntry<K, V>>();
}
/**
- * {@inheritDoc}
+ * Records a cache miss. This is how new entries join the LIRS stack and
+ * queue. This is called both when a new entry is first created, and when a
+ * non-resident entry is re-computed.
*/
- @Override
- public Set<HashEntry<K, V>> execute()
+ private Set<HashEntry<K, V>> miss()
{
- Set<HashEntry<K, V>> evicted = new HashSet<HashEntry<K, V>>();
- try
+ Set<HashEntry<K, V>> evicted = Collections.emptySet();
+ if (owner.hotSize < owner.maximumHotSize)
{
- for (HashEntry<K, V> e : accessQueue)
- {
- if (present(e))
- {
- if (e.recency() == Recency.LIR_RESIDENT)
- {
- handleLIRHit(e, evicted);
- }
- else if (e.recency() == Recency.HIR_RESIDENT)
- {
- handleHIRHit(e, evicted);
- }
- }
- }
- removeFromSegment(evicted);
+ warmupMiss();
}
- finally
+ else
{
- accessQueue.clear();
+ evicted = new HashSet<HashEntry<K, V>>();
+ fullMiss(evicted);
}
+
+ // now the missed item is in the cache
+ owner.size++;
+
return evicted;
}
- private void handleHIRHit(HashEntry<K, V> e, Set<HashEntry<K, V>> evicted)
+ /**
+ * Records a miss when the hot entry set is not full.
+ */
+ private void warmupMiss()
{
- boolean inStack = stack.containsKey(e.key);
- if (inStack)
+ // See section 3.3:
+ // "When LIR block set is not full, all the referenced blocks are
+ // given an LIR status until its size reaches L_lirs."
+ hot();
+ moveToStackTop();
+ }
+
+ /**
+ * Records a miss when the hot entry set is full.
+ */
+ private void fullMiss(Set<HashEntry<K, V>> evicted)
+ {
+ // See section 3.3 case 3:
+ // "Upon accessing an HIR non-resident block X:
+ // This is a miss."
+
+ // This condition is unspecified in the paper, but appears to be
+ // necessary.
+ if (owner.size >= owner.maximumSize)
{
- stack.remove(e.key);
+ // "We remove the HIR resident block at the front of list Q (it then
+ // becomes a non-resident block), and replace it out of the cache."
+ LIRSHashEntry<K, V> evictedNode = owner.queueFront();
+ evicted.add(evictedNode);
}
- // first put on top of the stack
- stack.put(e.key, e);
+ // "Then we load the requested block X into the freed buffer and place
+ // it on the top of stack S."
+ boolean inStack = inStack();
+ moveToStackTop();
+ // "There are two cases for block X:"
if (inStack)
{
- queue.remove(e);
- currentLIRSize++;
- e.transitionToLIRResident();
- switchBottomostLIRtoHIRAndPrune(evicted);
+ // "(1) If X is in stack S, we change its status to LIR and move the
+ // LIR block in the bottom of stack S to the end of list Q with its
+ // status changed to HIR. A stack pruning is then conducted.
+ hot();
+
+ owner.stackBottom().migrateToQueue();
+ owner.pruneStack(evicted);
}
else
{
- queue.remove(e);
- queue.addLast(e);
+ // "(2) If X is not in stack S, we leave its status in HIR and place
+ // it in the end of list Q."
+ cold();
}
}
- private void handleLIRHit(HashEntry<K, V> e, Set<HashEntry<K, V>> evicted)
+ /**
+ * Marks this entry as hot.
+ */
+ private void hot()
{
- stack.remove(e.key);
- stack.put(e.key, e);
- for (Iterator<HashEntry<K, V>> i = stack.values().iterator(); i.hasNext();)
+ if (state != Recency.LIR_RESIDENT)
{
- HashEntry<K, V> next = i.next();
- if (next.recency() == Recency.LIR_RESIDENT)
- {
+ owner.hotSize++;
+ }
+
+ state = Recency.LIR_RESIDENT;
+ }
+
+ /**
+ * Marks this entry as cold.
+ */
+ private void cold()
+ {
+ if (state == Recency.LIR_RESIDENT)
+ {
+ owner.hotSize--;
+ }
+
+ state = Recency.HIR_RESIDENT;
+ moveToQueueEnd();
+ }
+
+ /**
+ * Marks this entry as non-resident.
+ */
+ @SuppressWarnings("fallthrough")
+ private void nonResident()
+ {
+ switch (state)
+ {
+ case LIR_RESIDENT:
+ owner.hotSize--;
+ // fallthrough
+ case HIR_RESIDENT:
+ owner.size--;
break;
- }
- else
+ }
+
+ state = Recency.HIR_NONRESIDENT;
+ }
+
+ /**
+ * Returns true if this entry is resident in the cache, false otherwise.
+ */
+ public boolean isResident()
+ {
+ return (state != Recency.HIR_NONRESIDENT);
+ }
+
+ /**
+ * Temporarily removes this entry from the stack, fixing up neighbor links.
+ * This entry's links remain unchanged, meaning that {@link #inStack()} will
+ * continue to return true. This should only be called if this node's links
+ * will be subsequently changed.
+ */
+ private void tempRemoveFromStack()
+ {
+ if (inStack())
+ {
+ previousInStack.nextInStack = nextInStack;
+ nextInStack.previousInStack = previousInStack;
+ }
+ }
+
+ /**
+ * Removes this entry from the stack.
+ */
+ private void removeFromStack()
+ {
+ tempRemoveFromStack();
+ previousInStack = null;
+ nextInStack = null;
+ }
+
+ /**
+ * Inserts this entry before the specified existing entry in the stack.
+ */
+ private void addToStackBefore(LIRSHashEntry<K, V> existingEntry)
+ {
+ previousInStack = existingEntry.previousInStack;
+ nextInStack = existingEntry;
+ previousInStack.nextInStack = this;
+ nextInStack.previousInStack = this;
+ }
+
+ /**
+ * Moves this entry to the top of the stack.
+ */
+ private void moveToStackTop()
+ {
+ tempRemoveFromStack();
+ addToStackBefore(owner.header.nextInStack);
+ }
+
+ /**
+ * Moves this entry to the bottom of the stack.
+ */
+ private void moveToStackBottom()
+ {
+ tempRemoveFromStack();
+ addToStackBefore(owner.header);
+ }
+
+ /**
+ * Temporarily removes this entry from the queue, fixing up neighbor links.
+ * This entry's links remain unchanged. This should only be called if this
+ * node's links will be subsequently changed.
+ */
+ private void tempRemoveFromQueue()
+ {
+ if (inQueue())
+ {
+ previousInQueue.nextInQueue = nextInQueue;
+ nextInQueue.previousInQueue = previousInQueue;
+ }
+ }
+
+ /**
+ * Removes this entry from the queue.
+ */
+ private void removeFromQueue()
+ {
+ tempRemoveFromQueue();
+ previousInQueue = null;
+ nextInQueue = null;
+ }
+
+ /**
+ * Inserts this entry before the specified existing entry in the queue.
+ */
+ private void addToQueueBefore(LIRSHashEntry<K, V> existingEntry)
+ {
+ previousInQueue = existingEntry.previousInQueue;
+ nextInQueue = existingEntry;
+ previousInQueue.nextInQueue = this;
+ nextInQueue.previousInQueue = this;
+ }
+
+ /**
+ * Moves this entry to the end of the queue.
+ */
+ private void moveToQueueEnd()
+ {
+ tempRemoveFromQueue();
+ addToQueueBefore(owner.header);
+ }
+
+ /**
+ * Moves this entry from the stack to the queue, marking it cold
+ * (as hot entries must remain in the stack). This should only be called
+ * on resident entries, as non-resident entries should not be made resident.
+ * The bottom entry on the queue is always hot due to stack pruning.
+ */
+ private void migrateToQueue()
+ {
+ removeFromStack();
+ cold();
+ }
+
+ /**
+ * Moves this entry from the queue to the stack, marking it hot (as cold
+ * resident entries must remain in the queue).
+ */
+ private void migrateToStack()
+ {
+ removeFromQueue();
+ if (!inStack())
+ {
+ moveToStackBottom();
+ }
+
+ hot();
+ }
+
+ /**
+ * Evicts this entry, removing it from the queue and setting its status to
+ * cold non-resident. If the entry is already absent from the stack, it is
+ * removed from the backing map; otherwise it remains in order for its
+ * recency to be maintained.
+ */
+ private void evict()
+ {
+ removeFromQueue();
+ removeFromStack();
+ nonResident();
+ owner = null;
+ }
+
+ /**
+ * Removes this entry from the cache. This operation is not specified in
+ * the paper, which does not account for forced eviction.
+ */
+ private V remove()
+ {
+ boolean wasHot = (state == Recency.LIR_RESIDENT);
+ V result = value;
+ LIRSHashEntry<K, V> end = owner != null ? owner.queueEnd() : null;
+ evict();
+
+ // attempt to maintain a constant number of hot entries
+ if (wasHot)
+ {
+ if (end != null)
{
- i.remove();
- evicted.add(next);
+ end.migrateToStack();
}
}
+
+ return result;
}
+ }
- private boolean present(HashEntry<K, V> e)
+ /**
+ * LIRS
+ */
+ static final class LIRS<K, V> implements EvictionPolicy<K, V>
+ {
+ /**
+ * The percentage of the cache which is dedicated to hot blocks.
+ * See section 5.1
+ */
+ private static final float L_LIRS = 0.95f;
+
+ /** The owning segment */
+ private final Segment<K, V> segment;
+
+ /**
+ * The accessQueue for reducing lock contention
+ * See "BP-Wrapper: a system framework making any replacement algorithms
+ * (almost) lock contention free"
+ *
+ * http://www.cse.ohio-state.edu/hpcs/WWW/HTML/publications/abs09-1.html
+ *
+ */
+ private final ConcurrentLinkedQueue<LIRSHashEntry<K, V>> accessQueue;
+
+ /**
+ * The maxBatchQueueSize
+ *
+ * See "BP-Wrapper: a system framework making any replacement algorithms (almost) lock
+ * contention free"
+ */
+ private final int maxBatchQueueSize;
+
+ /** The number of LIRS entries in a segment */
+ private int size;
+
+ private final float batchThresholdFactor;
+
+ /**
+ * This header encompasses two data structures:
+ *
+ * <ul>
+ * <li>The LIRS stack, S, which is maintains recency information. All hot
+ * entries are on the stack. All cold and non-resident entries which are more
+ * recent than the least recent hot entry are also stored in the stack (the
+ * stack is always pruned such that the last entry is hot, and all entries
+ * accessed more recently than the last hot entry are present in the stack).
+ * The stack is ordered by recency, with its most recently accessed entry
+ * at the top, and its least recently accessed entry at the bottom.</li>
+ *
+ * <li>The LIRS queue, Q, which enqueues all cold entries for eviction. Cold
+ * entries (by definition in the queue) may be absent from the stack (due to
+ * pruning of the stack). Cold entries are added to the end of the queue
+ * and entries are evicted from the front of the queue.</li>
+ * </ul>
+ */
+ private final LIRSHashEntry<K, V> header = new LIRSHashEntry<K, V>(null, null, 0, null, null);
+
+ /** The maximum number of hot entries (L_lirs in the paper). */
+ private final int maximumHotSize;
+
+ /** The maximum number of resident entries (L in the paper). */
+ private final int maximumSize;
+
+ /** The actual number of hot entries. */
+ private int hotSize = 0;
+
+ /**
+ * LIRS
+ * @param s The segment
+ * @param capacity The capacity
+ * @param maxBatchSize The max batch size
+ * @param batchThresholdFactor The batch threshold factor
+ */
+ public LIRS(Segment<K, V> s, int capacity, int maxBatchSize, float batchThresholdFactor)
{
- return stack.containsKey(e.key) || queue.contains(e);
+ this.segment = s;
+ this.maximumSize = capacity;
+ this.maximumHotSize = calculateLIRSize(capacity);
+ this.maxBatchQueueSize = maxBatchSize > MAX_BATCH_SIZE ? MAX_BATCH_SIZE : maxBatchSize;
+ this.batchThresholdFactor = batchThresholdFactor;
+ this.accessQueue = new ConcurrentLinkedQueue<LIRSHashEntry<K, V>>();
}
+ private static int calculateLIRSize(int maximumSize)
+ {
+ int result = (int) (L_LIRS * maximumSize);
+ return (result == maximumSize) ? maximumSize - 1 : result;
+ }
+
/**
* {@inheritDoc}
*/
@Override
- public Set<HashEntry<K, V>> onEntryMiss(HashEntry<K, V> e)
+ public Set<HashEntry<K, V>> execute()
{
- // initialization
- Set<HashEntry<K, V>> evicted = Collections.emptySet();
- if (currentLIRSize + 1 < lirSizeLimit)
+ Set<HashEntry<K, V>> evicted = new HashSet<HashEntry<K, V>>();
+ try
{
- currentLIRSize++;
- e.transitionToLIRResident();
- stack.put(e.key, e);
+ for (LIRSHashEntry<K, V> e : accessQueue)
+ {
+ if (e.isResident())
+ {
+ e.hit(evicted);
+ }
+ }
+ removeFromSegment(evicted);
}
- else
+ finally
{
- if (queue.size() < hirSizeLimit)
- {
- queue.addLast(e);
- }
- else
- {
- boolean inStack = stack.containsKey(e.key);
- HashEntry<K, V> first = queue.removeFirst();
- first.transitionHIRResidentToHIRNonResident();
+ accessQueue.clear();
+ }
+ return evicted;
+ }
- stack.put(e.key, e);
+ /**
+ * Prunes HIR blocks in the bottom of the stack until an HOT block sits in
+ * the stack bottom. If pruned blocks were resident, then they
+ * remain in the queue; otherwise they are no longer referenced, and are thus
+ * removed from the backing map.
+ */
+ private void pruneStack(Set<HashEntry<K, V>> evicted)
+ {
+ // See section 3.3:
+ // "We define an operation called "stack pruning" on the LIRS
+ // stack S, which removes the HIR blocks in the bottom of
+ // the stack until an LIR block sits in the stack bottom. This
+ // operation serves for two purposes: (1) We ensure the block in
+ // the bottom of the stack always belongs to the LIR block set.
+ // (2) After the LIR block in the bottom is removed, those HIR
+ // blocks contiguously located above it will not have chances to
+ // change their status from HIR to LIR, because their recencies
+ // are larger than the new maximum recency of LIR blocks."
+ LIRSHashEntry<K, V> bottom = stackBottom();
- evicted = new HashSet<HashEntry<K, V>>();
- if (inStack)
- {
- e.transitionToLIRResident();
- switchBottomostLIRtoHIRAndPrune(evicted);
- }
- else
- {
- queue.addLast(e);
- evicted.add(first);
- }
- // evict from segment
- removeFromSegment(evicted);
+ while (bottom != null && bottom.state != Recency.LIR_RESIDENT)
+ {
+ bottom.removeFromStack();
+ if (bottom.state == Recency.HIR_NONRESIDENT)
+ {
+ evicted.add(bottom);
}
+ bottom = stackBottom();
}
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<HashEntry<K, V>> onEntryMiss(HashEntry<K, V> en)
+ {
+ LIRSHashEntry<K, V> e = (LIRSHashEntry<K, V>) en;
+ Set<HashEntry<K, V>> evicted = e.miss();
+ removeFromSegment(evicted);
return evicted;
}
@@ -1050,40 +1498,12 @@
{
for (HashEntry<K, V> e : evicted)
{
+ ((LIRSHashEntry<K, V>)e).evict();
segment.evictionListener.onEntryChosenForEviction(e.value);
segment.remove(e.key, e.hash, null);
}
}
- private void switchBottomostLIRtoHIRAndPrune(Set<HashEntry<K, V>> evicted)
- {
- boolean seenFirstLIR = false;
- for (Iterator<HashEntry<K, V>> i = stack.values().iterator(); i.hasNext();)
- {
- HashEntry<K, V> next = i.next();
- if (next.recency() == Recency.LIR_RESIDENT)
- {
- if (!seenFirstLIR)
- {
- seenFirstLIR = true;
- i.remove();
- currentLIRSize--;
- next.transitionLIRResidentToHIRResident();
- queue.addLast(next);
- }
- else
- {
- break;
- }
- }
- else
- {
- i.remove();
- evicted.add(next);
- }
- }
- }
-
/*
* Invoked without holding a lock on Segment
*/
@@ -1093,7 +1513,7 @@
@Override
public boolean onEntryHit(HashEntry<K, V> e)
{
- accessQueue.add(e);
+ accessQueue.add((LIRSHashEntry<K, V>)e);
return accessQueue.size() >= maxBatchQueueSize * batchThresholdFactor;
}
@@ -1115,12 +1535,7 @@
@Override
public void onEntryRemove(HashEntry<K, V> e)
{
- HashEntry<K, V> removed = stack.remove(e.key);
- if (removed != null && removed.recency() == Recency.LIR_RESIDENT)
- {
- currentLIRSize--;
- }
- queue.remove(e);
+ ((LIRSHashEntry<K, V>)e).remove();
// we could have multiple instances of e in accessQueue; remove them all
while (accessQueue.remove(e))
{
@@ -1134,7 +1549,6 @@
@Override
public void clear()
{
- stack.clear();
accessQueue.clear();
}
@@ -1146,6 +1560,39 @@
{
return Eviction.LIRS;
}
+
+ /**
+ * Returns the entry at the bottom of the stack.
+ */
+ private LIRSHashEntry<K, V> stackBottom()
+ {
+ LIRSHashEntry<K, V> bottom = header.previousInStack;
+ return (bottom == header) ? null : bottom;
+ }
+
+ /**
+ * Returns the entry at the front of the queue.
+ */
+ private LIRSHashEntry<K, V> queueFront()
+ {
+ LIRSHashEntry<K, V> front = header.nextInQueue;
+ return (front == header) ? null : front;
+ }
+
+ /**
+ * Returns the entry at the end of the queue.
+ */
+ private LIRSHashEntry<K, V> queueEnd()
+ {
+ LIRSHashEntry<K, V> end = header.previousInQueue;
+ return (end == header) ? null : end;
+ }
+
+ @Override
+ public HashEntry<K, V> createNewEntry(K key, int hash, HashEntry<K, V> next, V value)
+ {
+ return new LIRSHashEntry<K, V>(this, key, hash, next, value);
+ }
}
/**
@@ -1552,7 +1999,7 @@
first = tab[index];
}
// add a new entry
- tab[index] = new HashEntry<K, V>(key, hash, first, value);
+ tab[index] = eviction.createNewEntry(key, hash, first, value);
// notify a miss
Set<HashEntry<K, V>> newlyEvicted = eviction.onEntryMiss(tab[index]);
if (!newlyEvicted.isEmpty())
@@ -1569,7 +2016,7 @@
}
else
{
- tab[index] = new HashEntry<K, V>(key, hash, first, value);
+ tab[index] = eviction.createNewEntry(key, hash, first, value);
}
}
return oldValue;
@@ -1647,7 +2094,7 @@
{
int k = p.hash & sizeMask;
HashEntry<K, V> n = newTable[k];
- newTable[k] = new HashEntry<K, V>(p.key, p.hash, n, p.value);
+ newTable[k] = eviction.createNewEntry(p.key, p.hash, n, p.value);
}
}
}
@@ -1697,7 +2144,7 @@
{
// allow p to be GC-ed
eviction.onEntryRemove(p);
- newFirst = new HashEntry<K, V>(p.key, p.hash, newFirst, p.value);
+ newFirst = eviction.createNewEntry(p.key, p.hash, newFirst, p.value);
// and notify eviction algorithm about new hash entries
eviction.onEntryMiss(newFirst);
}
More information about the jboss-cvs-commits
mailing list