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);
|
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
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public <T> NullableLazyValue<T> createNullableLazyValueWithPostCompute(
|
public <T> NullableLazyValue<T> createNullableLazyValueWithPostCompute(
|
||||||
@@ -103,6 +114,9 @@ public class LockBasedStorageManager implements StorageManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static class LockBasedLazyValue<T> implements NullableLazyValue<T> {
|
private static class LockBasedLazyValue<T> implements NullableLazyValue<T> {
|
||||||
|
|
||||||
|
private static final Object COMPUTING = new Object();
|
||||||
|
|
||||||
private final Object lock;
|
private final Object lock;
|
||||||
private final Computable<T> computable;
|
private final Computable<T> computable;
|
||||||
|
|
||||||
@@ -117,12 +131,20 @@ public class LockBasedStorageManager implements StorageManager {
|
|||||||
@Override
|
@Override
|
||||||
public T compute() {
|
public T compute() {
|
||||||
Object _value = value;
|
Object _value = value;
|
||||||
if (_value != null) return WrappedValues.unescapeExceptionOrNull(_value);
|
if (_value != null && _value != COMPUTING) return WrappedValues.unescapeExceptionOrNull(_value);
|
||||||
|
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
_value = value;
|
_value = value;
|
||||||
|
if (_value == COMPUTING) {
|
||||||
|
Object result = recursionDetected();
|
||||||
|
if (result != null) {
|
||||||
|
return WrappedValues.unescapeExceptionOrNull(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (_value != null) return WrappedValues.unescapeExceptionOrNull(_value);
|
if (_value != null) return WrappedValues.unescapeExceptionOrNull(_value);
|
||||||
|
|
||||||
|
value = COMPUTING;
|
||||||
try {
|
try {
|
||||||
T typedValue = computable.compute();
|
T typedValue = computable.compute();
|
||||||
value = WrappedValues.escapeNull(typedValue);
|
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) {
|
protected void postCompute(T value) {
|
||||||
// Doing something in post-compute helps prevent infinite recursion
|
// Doing something in post-compute helps prevent infinite recursion
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,6 +48,12 @@ public interface StorageManager {
|
|||||||
@NotNull
|
@NotNull
|
||||||
<T> NullableLazyValue<T> createNullableLazyValue(@NotNull Computable<T> computable);
|
<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
|
* {@code postCompute} is called after the value is computed, but before any other thread sees it (the current thread may
|
||||||
* see it in between)
|
* see it in between)
|
||||||
|
|||||||
Reference in New Issue
Block a user