Support recursion-tolerant nullable values

All other values are made recursion-intolerant
This commit is contained in:
Andrey Breslav
2013-09-26 12:40:19 -07:00
parent 9fea289585
commit bbdff45246
2 changed files with 38 additions and 1 deletions
@@ -82,6 +82,17 @@ public class LockBasedStorageManager implements StorageManager {
return new LockBasedLazyValue<T>(lock, computable);
}
@NotNull
@Override
public <T> NullableLazyValue<T> createRecursionTolerantNullableLazyValue(@NotNull Computable<T> computable) {
return new LockBasedLazyValue<T>(lock, computable) {
@Override
protected Object recursionDetected() {
return WrappedValues.escapeNull(null);
}
};
}
@NotNull
@Override
public <T> NullableLazyValue<T> createNullableLazyValueWithPostCompute(
@@ -103,6 +114,9 @@ public class LockBasedStorageManager implements StorageManager {
}
private static class LockBasedLazyValue<T> implements NullableLazyValue<T> {
private static final Object COMPUTING = new Object();
private final Object lock;
private final Computable<T> computable;
@@ -117,12 +131,20 @@ public class LockBasedStorageManager implements StorageManager {
@Override
public T compute() {
Object _value = value;
if (_value != null) return WrappedValues.unescapeExceptionOrNull(_value);
if (_value != null && _value != COMPUTING) return WrappedValues.unescapeExceptionOrNull(_value);
synchronized (lock) {
_value = value;
if (_value == COMPUTING) {
Object result = recursionDetected();
if (result != null) {
return WrappedValues.unescapeExceptionOrNull(result);
}
}
if (_value != null) return WrappedValues.unescapeExceptionOrNull(_value);
value = COMPUTING;
try {
T typedValue = computable.compute();
value = WrappedValues.escapeNull(typedValue);
@@ -136,6 +158,15 @@ public class LockBasedStorageManager implements StorageManager {
}
}
/**
* @return {@code null} to proceed, a wrapped value otherwise, see WrappedValues
* @throws DO NOT throw exceptions from implementations of this method, instead return WrappedValues.escapeThrowable(exception)
*/
@Nullable
protected Object recursionDetected() {
return WrappedValues.escapeThrowable(new IllegalStateException("Recursive call in a lazy value"));
}
protected void postCompute(T value) {
// Doing something in post-compute helps prevent infinite recursion
}
@@ -48,6 +48,12 @@ public interface StorageManager {
@NotNull
<T> NullableLazyValue<T> createNullableLazyValue(@NotNull Computable<T> computable);
/**
* If recursion is detected, respective calls to compute() simply return null
*/
@NotNull
<T> NullableLazyValue<T> createRecursionTolerantNullableLazyValue(@NotNull Computable<T> computable);
/**
* {@code postCompute} is called after the value is computed, but before any other thread sees it (the current thread may
* see it in between)