Introduce fall-through handling of recursion in lazy values

Fixed EA-52272 - NA: LockBasedStorageManager$LockBasedLazyValue.recursionDetected
This commit is contained in:
Andrey Breslav
2013-12-20 17:40:37 +04:00
parent 44e055e698
commit ac191a7dbb
2 changed files with 87 additions and 12 deletions
@@ -384,6 +384,27 @@ public class StorageManagerTest extends TestCase {
assertEquals("second", c.rec.invoke());
}
public void testFallThrough() throws Exception {
final CounterImpl c = new CounterImpl();
class C {
NotNullLazyValue<Integer> rec = LockBasedStorageManager.NO_LOCKS.createLazyValue(new Function0<Integer>() {
@Override
public Integer invoke() {
c.inc();
if (c.getCount() < 2) {
return rec.invoke();
}
else {
return c.getCount();
}
}
});
}
assertEquals(2, new C().rec.invoke().intValue());
assertEquals(2, c.getCount());
}
// Utilities
private static <K, V> Function0<V> apply(final Function1<K, V> f, final K x) {
@@ -32,6 +32,12 @@ import java.util.concurrent.locks.ReentrantLock;
public class LockBasedStorageManager implements StorageManager {
public static final StorageManager NO_LOCKS = new LockBasedStorageManager(NoLock.INSTANCE) {
@NotNull
@Override
protected <T> RecursionDetectedResult<T> recursionDetectedDefault() {
return RecursionDetectedResult.fallThrough();
}
@Override
public String toString() {
return "NO_LOCKS";
@@ -88,9 +94,10 @@ public class LockBasedStorageManager implements StorageManager {
@NotNull Function0<T> computable, @NotNull final T onRecursiveCall
) {
return new LockBasedNotNullLazyValue<T>(computable) {
@NotNull
@Override
protected T recursionDetected(boolean firstTime) {
return onRecursiveCall;
protected RecursionDetectedResult<T> recursionDetected(boolean firstTime) {
return RecursionDetectedResult.value(onRecursiveCall);
}
};
}
@@ -103,13 +110,13 @@ public class LockBasedStorageManager implements StorageManager {
@NotNull final Function1<T, Unit> postCompute
) {
return new LockBasedNotNullLazyValue<T>(computable) {
@Nullable
@NotNull
@Override
protected T recursionDetected(boolean firstTime) {
protected RecursionDetectedResult<T> recursionDetected(boolean firstTime) {
if (onRecursiveCall == null) {
return super.recursionDetected(firstTime);
}
return onRecursiveCall.invoke(firstTime);
return RecursionDetectedResult.value(onRecursiveCall.invoke(firstTime));
}
@Override
@@ -129,9 +136,10 @@ public class LockBasedStorageManager implements StorageManager {
@Override
public <T> NullableLazyValue<T> createRecursionTolerantNullableLazyValue(@NotNull Function0<T> computable, final T onRecursiveCall) {
return new LockBasedLazyValue<T>(computable) {
@NotNull
@Override
protected T recursionDetected(boolean firstTime) {
return onRecursiveCall;
protected RecursionDetectedResult<T> recursionDetected(boolean firstTime) {
return RecursionDetectedResult.value(onRecursiveCall);
}
};
}
@@ -160,6 +168,46 @@ public class LockBasedStorageManager implements StorageManager {
}
}
@NotNull
protected <T> RecursionDetectedResult<T> recursionDetectedDefault() {
throw new IllegalStateException("Recursive call in a lazy value");
}
private static class RecursionDetectedResult<T> {
@NotNull
public static <T> RecursionDetectedResult<T> value(T value) {
return new RecursionDetectedResult<T>(value, false);
}
@NotNull
public static <T> RecursionDetectedResult<T> fallThrough() {
return new RecursionDetectedResult<T>(null, true);
}
private final T value;
private final boolean fallThrough;
private RecursionDetectedResult(T value, boolean fallThrough) {
this.value = value;
this.fallThrough = fallThrough;
}
public T getValue() {
assert !fallThrough : "A value requested from FALL_THROUGH ";
return value;
}
public boolean isFallThrough() {
return fallThrough;
}
@Override
public String toString() {
return isFallThrough() ? "FALL_THROUGH" : String.valueOf(value);
}
}
private enum NotValue {
NOT_COMPUTED,
COMPUTING,
@@ -194,11 +242,17 @@ public class LockBasedStorageManager implements StorageManager {
if (_value == NotValue.COMPUTING) {
value = NotValue.RECURSION_WAS_DETECTED;
return recursionDetected(/*firstTime = */ true);
RecursionDetectedResult<T> result = recursionDetected(/*firstTime = */ true);
if (!result.isFallThrough()) {
return result.getValue();
}
}
if (_value == NotValue.RECURSION_WAS_DETECTED) {
return recursionDetected(/*firstTime = */ false);
RecursionDetectedResult<T> result = recursionDetected(/*firstTime = */ false);
if (!result.isFallThrough()) {
return result.getValue();
}
}
value = NotValue.COMPUTING;
@@ -225,9 +279,9 @@ public class LockBasedStorageManager implements StorageManager {
* @param firstTime {@code true} when recursion has been just detected, {@code false} otherwise
* @return a value to be returned on a recursive call or subsequent calls
*/
@Nullable
protected T recursionDetected(boolean firstTime) {
throw new IllegalStateException("Recursive call in a lazy value");
@NotNull
protected RecursionDetectedResult<T> recursionDetected(boolean firstTime) {
return recursionDetectedDefault();
}
protected void postCompute(T value) {