From ac191a7dbb78b6d3892d3779a33196c30efce5dd Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Fri, 20 Dec 2013 17:40:37 +0400 Subject: [PATCH] Introduce fall-through handling of recursion in lazy values Fixed EA-52272 - NA: LockBasedStorageManager$LockBasedLazyValue.recursionDetected --- .../jet/storage/StorageManagerTest.java | 21 +++++ .../jet/storage/LockBasedStorageManager.java | 78 ++++++++++++++++--- 2 files changed, 87 insertions(+), 12 deletions(-) diff --git a/compiler/tests/org/jetbrains/jet/storage/StorageManagerTest.java b/compiler/tests/org/jetbrains/jet/storage/StorageManagerTest.java index 3debea3cb93..0e2c39eff35 100644 --- a/compiler/tests/org/jetbrains/jet/storage/StorageManagerTest.java +++ b/compiler/tests/org/jetbrains/jet/storage/StorageManagerTest.java @@ -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 rec = LockBasedStorageManager.NO_LOCKS.createLazyValue(new Function0() { + @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 Function0 apply(final Function1 f, final K x) { diff --git a/core/util.runtime/src/org/jetbrains/jet/storage/LockBasedStorageManager.java b/core/util.runtime/src/org/jetbrains/jet/storage/LockBasedStorageManager.java index 7b10cb5e84b..47fc5af18ca 100644 --- a/core/util.runtime/src/org/jetbrains/jet/storage/LockBasedStorageManager.java +++ b/core/util.runtime/src/org/jetbrains/jet/storage/LockBasedStorageManager.java @@ -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 RecursionDetectedResult recursionDetectedDefault() { + return RecursionDetectedResult.fallThrough(); + } + @Override public String toString() { return "NO_LOCKS"; @@ -88,9 +94,10 @@ public class LockBasedStorageManager implements StorageManager { @NotNull Function0 computable, @NotNull final T onRecursiveCall ) { return new LockBasedNotNullLazyValue(computable) { + @NotNull @Override - protected T recursionDetected(boolean firstTime) { - return onRecursiveCall; + protected RecursionDetectedResult recursionDetected(boolean firstTime) { + return RecursionDetectedResult.value(onRecursiveCall); } }; } @@ -103,13 +110,13 @@ public class LockBasedStorageManager implements StorageManager { @NotNull final Function1 postCompute ) { return new LockBasedNotNullLazyValue(computable) { - @Nullable + @NotNull @Override - protected T recursionDetected(boolean firstTime) { + protected RecursionDetectedResult 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 NullableLazyValue createRecursionTolerantNullableLazyValue(@NotNull Function0 computable, final T onRecursiveCall) { return new LockBasedLazyValue(computable) { + @NotNull @Override - protected T recursionDetected(boolean firstTime) { - return onRecursiveCall; + protected RecursionDetectedResult recursionDetected(boolean firstTime) { + return RecursionDetectedResult.value(onRecursiveCall); } }; } @@ -160,6 +168,46 @@ public class LockBasedStorageManager implements StorageManager { } } + @NotNull + protected RecursionDetectedResult recursionDetectedDefault() { + throw new IllegalStateException("Recursive call in a lazy value"); + } + + private static class RecursionDetectedResult { + + @NotNull + public static RecursionDetectedResult value(T value) { + return new RecursionDetectedResult(value, false); + } + + @NotNull + public static RecursionDetectedResult fallThrough() { + return new RecursionDetectedResult(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 result = recursionDetected(/*firstTime = */ true); + if (!result.isFallThrough()) { + return result.getValue(); + } } if (_value == NotValue.RECURSION_WAS_DETECTED) { - return recursionDetected(/*firstTime = */ false); + RecursionDetectedResult 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 recursionDetected(boolean firstTime) { + return recursionDetectedDefault(); } protected void postCompute(T value) {