/* * Copyright Terracotta, Inc. * * Licensed 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.ehcache.impl.internal.store.disk; import org.ehcache.config.EvictionAdvisor; import org.ehcache.core.spi.function.BiFunction; import org.ehcache.core.spi.function.Function; import org.ehcache.impl.internal.store.disk.factories.EhcachePersistentSegmentFactory; import org.ehcache.impl.internal.store.offheap.EhcacheOffHeapBackingMap; import org.terracotta.offheapstore.Metadata; import org.terracotta.offheapstore.MetadataTuple; import org.terracotta.offheapstore.Segment; import org.terracotta.offheapstore.disk.persistent.AbstractPersistentConcurrentOffHeapCache; import java.io.IOException; import java.io.ObjectInput; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import static org.ehcache.impl.internal.store.offheap.factories.EhcacheSegmentFactory.EhcacheSegment.ADVISED_AGAINST_EVICTION; import static org.terracotta.offheapstore.Metadata.PINNED; import static org.terracotta.offheapstore.MetadataTuple.metadataTuple; /** * * @author Chris Dennis */ public class EhcachePersistentConcurrentOffHeapClockCache<K, V> extends AbstractPersistentConcurrentOffHeapCache<K, V> implements EhcacheOffHeapBackingMap<K, V> { private final EvictionAdvisor<? super K, ? super V> evictionAdvisor; private final AtomicLong[] counters; public EhcachePersistentConcurrentOffHeapClockCache(ObjectInput input, EvictionAdvisor<? super K, ? super V> evictionAdvisor, EhcachePersistentSegmentFactory<K, V> segmentFactory) throws IOException { this(evictionAdvisor, segmentFactory, readSegmentCount(input)); } public EhcachePersistentConcurrentOffHeapClockCache(EvictionAdvisor<? super K, ? super V> evictionAdvisor, EhcachePersistentSegmentFactory<K, V> segmentFactory, int concurrency) { super(segmentFactory, concurrency); this.evictionAdvisor = evictionAdvisor; this.counters = new AtomicLong[segments.length]; for(int i = 0; i < segments.length; i++) { counters[i] = new AtomicLong(); } } public long allocatedMemory() { long total = 0L; for (Segment<K, V> segment : segments) { total += segment.getAllocatedMemory(); } return total; } public long occupiedMemory() { long total = 0L; for (Segment<K, V> segment : segments) { total += segment.getOccupiedMemory(); } return total; } public long dataAllocatedMemory() { long total = 0L; for (Segment<K, V> segment : segments) { total += segment.getDataAllocatedMemory(); } return total; } public long dataOccupiedMemory() { long total = 0L; for (Segment<K, V> segment : segments) { total += segment.getDataOccupiedMemory(); } return total; } public long dataSize() { long total = 0L; for (Segment<K, V> segment : segments) { total += segment.getDataSize(); } return total; } public long longSize() { long total = 0L; for (Segment<K, V> segment : segments) { total += segment.getSize(); } return total; } public long tableCapacity() { long total = 0L; for (Segment<K, V> segment : segments) { total += segment.getTableCapacity(); } return total; } public long usedSlotCount() { long total = 0L; for (Segment<K, V> segment : segments) { total += segment.getUsedSlotCount(); } return total; } public long removedSlotCount() { long total = 0L; for (Segment<K, V> segment : segments) { total += segment.getRemovedSlotCount(); } return total; } public long reprobeLength() { long total = 0L; for (Segment<K, V> segment : segments) { total += segment.getReprobeLength(); } return total; } public long vitalMemory() { long total = 0L; for (Segment<K, V> segment : segments) { total += segment.getVitalMemory(); } return total; } public long dataVitalMemory() { long total = 0L; for (Segment<K, V> segment : segments) { total += segment.getDataVitalMemory(); } return total; } @Override public V compute(K key, final BiFunction<K, V, V> mappingFunction, final boolean pin) { MetadataTuple<V> result = computeWithMetadata(key, new org.terracotta.offheapstore.jdk8.BiFunction<K, MetadataTuple<V>, MetadataTuple<V>>() { @Override public MetadataTuple<V> apply(K k, MetadataTuple<V> current) { V oldValue = current == null ? null : current.value(); V newValue = mappingFunction.apply(k, oldValue); if (newValue == null) { return null; } else if (oldValue == newValue) { return metadataTuple(newValue, (pin ? PINNED : 0) | current.metadata()); } else { return metadataTuple(newValue, (pin ? PINNED : 0) | (evictionAdvisor.adviseAgainstEviction(k, newValue) ? ADVISED_AGAINST_EVICTION : 0)); } } }); return result == null ? null : result.value(); } @Override public V computeIfPresent(K key, final BiFunction<K, V, V> mappingFunction) { MetadataTuple<V> result = computeIfPresentWithMetadata(key, new org.terracotta.offheapstore.jdk8.BiFunction<K, MetadataTuple<V>, MetadataTuple<V>>() { @Override public MetadataTuple<V> apply(K k, MetadataTuple<V> current) { V oldValue = current.value(); V newValue = mappingFunction.apply(k, oldValue); if (newValue == null) { return null; } else if (oldValue == newValue) { return current; } else { return metadataTuple(newValue, (evictionAdvisor.adviseAgainstEviction(k, newValue) ? ADVISED_AGAINST_EVICTION : 0)); } } }); return result == null ? null : result.value(); } @Override public V computeIfPresentAndPin(final K key, final BiFunction<K, V, V> mappingFunction) { MetadataTuple<V> result = computeIfPresentWithMetadata(key, new org.terracotta.offheapstore.jdk8.BiFunction<K, MetadataTuple<V>, MetadataTuple<V>>() { @Override public MetadataTuple<V> apply(K k, MetadataTuple<V> current) { V oldValue = current.value(); V newValue = mappingFunction.apply(k, oldValue); if (newValue == null) { return null; } else if (oldValue == newValue) { return metadataTuple(newValue, PINNED | current.metadata()); } else { return metadataTuple(newValue, PINNED | (evictionAdvisor.adviseAgainstEviction(k, newValue) ? ADVISED_AGAINST_EVICTION : 0)); } } }); return result == null ? null : result.value(); } @Override public boolean computeIfPinned(final K key, final BiFunction<K,V,V> remappingFunction, final Function<V,Boolean> unpinFunction) { final AtomicBoolean unpin = new AtomicBoolean(); computeIfPresentWithMetadata(key, new org.terracotta.offheapstore.jdk8.BiFunction<K, MetadataTuple<V>, MetadataTuple<V>>() { @Override public MetadataTuple<V> apply(K k, MetadataTuple<V> current) { if ((current.metadata() & Metadata.PINNED) != 0) { V oldValue = current.value(); V newValue = remappingFunction.apply(k, oldValue); Boolean unpinLocal = unpinFunction.apply(oldValue); if (newValue == null) { unpin.set(true); return null; } else if (oldValue == newValue) { unpin.set(unpinLocal); return metadataTuple(oldValue, current.metadata() & (unpinLocal ? ~Metadata.PINNED : -1)); } else { unpin.set(false); return metadataTuple(newValue, (evictionAdvisor.adviseAgainstEviction(k, newValue) ? ADVISED_AGAINST_EVICTION : 0)); } } else { return current; } } }); return unpin.get(); } @Override public long nextIdFor(final K key) { return counters[getIndexFor(key.hashCode())].getAndIncrement(); } }