Support recursion-tolerant nullable values
All other values are made recursion-intolerant
This commit is contained in:
+32
-1
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user