/* * 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.core.spi.store; import org.ehcache.expiry.Duration; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLongFieldUpdater; import static java.lang.String.format; /** * @author Ludovic Orban */ public abstract class AbstractValueHolder<V> implements Store.ValueHolder<V> { private static final AtomicLongFieldUpdater<AbstractValueHolder> HITS_UPDATER = AtomicLongFieldUpdater.newUpdater(AbstractValueHolder.class, "hits"); private final long id; private final long creationTime; private volatile long lastAccessTime; private volatile long expirationTime; private volatile long hits; private static final AtomicLongFieldUpdater<AbstractValueHolder> ACCESSTIME_UPDATER = AtomicLongFieldUpdater.newUpdater(AbstractValueHolder.class, "lastAccessTime"); private static final AtomicLongFieldUpdater<AbstractValueHolder> EXPIRATIONTIME_UPDATER = AtomicLongFieldUpdater.newUpdater(AbstractValueHolder.class, "expirationTime"); protected AbstractValueHolder(long id, long creationTime) { this(id, creationTime, NO_EXPIRE); } protected AbstractValueHolder(long id, long creationTime, long expirationTime) { this.id = id; this.creationTime = creationTime; this.expirationTime = expirationTime; this.lastAccessTime = creationTime; } protected abstract TimeUnit nativeTimeUnit(); @Override public long creationTime(TimeUnit unit) { return unit.convert(creationTime, nativeTimeUnit()); } public void setExpirationTime(long expirationTime, TimeUnit unit) { if (expirationTime == NO_EXPIRE) { updateExpirationTime(NO_EXPIRE); } else if (expirationTime <= 0) { throw new IllegalArgumentException("invalid expiration time: " + expirationTime); } else { updateExpirationTime(nativeTimeUnit().convert(expirationTime, unit)); } } private void updateExpirationTime(long update) { while (true) { long current = this.expirationTime; if (current >= update) { break; } if (EXPIRATIONTIME_UPDATER.compareAndSet(this, current, update)) { break; } }; } public void accessed(long now, Duration expiration) { final TimeUnit timeUnit = nativeTimeUnit(); if (expiration != null) { if (expiration.isInfinite()) { setExpirationTime(Store.ValueHolder.NO_EXPIRE, null); } else { long millis = timeUnit.convert(expiration.getLength(), expiration.getTimeUnit()); long newExpirationTime ; if (millis == Long.MAX_VALUE) { newExpirationTime = Long.MAX_VALUE; } else { newExpirationTime = now + millis; if (newExpirationTime < 0) { newExpirationTime = Long.MAX_VALUE; } } setExpirationTime(newExpirationTime, timeUnit); } } setLastAccessTime(now, timeUnit); HITS_UPDATER.getAndIncrement(this); } @Override public long expirationTime(TimeUnit unit) { final long expire = this.expirationTime; if (expire == NO_EXPIRE) { return NO_EXPIRE; } return unit.convert(expire, nativeTimeUnit()); } @Override public boolean isExpired(long expirationTime, TimeUnit unit) { final long expire = this.expirationTime; if (expire == NO_EXPIRE) { return false; } return expire <= nativeTimeUnit().convert(expirationTime, unit); } @Override public long lastAccessTime(TimeUnit unit) { return unit.convert(lastAccessTime, nativeTimeUnit()); } public void setLastAccessTime(long lastAccessTime, TimeUnit unit) { long update = unit.convert(lastAccessTime, nativeTimeUnit()); while (true) { long current = this.lastAccessTime; if (current >= update) { break; } if (ACCESSTIME_UPDATER.compareAndSet(this, current, update)) { break; } }; } @Override public int hashCode() { int result = 1; result = 31 * result + (int)(creationTime ^ (creationTime >>> 32)); result = 31 * result + (int)(lastAccessTime ^ (lastAccessTime >>> 32)); result = 31 * result + (int)(expirationTime ^ (expirationTime >>> 32)); return result; } @Override public boolean equals(Object obj) { if (obj instanceof AbstractValueHolder) { AbstractValueHolder<?> other = (AbstractValueHolder<?>) obj; return other.creationTime(nativeTimeUnit()) == creationTime && creationTime(other.nativeTimeUnit()) == other.creationTime && other.expirationTime(nativeTimeUnit()) == expirationTime && expirationTime(other.nativeTimeUnit()) == other.expirationTime && other.lastAccessTime(nativeTimeUnit()) == lastAccessTime && lastAccessTime(other.nativeTimeUnit()) == other.lastAccessTime; } return false; } @Override public float hitRate(long now, TimeUnit unit) { final long endTime = TimeUnit.NANOSECONDS.convert(now, TimeUnit.MILLISECONDS); final long startTime = TimeUnit.NANOSECONDS.convert(creationTime, nativeTimeUnit()); float duration = (endTime - startTime)/(float)TimeUnit.NANOSECONDS.convert(1, unit); return (hits/duration); } @Override public long hits() { return this.hits; } protected void setHits(long hits) { HITS_UPDATER.set(this, hits); } @Override public long getId() { return id; } @Override public String toString() { return format("%s", value()); } }