All storage-related code moved to util.runtime

This commit is contained in:
Andrey Breslav
2013-09-30 20:04:40 +04:00
parent 48605074aa
commit f3cd83c744
42 changed files with 79 additions and 72 deletions
@@ -0,0 +1,273 @@
/*
* Copyright 2010-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.storage;
import com.intellij.openapi.util.Computable;
import com.intellij.util.Consumer;
import com.intellij.util.Function;
import com.intellij.util.containers.ConcurrentWeakValueHashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.utils.ExceptionUtils;
import org.jetbrains.jet.utils.WrappedValues;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class LockBasedStorageManager implements StorageManager {
protected final Object lock = new Object() {
@Override
public String toString() {
return "LockBasedStorageManager centralized lock";
}
};
@NotNull
@Override
public <K, V> MemoizedFunctionToNotNull<K, V> createMemoizedFunction(
@NotNull Function<K, V> compute, @NotNull ReferenceKind valuesReferenceKind
) {
ConcurrentMap<K, Object> map = createConcurrentMap(valuesReferenceKind);
return new MapBasedMemoizedFunctionToNotNull<K, V>(lock, map, compute);
}
@NotNull
@Override
public <K, V> MemoizedFunctionToNullable<K, V> createMemoizedFunctionWithNullableValues(
@NotNull Function<K, V> compute, @NotNull ReferenceKind valuesReferenceKind
) {
ConcurrentMap<K, Object> map = createConcurrentMap(valuesReferenceKind);
return new MapBasedMemoizedFunction<K, V>(lock, map, compute);
}
private static <K, V> ConcurrentMap<K, V> createConcurrentMap(ReferenceKind referenceKind) {
return (referenceKind == ReferenceKind.WEAK) ? new ConcurrentWeakValueHashMap<K, V>() : new ConcurrentHashMap<K, V>();
}
@NotNull
@Override
public <T> NotNullLazyValue<T> createLazyValue(@NotNull Computable<T> computable) {
return new LockBasedNotNullLazyValue<T>(lock, computable);
}
@NotNull
@Override
public <T> NotNullLazyValue<T> createRecursionTolerantLazyValue(
@NotNull Computable<T> computable, @NotNull final T onRecursiveCall
) {
return new LockBasedNotNullLazyValue<T>(lock, computable) {
@Override
protected Object recursionDetected() {
return onRecursiveCall;
}
};
}
@NotNull
@Override
public <T> NotNullLazyValue<T> createLazyValueWithPostCompute(
@NotNull Computable<T> computable,
final Computable<Object> onRecursiveCall,
@NotNull final Consumer<T> postCompute
) {
return new LockBasedNotNullLazyValue<T>(lock, computable) {
@Nullable
@Override
protected Object recursionDetected() {
if (onRecursiveCall == null) {
return super.recursionDetected();
}
return onRecursiveCall.compute();
}
@Override
protected void postCompute(@NotNull T value) {
postCompute.consume(value);
}
};
}
@NotNull
@Override
public <T> NullableLazyValue<T> createNullableLazyValue(@NotNull Computable<T> computable) {
return new LockBasedLazyValue<T>(lock, computable);
}
@NotNull
@Override
public <T> NullableLazyValue<T> createRecursionTolerantNullableLazyValue(@NotNull Computable<T> computable, final T onRecursiveCall) {
return new LockBasedLazyValue<T>(lock, computable) {
@Override
protected Object recursionDetected() {
return onRecursiveCall;
}
};
}
@NotNull
@Override
public <T> NullableLazyValue<T> createNullableLazyValueWithPostCompute(
@NotNull Computable<T> computable, @NotNull final Consumer<T> postCompute
) {
return new LockBasedLazyValue<T>(lock, computable) {
@Override
protected void postCompute(@Nullable T value) {
postCompute.consume(value);
}
};
}
@Override
public <T> T compute(@NotNull Computable<T> computable) {
synchronized (lock) {
return computable.compute();
}
}
private static class LockBasedLazyValue<T> implements NullableLazyValue<T> {
private static final Object NOT_COMPUTED = new Object();
private static final Object COMPUTING = new Object();
private final Object lock;
private final Computable<T> computable;
@Nullable
private volatile Object value = NOT_COMPUTED;
public LockBasedLazyValue(@NotNull Object lock, @NotNull Computable<T> computable) {
this.lock = lock;
this.computable = computable;
}
@Override
public T compute() {
Object _value = value;
if (_value != NOT_COMPUTED && _value != COMPUTING) return WrappedValues.unescapeThrowable(_value);
synchronized (lock) {
_value = value;
if (_value == COMPUTING) {
Object result = recursionDetected();
if (result != NOT_COMPUTED) {
return WrappedValues.unescapeThrowable(result);
}
}
if (_value != NOT_COMPUTED) return WrappedValues.unescapeThrowable(_value);
value = COMPUTING;
try {
T typedValue = computable.compute();
value = typedValue;
postCompute(typedValue);
return typedValue;
}
catch (Throwable throwable) {
value = WrappedValues.escapeThrowable(throwable);
throw ExceptionUtils.rethrow(throwable);
}
}
}
/**
* @return {@code NOT_COMPUTED} to proceed, a value or wrapped exception 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) {
// Doing something in post-compute helps prevent infinite recursion
}
}
private static class LockBasedNotNullLazyValue<T> extends LockBasedLazyValue<T> implements NotNullLazyValue<T> {
public LockBasedNotNullLazyValue(@NotNull Object lock, @NotNull Computable<T> computable) {
super(lock, computable);
}
@Override
@NotNull
public T compute() {
T result = super.compute();
assert result != null : "compute() returned null";
return result;
}
}
private static class MapBasedMemoizedFunction<K, V> implements MemoizedFunctionToNullable<K, V> {
private final Object lock;
private final ConcurrentMap<K, Object> cache;
private final Function<K, V> compute;
public MapBasedMemoizedFunction(@NotNull Object lock, @NotNull ConcurrentMap<K, Object> map, @NotNull Function<K, V> compute) {
this.lock = lock;
this.cache = map;
this.compute = compute;
}
@Override
@Nullable
public V fun(@NotNull K input) {
Object value = cache.get(input);
if (value != null) return WrappedValues.unescapeExceptionOrNull(value);
synchronized (lock) {
value = cache.get(input);
if (value != null) return WrappedValues.unescapeExceptionOrNull(value);
try {
V typedValue = compute.fun(input);
Object oldValue = cache.put(input, WrappedValues.escapeNull(typedValue));
assert oldValue == null : "Race condition detected";
return typedValue;
}
catch (Throwable throwable) {
Object oldValue = cache.put(input, WrappedValues.escapeThrowable(throwable));
assert oldValue == null : "Race condition detected";
throw ExceptionUtils.rethrow(throwable);
}
}
}
}
private static class MapBasedMemoizedFunctionToNotNull<K, V> extends MapBasedMemoizedFunction<K, V> implements MemoizedFunctionToNotNull<K, V> {
public MapBasedMemoizedFunctionToNotNull(
@NotNull Object lock,
@NotNull ConcurrentMap<K, Object> map,
@NotNull Function<K, V> compute
) {
super(lock, map, compute);
}
@NotNull
@Override
public V fun(@NotNull K input) {
V result = super.fun(input);
assert result != null : "compute() returned null";
return result;
}
}
}
@@ -0,0 +1,26 @@
/*
* Copyright 2010-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.storage;
import com.intellij.util.Function;
import org.jetbrains.annotations.NotNull;
public interface MemoizedFunctionToNotNull<P, R> extends Function<P, R> {
@Override
@NotNull
R fun(P p);
}
@@ -0,0 +1,44 @@
/*
* Copyright 2010-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.storage;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
public abstract class MemoizedFunctionToNotNullImpl<K, V> extends MemoizedFunctionToNullableImpl<K, V> implements
MemoizedFunctionToNotNull<K, V> {
public MemoizedFunctionToNotNullImpl() {
}
public MemoizedFunctionToNotNullImpl(@NotNull Map<K, Object> map) {
super(map);
}
@NotNull
@Override
public V fun(K input) {
V result = super.fun(input);
assert result != null : "compute() returned null";
return result;
}
@NotNull
@Override
protected abstract V doCompute(K input);
}
@@ -0,0 +1,26 @@
/*
* Copyright 2010-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.storage;
import com.intellij.util.Function;
import org.jetbrains.annotations.Nullable;
public interface MemoizedFunctionToNullable<P, R> extends Function<P, R> {
@Override
@Nullable
R fun(P p);
}
@@ -0,0 +1,54 @@
/*
* Copyright 2010-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.storage;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.storage.MemoizedFunctionToNullable;
import org.jetbrains.jet.utils.WrappedValues;
import java.util.HashMap;
import java.util.Map;
public abstract class MemoizedFunctionToNullableImpl<K, V> implements MemoizedFunctionToNullable<K, V> {
private final Map<K, Object> cache;
public MemoizedFunctionToNullableImpl() {
this(new HashMap<K, Object>());
}
public MemoizedFunctionToNullableImpl(@NotNull Map<K, Object> map) {
this.cache = map;
}
@Nullable
@Override
public V fun(K input) {
Object value = cache.get(input);
if (value != null) return WrappedValues.unescapeNull(value);
V typedValue = doCompute(input);
Object oldValue = cache.put(input, WrappedValues.escapeNull(typedValue));
assert oldValue == null : "Race condition detected";
return typedValue;
}
@Nullable
protected abstract V doCompute(K input);
}
@@ -0,0 +1,26 @@
/*
* Copyright 2010-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.storage;
import com.intellij.openapi.util.Computable;
import org.jetbrains.annotations.NotNull;
public interface NotNullLazyValue<T> extends Computable<T> {
@Override
@NotNull
T compute();
}
@@ -0,0 +1,34 @@
/*
* Copyright 2010-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.storage;
import org.jetbrains.annotations.NotNull;
public abstract class NotNullLazyValueImpl<T> extends NullableLazyValueImpl<T> implements NotNullLazyValue<T> {
@NotNull
@Override
public T compute() {
T result = super.compute();
assert result != null : "compute() returned null";
return result;
}
@NotNull
@Override
protected abstract T doCompute();
}
@@ -0,0 +1,26 @@
/*
* Copyright 2010-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.storage;
import com.intellij.openapi.util.Computable;
import org.jetbrains.annotations.Nullable;
public interface NullableLazyValue<T> extends Computable<T> {
@Override
@Nullable
T compute();
}
@@ -0,0 +1,45 @@
/*
* Copyright 2010-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.storage;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.storage.NullableLazyValue;
import org.jetbrains.jet.utils.WrappedValues;
public abstract class NullableLazyValueImpl<T> implements NullableLazyValue<T> {
@Nullable
private Object value = null;
@Override
public T compute() {
Object _value = value;
if (_value != null) return WrappedValues.unescapeNull(_value);
T typedValue = doCompute();
value = WrappedValues.escapeNull(typedValue);
postCompute(typedValue);
return typedValue;
}
protected abstract T doCompute();
protected void postCompute(T value) {
// Doing something in post-compute helps prevent infinite recursion
}
}
@@ -0,0 +1,78 @@
/*
* Copyright 2010-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.storage;
import com.intellij.openapi.util.Computable;
import com.intellij.util.Consumer;
import com.intellij.util.Function;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public interface StorageManager {
/**
* Given a function compute: K -> V create a memoized version of it that computes a value only once for each key
* @param compute the function to be memoized
* @param valuesReferenceKind how to store the memoized values
*
* NOTE: if compute() has side-effects the WEAK reference kind is dangerous: the side-effects will be repeated if
* the value gets collected and then re-computed
*/
@NotNull
<K, V> MemoizedFunctionToNotNull<K, V> createMemoizedFunction(@NotNull Function<K, V> compute, @NotNull ReferenceKind valuesReferenceKind);
@NotNull
<K, V> MemoizedFunctionToNullable<K, V> createMemoizedFunctionWithNullableValues(@NotNull Function<K, V> compute, @NotNull ReferenceKind valuesReferenceKind);
@NotNull
<T> NotNullLazyValue<T> createLazyValue(@NotNull Computable<T> computable);
@NotNull
<T> NotNullLazyValue<T> createRecursionTolerantLazyValue(@NotNull Computable<T> computable, @NotNull T onRecursiveCall);
/**
* @param onRecursiveCall is called if the computation calls itself recursively.
* If this parameter is null, an exception will be thrown on a recursive call,
* otherwise it should return a result of WrappedValues.escapeThrowable() method
* @param postCompute is called after the value is computed, but before any other thread sees it (the current thread may
* see it in between)
*/
@NotNull
<T> NotNullLazyValue<T> createLazyValueWithPostCompute(
@NotNull Computable<T> computable,
@Nullable Computable<Object> onRecursiveCall,
@NotNull Consumer<T> postCompute
);
@NotNull
<T> NullableLazyValue<T> createNullableLazyValue(@NotNull Computable<T> computable);
@NotNull
<T> NullableLazyValue<T> createRecursionTolerantNullableLazyValue(@NotNull Computable<T> computable, @Nullable T onRecursiveCall);
/**
* {@code postCompute} is called after the value is computed, but before any other thread sees it (the current thread may
* see it in between)
*/
@NotNull
<T> NullableLazyValue<T> createNullableLazyValueWithPostCompute(@NotNull Computable<T> computable, @NotNull Consumer<T> postCompute);
<T> T compute(@NotNull Computable<T> computable);
enum ReferenceKind {
STRONG,
WEAK
}
}