diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/LazyValue.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/LazyValue.java index 32b5225b303..969434d576a 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/LazyValue.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/LazyValue.java @@ -16,12 +16,6 @@ package org.jetbrains.jet.lang.resolve.lazy; -import org.jetbrains.annotations.NotNull; - public interface LazyValue { - /** - * Use Optional<T> - */ - @NotNull T get(); } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/LockBasedStorageManager.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/LockBasedStorageManager.java index 19f58da2b1f..f6ea17e2c4b 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/LockBasedStorageManager.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/LockBasedStorageManager.java @@ -52,6 +52,19 @@ public class LockBasedStorageManager implements StorageManager { @NotNull @Override public Function createMemoizedFunction(@NotNull final Function compute, @NotNull final MemoizationMode mode) { + return createMemoizedFunction(compute, mode, false); + } + + @NotNull + @Override + public Function createMemoizedFunctionWithNullableValues( + @NotNull Function compute, @NotNull MemoizationMode modeForValues + ) { + return createMemoizedFunction(compute, modeForValues, true); + } + + @NotNull + private Function createMemoizedFunction(final Function compute, final MemoizationMode mode, final boolean nullable) { return new Function() { private final ConcurrentMap> cache; { @@ -63,12 +76,13 @@ public class LockBasedStorageManager implements StorageManager { LazyValue lazyValue = cache.get(input); if (lazyValue != null) return lazyValue.get(); - lazyValue = createLazyValue(new Computable() { + Computable computable = new Computable() { @Override public V compute() { return compute.fun(input); } - }); + }; + lazyValue = nullable ? createNullableLazyValue(computable) : createLazyValue(computable); LazyValue oldValue = cache.putIfAbsent(input, lazyValue); if (oldValue != null) return oldValue.get(); @@ -89,6 +103,12 @@ public class LockBasedStorageManager implements StorageManager { return new LockBasedLazyValue(lock, computable); } + @NotNull + @Override + public LazyValue createNullableLazyValue(@NotNull Computable computable) { + return new LockBasedNullableLazyValue(lock, computable); + } + @NotNull @Override public BindingTrace createSafeTrace(@NotNull BindingTrace originalTrace) { @@ -128,6 +148,45 @@ public class LockBasedStorageManager implements StorageManager { } } + private static class LockBasedNullableLazyValue implements LazyValue { + private final Object lock; + private final Computable computable; + + private volatile boolean computed = false; + @Nullable + private volatile T value = null; + + public LockBasedNullableLazyValue(@NotNull Object lock, @NotNull Computable computable) { + this.lock = lock; + this.computable = computable; + } + + @Override + @Nullable + public T get() { + // NOTE: no local variables used here, because they would not reduce the number of volatile reads/writes + + // We want to guarantee that whenever computed = true, value is not null + // First, read computed, then read value + if (computed) { + return value; + } + + synchronized (lock) { + if (!computed) { + T _value = computable.compute(); + + // First write value, then write computed + value = _value; + computed = true; + + return _value; + } + return value; + } + } + } + private static class LockProtectedTrace implements BindingTrace { private final Object lock; private final BindingTrace trace; diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/StorageManager.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/StorageManager.java index d1ebb20dedd..ec8e6f3ab49 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/StorageManager.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/StorageManager.java @@ -35,12 +35,17 @@ public interface StorageManager { */ @NotNull Function createMemoizedFunction(@NotNull Function compute, @NotNull MemoizationMode modeForValues); + @NotNull + Function createMemoizedFunctionWithNullableValues(@NotNull Function compute, @NotNull MemoizationMode modeForValues); Collection createConcurrentCollection(); @NotNull LazyValue createLazyValue(@NotNull Computable computable); + @NotNull + LazyValue createNullableLazyValue(@NotNull Computable computable); + @NotNull BindingTrace createSafeTrace(@NotNull BindingTrace originalTrace);