| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more contributor license |
| * agreements. See the NOTICE file distributed with this work for additional information regarding |
| * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the License. You may obtain a |
| * copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software distributed under the License |
| * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
| * or implied. See the License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| package org.apache.geode.internal.cache.eviction; |
| |
| import java.util.concurrent.atomic.AtomicInteger; |
| |
| import org.apache.logging.log4j.Logger; |
| |
| import org.apache.geode.internal.cache.BucketRegion; |
| import org.apache.geode.internal.cache.versions.RegionVersionVector; |
| import org.apache.geode.internal.logging.log4j.LogMarker; |
| import org.apache.geode.logging.internal.log4j.api.LogService; |
| |
| abstract class AbstractEvictionList implements EvictionList { |
| private static final Logger logger = LogService.getLogger(); |
| |
| /** The last node in the LRU list after which all new nodes are added */ |
| protected final EvictionNode tail = new GuardNode(); |
| |
| /** The starting point in the LRU list for searching for the LRU node */ |
| protected final EvictionNode head = new GuardNode(); |
| |
| /** Counter for the size of the LRU list */ |
| private final AtomicInteger size = new AtomicInteger(); |
| |
| private final EvictionController controller; |
| |
| protected AbstractEvictionList(EvictionController controller) { |
| this.controller = controller; |
| initEmptyList(); |
| } |
| |
| @Override |
| public int size() { |
| return size.get(); |
| } |
| |
| private void incrementSize() { |
| size.incrementAndGet(); |
| } |
| |
| void decrementSize() { |
| // Size could go negative if there is a concurrent clear and |
| // cache updates are in progress. |
| size.decrementAndGet(); |
| } |
| |
| @Override |
| public void closeStats() { |
| getStatistics().close(); |
| } |
| |
| @Override |
| public EvictionCounters getStatistics() { |
| return this.controller.getCounters(); |
| } |
| |
| @Override |
| public void clear(RegionVersionVector regionVersionVector, BucketRegion bucketRegion) { |
| if (regionVersionVector != null) { |
| return; // when concurrency checks are enabled the clear operation removes entries iteratively |
| } |
| |
| synchronized (this) { |
| if (bucketRegion != null) { |
| getStatistics().decrementCounter(bucketRegion.getCounter()); |
| bucketRegion.resetCounter(); |
| } else { |
| getStatistics().resetCounter(); |
| } |
| initEmptyList(); |
| } |
| } |
| |
| private synchronized void initEmptyList() { |
| size.set(0); |
| head.setNext(tail); |
| tail.setPrevious(head); |
| } |
| |
| /** |
| * Adds an lru node to the tail of the list. |
| */ |
| @Override |
| public synchronized void appendEntry(final EvictionNode evictionNode) { |
| if (evictionNode.next() != null) { |
| // already in the list |
| return; |
| } |
| |
| if (logger.isTraceEnabled(LogMarker.LRU_CLOCK_VERBOSE)) { |
| logger.trace(LogMarker.LRU_CLOCK_VERBOSE, "adding a Node to lru list: {}", evictionNode); |
| } |
| |
| evictionNode.setNext(tail); |
| tail.previous().setNext(evictionNode); |
| evictionNode.setPrevious(tail.previous()); |
| tail.setPrevious(evictionNode); |
| |
| incrementSize(); |
| } |
| |
| @Override |
| public synchronized void destroyEntry(EvictionNode evictionNode) { |
| if (logger.isTraceEnabled(LogMarker.LRU_CLOCK_VERBOSE)) { |
| logger.trace(LogMarker.LRU_CLOCK_VERBOSE, "destroyEntry called for {}", evictionNode); |
| } |
| |
| if (removeEntry(evictionNode)) { |
| getStatistics().incDestroys(); |
| } |
| |
| } |
| |
| protected synchronized boolean removeEntry(EvictionNode evictionNode) { |
| if (evictionNode.next() == null) { |
| // not in the list anymore. |
| return false; |
| } |
| |
| unlinkEntry(evictionNode); |
| return true; |
| } |
| |
| protected synchronized void unlinkEntry(EvictionNode evictionNode) { |
| EvictionNode next = evictionNode.next(); |
| EvictionNode previous = evictionNode.previous(); |
| next.setPrevious(previous); |
| previous.setNext(next); |
| evictionNode.setNext(null); |
| evictionNode.setPrevious(null); |
| decrementSize(); |
| } |
| |
| protected synchronized EvictionNode unlinkTailEntry() { |
| EvictionNode evictionNode = tail.previous(); |
| if (evictionNode == head) { |
| return null; // end of eviction list |
| } |
| |
| unlinkEntry(evictionNode); |
| return evictionNode; |
| } |
| |
| /** |
| * Remove and return the head entry in the list |
| */ |
| protected synchronized EvictionNode unlinkHeadEntry() { |
| EvictionNode evictionNode = head.next(); |
| if (evictionNode == tail) { |
| return null; // end of list |
| } |
| |
| unlinkEntry(evictionNode); |
| return evictionNode; |
| } |
| |
| protected boolean isEvictable(EvictionNode evictionNode) { |
| if (evictionNode.isEvicted()) { |
| if (logger.isTraceEnabled(LogMarker.LRU_CLOCK_VERBOSE)) { |
| logger.trace(LogMarker.LRU_CLOCK_VERBOSE, |
| "discarding evicted entry"); |
| } |
| return false; |
| } |
| |
| // If this Entry is part of a transaction, skip it since |
| // eviction should not cause commit conflicts |
| synchronized (evictionNode) { |
| if (evictionNode.isInUseByTransaction()) { |
| if (logger.isTraceEnabled(LogMarker.LRU_CLOCK_VERBOSE)) { |
| logger.trace(LogMarker.LRU_CLOCK_VERBOSE, |
| "removing transactional entry from consideration"); |
| } |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| } |