From d0787795b602782aebbd219681b5207cb47c8bc7 Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Wed, 29 Jan 2014 17:23:50 +0400 Subject: [PATCH] StorageManager supports centralized exception handling --- .../jet/storage/StorageManagerTest.java | 56 +++++++++++++++++++ .../jet/storage/LockBasedStorageManager.java | 46 +++++++++++++-- 2 files changed, 96 insertions(+), 6 deletions(-) diff --git a/compiler/tests/org/jetbrains/jet/storage/StorageManagerTest.java b/compiler/tests/org/jetbrains/jet/storage/StorageManagerTest.java index 2441ee59a56..5419f0ba79f 100644 --- a/compiler/tests/org/jetbrains/jet/storage/StorageManagerTest.java +++ b/compiler/tests/org/jetbrains/jet/storage/StorageManagerTest.java @@ -4,6 +4,8 @@ import jet.Function0; import jet.Function1; import jet.Unit; import junit.framework.TestCase; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.util.ReenteringLazyValueComputationException; import java.util.ArrayList; @@ -426,6 +428,60 @@ public class StorageManagerTest extends TestCase { assertEquals(2, c.getCount()); } + // ExceptionHandlingStrategy + + public void testExceptionHandlingStrategyForLazyValues() throws Exception { + class RethrownException extends RuntimeException {} + + LockBasedStorageManager m = LockBasedStorageManager.createWithExceptionHandling(new LockBasedStorageManager.ExceptionHandlingStrategy() { + @NotNull + @Override + public RuntimeException handleException(@NotNull Throwable throwable) { + throw new RethrownException(); + } + }); + try { + m.createLazyValue( + new Function0() { + @Nullable + @Override + public Object invoke() { + throw new RuntimeException(); + } + } + ).invoke(); + fail("Exception should have occurred"); + } + catch (RethrownException ignored) { + } + } + + public void testExceptionHandlingStrategyForMemoizedFunctions() throws Exception { + class RethrownException extends RuntimeException {} + + LockBasedStorageManager m = LockBasedStorageManager.createWithExceptionHandling(new LockBasedStorageManager.ExceptionHandlingStrategy() { + @NotNull + @Override + public RuntimeException handleException(@NotNull Throwable throwable) { + throw new RethrownException(); + } + }); + try { + m.createMemoizedFunction( + new Function1() { + @Nullable + @Override + public Object invoke(@Nullable Object o) { + throw new RuntimeException(); + } + } + ).invoke(""); + fail("Exception should have occurred"); + } + catch (RethrownException ignored) { + } + } + // toString() public void testToString() throws Exception { 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 d933405b466..fa20aa183eb 100644 --- a/core/util.runtime/src/org/jetbrains/jet/storage/LockBasedStorageManager.java +++ b/core/util.runtime/src/org/jetbrains/jet/storage/LockBasedStorageManager.java @@ -31,7 +31,27 @@ import java.util.concurrent.locks.ReentrantLock; public class LockBasedStorageManager implements StorageManager { - public static final StorageManager NO_LOCKS = new LockBasedStorageManager("NO_LOCKS", NoLock.INSTANCE) { + public interface ExceptionHandlingStrategy { + ExceptionHandlingStrategy THROW = new ExceptionHandlingStrategy() { + @NotNull + @Override + public RuntimeException handleException(@NotNull Throwable throwable) { + throw ExceptionUtils.rethrow(throwable); + } + }; + + /* + * The signature of this method is a trick: it is used as + * + * throw strategy.handleException(...) + * + * most implementations of this method throw exceptions themselves, so it does not matter what they return + */ + @NotNull + RuntimeException handleException(@NotNull Throwable throwable); + } + + public static final StorageManager NO_LOCKS = new LockBasedStorageManager("NO_LOCKS", ExceptionHandlingStrategy.THROW, NoLock.INSTANCE) { @NotNull @Override protected RecursionDetectedResult recursionDetectedDefault() { @@ -39,16 +59,30 @@ public class LockBasedStorageManager implements StorageManager { } }; + public static LockBasedStorageManager createWithExceptionHandling(@NotNull ExceptionHandlingStrategy exceptionHandlingStrategy) { + return new LockBasedStorageManager(exceptionHandlingStrategy); + } + protected final Lock lock; + private final ExceptionHandlingStrategy exceptionHandlingStrategy; private final String debugText; - private LockBasedStorageManager(@NotNull String debugText, @NotNull Lock lock) { + private LockBasedStorageManager( + @NotNull String debugText, + @NotNull ExceptionHandlingStrategy exceptionHandlingStrategy, + @NotNull Lock lock + ) { this.lock = lock; + this.exceptionHandlingStrategy = exceptionHandlingStrategy; this.debugText = debugText; } public LockBasedStorageManager() { - this(getPointOfConstruction(), new ReentrantLock()); + this(getPointOfConstruction(), ExceptionHandlingStrategy.THROW, new ReentrantLock()); + } + + private LockBasedStorageManager(@NotNull ExceptionHandlingStrategy exceptionHandlingStrategy) { + this(getPointOfConstruction(), exceptionHandlingStrategy, new ReentrantLock()); } private static String getPointOfConstruction() { @@ -276,7 +310,7 @@ public class LockBasedStorageManager implements StorageManager { // Store only if it's a genuine result, not something thrown through recursionDetected() value = WrappedValues.escapeThrowable(throwable); } - throw ExceptionUtils.rethrow(throwable); + throw exceptionHandlingStrategy.handleException(throwable); } } finally { @@ -352,12 +386,12 @@ public class LockBasedStorageManager implements StorageManager { return typedValue; } catch (Throwable throwable) { - if (throwable == error) throw error; + if (throwable == error) throw exceptionHandlingStrategy.handleException(throwable); Object oldValue = cache.put(input, WrappedValues.escapeThrowable(throwable)); assert oldValue == NotValue.COMPUTING : "Race condition detected on input " + input + ". Old value is " + oldValue; - throw ExceptionUtils.rethrow(throwable); + throw exceptionHandlingStrategy.handleException(throwable); } } finally {