class LRUListWithSyncSorting extends AbstractEvictionList {

  @Override
  public EvictableEntry getEvictableEntry() {
    long numEvals = 0;

    for (;;) {
      EvictionNode aNode = this.unlinkHeadEntry();

      if (logger.isTraceEnabled(LogMarker.LRU_CLOCK_VERBOSE)) {
        logger.trace(LogMarker.LRU_CLOCK_VERBOSE, "lru considering {}", aNode);
      }

      if (aNode == null) { // hit the end of the list
        getStatistics().incEvaluations(numEvals);
        return null;
      }

      numEvals++;

      if (!isEvictable(aNode)) {
        continue;
      }

      // At this point we have any acceptable entry. Now
      // use various criteria to determine if it's good enough
      // to return, or if we need to add it back to the list.
      if (maxEntries > 0 && numEvals > maxEntries) {
        if (logger.isTraceEnabled(LogMarker.LRU_CLOCK_VERBOSE)) {
          logger.trace(LogMarker.LRU_CLOCK_VERBOSE, "greedily picking an available entry");
        }
        getStatistics().incGreedyReturns(1);
        // fall through, return this node
      } else if (aNode.isRecentlyUsed()) {
        if (logger.isTraceEnabled(LogMarker.LRU_CLOCK_VERBOSE)) {
          logger.trace(LogMarker.LRU_CLOCK_VERBOSE, "skipping recently used entry {}", aNode);
        }
        aNode.unsetRecentlyUsed();
        appendEntry(aNode);
        continue; // keep looking
      } else {
        if (logger.isTraceEnabled(LogMarker.LRU_CLOCK_VERBOSE)) {
          logger.trace(LogMarker.LRU_CLOCK_VERBOSE, "returning unused entry: {}", aNode);
        }
        // fall through, return this node
      }

      // Return the current node.
      getStatistics().incEvaluations(numEvals);
      return (EvictableEntry) aNode;
    } // synchronized
  }

  public  LRUListWithSyncSorting(EvictionController controller);

}