001package org.cache2k.impl;
002
003/*
004 * #%L
005 * cache2k core package
006 * %%
007 * Copyright (C) 2000 - 2015 headissue GmbH, Munich
008 * %%
009 * This program is free software: you can redistribute it and/or modify
010 * it under the terms of the GNU General Public License as
011 * published by the Free Software Foundation, either version 3 of the 
012 * License, or (at your option) any later version.
013 * 
014 * This program is distributed in the hope that it will be useful,
015 * but WITHOUT ANY WARRANTY; without even the implied warranty of
016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
017 * GNU General Public License for more details.
018 * 
019 * You should have received a copy of the GNU General Public 
020 * License along with this program.  If not, see
021 * <http://www.gnu.org/licenses/gpl-3.0.html>.
022 * #L%
023 */
024
025/**
026 * Straight forward CLOCK implementation. This is probably the simplest
027 * eviction algorithm around. Interestingly it produces good results.
028 *
029 * @author Jens Wilke; created: 2013-12-01
030 */
031public class ClockCache<K, T> extends LockFreeCache<ClockCache.Entry, K, T> {
032
033  long hits;
034  int runCnt;
035  int scan24hCnt;
036  int scanCnt;
037  int size;
038
039  Entry hand;
040
041  @Override
042  public long getHitCnt() {
043    return hits + sumUpListHits(hand);
044  }
045
046  private int sumUpListHits(Entry e) {
047    if (e == null) { return 0; }
048    int cnt = 0;
049    Entry _head = e;
050    do {
051      cnt += e.hitCnt;
052      e = (Entry) e.prev;
053    } while (e != _head);
054    return cnt;
055  }
056
057  @Override
058  protected void initializeHeapCache() {
059    super.initializeHeapCache();
060    size = 0;
061    hand = null;
062  }
063
064  @Override
065  protected void removeEntryFromReplacementList(Entry e) {
066    hand = removeFromCyclicList(hand, e);
067    size--;
068  }
069
070  private int getListSize() {
071    return size;
072  }
073
074  @Override
075  protected void recordHit(Entry e) {
076    e.hitCnt++;
077  }
078
079  @Override
080  protected void insertIntoReplacementList(Entry e) {
081    size++;
082    hand = insertIntoTailCyclicList(hand, e);
083  }
084
085  @Override
086  protected Entry newEntry() {
087    return new Entry();
088  }
089
090  /**
091   * Run to evict an entry.
092   */
093  @Override
094  protected Entry findEvictionCandidate() {
095    runCnt++;
096    int _scanCnt = 0;
097
098    while (hand.hitCnt > 0) {
099      _scanCnt++;
100      hits += hand.hitCnt;
101      hand.hitCnt = 0;
102      hand = (Entry) hand.next;
103    }
104    if (_scanCnt > size) {
105      scan24hCnt++;
106    }
107    scanCnt += _scanCnt;
108    return hand;
109  }
110
111  @Override
112  protected IntegrityState getIntegrityState() {
113    synchronized (lock) {
114      return super.getIntegrityState()
115              .checkEquals("getListSize() + evictedButInHashCnt == getSize()", getListSize() + evictedButInHashCnt, getLocalSize())
116              .check("checkCyclicListIntegrity(hand)", checkCyclicListIntegrity(hand))
117              .checkEquals("getCyclicListEntryCount(hand) == size", getCyclicListEntryCount(hand), size);
118    }
119  }
120
121  @Override
122  protected String getExtraStatistics() {
123    return  ", clockRunCnt=" + runCnt +
124            ", scanCnt=" + scanCnt +
125            ", scan24hCnt=" + scan24hCnt;
126  }
127
128  static class Entry<K, T> extends org.cache2k.impl.Entry<Entry, K, T> {
129
130    int hitCnt;
131
132  }
133
134}