[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