removed copy/paste code relating to expiration after write

This commit is contained in:
Adrian Cole 2012-07-02 15:27:31 -07:00
parent 17fce87661
commit 07240f3e77
3 changed files with 40 additions and 64 deletions

View File

@ -18,7 +18,7 @@
*/ */
package org.jclouds.rest.suppliers; package org.jclouds.rest.suppliers;
import static org.jclouds.util.Suppliers2.memoizeWithExpirationOnAbsoluteInterval; import static org.jclouds.util.Suppliers2.memoizeWithExpirationAfterWrite;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
@ -54,7 +54,7 @@ public class MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<T> imp
public MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier( public MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier(
AtomicReference<AuthorizationException> authException, long seconds, Supplier<T> delegate) { AtomicReference<AuthorizationException> authException, long seconds, Supplier<T> delegate) {
this.delegate = memoizeWithExpirationOnAbsoluteInterval(new RetryOnTimeOutExceptionSupplier<T>( this.delegate = memoizeWithExpirationAfterWrite(new RetryOnTimeOutExceptionSupplier<T>(
new SetAndThrowAuthorizationExceptionSupplier<T>(delegate, authException)), seconds, TimeUnit.SECONDS); new SetAndThrowAuthorizationExceptionSupplier<T>(delegate, authException)), seconds, TimeUnit.SECONDS);
this.seconds = seconds; this.seconds = seconds;
} }

View File

@ -26,12 +26,14 @@ import java.io.Serializable;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Objects; import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.base.Suppliers; import com.google.common.base.Suppliers;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ForwardingObject;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.io.OutputSupplier; import com.google.common.io.OutputSupplier;
@ -85,70 +87,43 @@ public class Suppliers2 {
} }
/** /**
* See Supplier.memoizeWithExpiration. * same as {@link Supplier.memoizeWithExpiration} except that the expiration ticker starts after
* write vs after call to {@code get}.
* *
* Difference between this impl and v11.0 is that we fix * @see Supplier.memoizeWithExpiration
* http://code.google.com/p/guava-libraries/issues/detail?id=857.
*/ */
public static <T> Supplier<T> memoizeWithExpirationOnAbsoluteInterval(Supplier<T> delegate, long duration, public static <T> Supplier<T> memoizeWithExpirationAfterWrite(Supplier<T> delegate, long duration, TimeUnit unit) {
TimeUnit unit) { return new ExpireAfterWriteSupplier<T>(delegate, duration, unit);
return new ExpiringMemoizingSupplier<T>(delegate, duration, unit);
} }
@VisibleForTesting static class ExpireAfterWriteSupplier<T> extends ForwardingObject implements Supplier<T>, Serializable {
static class ExpiringMemoizingSupplier<T> implements Supplier<T>, Serializable { private final Supplier<T> delegate;
final Supplier<T> delegate; private final long duration;
final long durationNanos; private final TimeUnit unit;
transient volatile T value; private final LoadingCache<Object, T> cache;
// The special value 0 means "not yet initialized".
transient volatile long expirationNanos;
ExpiringMemoizingSupplier(Supplier<T> delegate, long duration, TimeUnit unit) { public ExpireAfterWriteSupplier(Supplier<T> delegate, long duration, TimeUnit unit) {
this.delegate = Preconditions.checkNotNull(delegate); this.delegate = delegate;
this.durationNanos = unit.toNanos(duration); this.duration = duration;
Preconditions.checkArgument(duration > 0); this.unit = unit;
cache = CacheBuilder.newBuilder().expireAfterWrite(duration, unit).build(CacheLoader.from(delegate));
}
@Override
protected Supplier<T> delegate() {
return delegate;
} }
@Override @Override
public T get() { public T get() {
// Another variant of Double Checked Locking. return cache.getUnchecked("FOO");
//
// We use two volatile reads. We could reduce this to one by
// putting our fields into a holder class, but (at least on x86)
// the extra memory consumption and indirection are more
// expensive than the extra volatile reads.
long nanos = expirationNanos;
long now = System.nanoTime();
if (nanos == 0 || now - nanos >= 0) {
synchronized (this) {
if (nanos == expirationNanos) { // recheck for lost race
// Set value to null prior to retrieving new val, so old and new are not held in
// memory simultaneously
value = null;
T t = delegate.get();
value = t;
// Update now so that, if call was expensive, we keep value for the full duration
now = System.nanoTime();
nanos = now + durationNanos;
// In the very unlikely event that nanos is 0, set it to 1;
// no one will notice 1 ns of tardiness.
expirationNanos = (nanos == 0) ? 1 : nanos;
return t;
}
}
}
return value;
} }
private static final long serialVersionUID = 0; private static final long serialVersionUID = 0;
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hashCode(delegate, durationNanos); return Objects.hashCode(delegate, duration, unit);
} }
@Override @Override
@ -159,13 +134,14 @@ public class Suppliers2 {
return false; return false;
if (getClass() != obj.getClass()) if (getClass() != obj.getClass())
return false; return false;
ExpiringMemoizingSupplier<?> that = ExpiringMemoizingSupplier.class.cast(obj); ExpireAfterWriteSupplier<?> that = ExpireAfterWriteSupplier.class.cast(obj);
return Objects.equal(delegate, that.delegate) && Objects.equal(durationNanos, that.durationNanos); return Objects.equal(delegate, that.delegate) && Objects.equal(duration, that.duration);
} }
@Override @Override
public String toString() { public String toString() {
return Objects.toStringHelper(this).add("delegate", delegate).add("durationNanos", durationNanos).toString(); return Objects.toStringHelper(this).add("delegate", delegate).add("duration", duration).add("unit", unit)
.toString();
} }
} }

View File

@ -74,7 +74,7 @@ public class Suppliers2Test {
} }
}; };
Supplier<Integer> memoizedSupplier = Suppliers2.memoizeWithExpirationOnAbsoluteInterval( Supplier<Integer> memoizedSupplier = Suppliers2.memoizeWithExpirationAfterWrite(
slowSupplier, EXPIRATION_TIME, TimeUnit.MILLISECONDS); slowSupplier, EXPIRATION_TIME, TimeUnit.MILLISECONDS);
assertEquals(memoizedSupplier.get(), (Integer)10); assertEquals(memoizedSupplier.get(), (Integer)10);
@ -100,7 +100,7 @@ public class Suppliers2Test {
public void testMemoizeWithExpiration() throws InterruptedException { public void testMemoizeWithExpiration() throws InterruptedException {
CountingSupplier countingSupplier = new CountingSupplier(); CountingSupplier countingSupplier = new CountingSupplier();
Supplier<Integer> memoizedSupplier = Suppliers2.memoizeWithExpirationOnAbsoluteInterval( Supplier<Integer> memoizedSupplier = Suppliers2.memoizeWithExpirationAfterWrite(
countingSupplier, 75, TimeUnit.MILLISECONDS); countingSupplier, 75, TimeUnit.MILLISECONDS);
checkExpiration(countingSupplier, memoizedSupplier); checkExpiration(countingSupplier, memoizedSupplier);
@ -111,7 +111,7 @@ public class Suppliers2Test {
throws InterruptedException { throws InterruptedException {
CountingSupplier countingSupplier = new CountingSupplier(); CountingSupplier countingSupplier = new CountingSupplier();
Supplier<Integer> memoizedSupplier = Suppliers2.memoizeWithExpirationOnAbsoluteInterval( Supplier<Integer> memoizedSupplier = Suppliers2.memoizeWithExpirationAfterWrite(
countingSupplier, 75, TimeUnit.MILLISECONDS); countingSupplier, 75, TimeUnit.MILLISECONDS);
// Calls to the original memoized supplier shouldn't affect its copy. // Calls to the original memoized supplier shouldn't affect its copy.
memoizedSupplier.get(); memoizedSupplier.get();
@ -120,7 +120,7 @@ public class Suppliers2Test {
memoizedSupplier.get(); memoizedSupplier.get();
CountingSupplier countingCopy = (CountingSupplier) CountingSupplier countingCopy = (CountingSupplier)
((Suppliers2.ExpiringMemoizingSupplier<Integer>) copy).delegate; ((Suppliers2.ExpireAfterWriteSupplier<Integer>) copy).delegate();
checkExpiration(countingCopy, copy); checkExpiration(countingCopy, copy);
} }
@ -154,7 +154,7 @@ public class Suppliers2Test {
Function<Supplier<Boolean>, Supplier<Boolean>> memoizer = Function<Supplier<Boolean>, Supplier<Boolean>> memoizer =
new Function<Supplier<Boolean>, Supplier<Boolean>>() { new Function<Supplier<Boolean>, Supplier<Boolean>>() {
@Override public Supplier<Boolean> apply(Supplier<Boolean> supplier) { @Override public Supplier<Boolean> apply(Supplier<Boolean> supplier) {
return Suppliers2.memoizeWithExpirationOnAbsoluteInterval( return Suppliers2.memoizeWithExpirationAfterWrite(
supplier, Long.MAX_VALUE, TimeUnit.NANOSECONDS); supplier, Long.MAX_VALUE, TimeUnit.NANOSECONDS);
} }
}; };