001 /*
002 * Copyright 2010-2013 JetBrains s.r.o.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017 package org.jetbrains.jet.lang.resolve.lazy.storage;
018
019 import com.intellij.openapi.util.Computable;
020 import com.intellij.util.Consumer;
021 import com.intellij.util.Function;
022 import com.intellij.util.containers.ConcurrentWeakValueHashMap;
023 import org.jetbrains.annotations.NotNull;
024 import org.jetbrains.annotations.Nullable;
025 import org.jetbrains.jet.lang.diagnostics.Diagnostic;
026 import org.jetbrains.jet.lang.resolve.BindingContext;
027 import org.jetbrains.jet.lang.resolve.BindingTrace;
028 import org.jetbrains.jet.util.slicedmap.ReadOnlySlice;
029 import org.jetbrains.jet.util.slicedmap.WritableSlice;
030 import org.jetbrains.jet.utils.Nulls;
031
032 import java.util.Collection;
033 import java.util.concurrent.ConcurrentHashMap;
034 import java.util.concurrent.ConcurrentMap;
035
036 public class LockBasedStorageManager implements StorageManager {
037
038 private final Object lock = new Object() {
039 @Override
040 public String toString() {
041 return "LockBasedStorageManager centralized lock";
042 }
043 };
044
045 @NotNull
046 @Override
047 public <K, V> MemoizedFunctionToNotNull<K, V> createMemoizedFunction(
048 @NotNull Function<K, V> compute, @NotNull ReferenceKind valuesReferenceKind
049 ) {
050 ConcurrentMap<K, Object> map = createConcurrentMap(valuesReferenceKind);
051 return new MapBasedMemoizedFunctionToNotNull<K, V>(lock, map, compute);
052 }
053
054 @NotNull
055 @Override
056 public <K, V> MemoizedFunctionToNullable<K, V> createMemoizedFunctionWithNullableValues(
057 @NotNull Function<K, V> compute, @NotNull ReferenceKind valuesReferenceKind
058 ) {
059 ConcurrentMap<K, Object> map = createConcurrentMap(valuesReferenceKind);
060 return new MapBasedMemoizedFunction<K, V>(lock, map, compute);
061 }
062
063 private static <K, V> ConcurrentMap<K, V> createConcurrentMap(ReferenceKind referenceKind) {
064 return (referenceKind == ReferenceKind.WEAK) ? new ConcurrentWeakValueHashMap<K, V>() : new ConcurrentHashMap<K, V>();
065 }
066
067 @NotNull
068 @Override
069 public <T> NotNullLazyValue<T> createLazyValue(@NotNull Computable<T> computable) {
070 return new LockBasedNotNullLazyValue<T>(lock, computable);
071 }
072
073 @NotNull
074 @Override
075 public <T> NotNullLazyValue<T> createLazyValueWithPostCompute(@NotNull Computable<T> computable, @NotNull final Consumer<T> postCompute) {
076 return new LockBasedNotNullLazyValue<T>(lock, computable) {
077 @Override
078 protected void postCompute(@NotNull T value) {
079 postCompute.consume(value);
080 }
081 };
082 }
083
084 @NotNull
085 @Override
086 public <T> NullableLazyValue<T> createNullableLazyValue(@NotNull Computable<T> computable) {
087 return new LockBasedLazyValue<T>(lock, computable);
088 }
089
090 @NotNull
091 @Override
092 public <T> NullableLazyValue<T> createNullableLazyValueWithPostCompute(
093 @NotNull Computable<T> computable, @NotNull final Consumer<T> postCompute
094 ) {
095 return new LockBasedLazyValue<T>(lock, computable) {
096 @Override
097 protected void postCompute(@Nullable T value) {
098 postCompute.consume(value);
099 }
100 };
101 }
102
103 @NotNull
104 @Override
105 public BindingTrace createSafeTrace(@NotNull BindingTrace originalTrace) {
106 // It seems safe to have a separate lock for traces:
107 // no other locks will be acquired inside the trace operations
108 return new LockProtectedTrace(lock, originalTrace);
109 }
110
111 private static class LockBasedLazyValue<T> implements NullableLazyValue<T> {
112 private final Object lock;
113 private final Computable<T> computable;
114
115 @Nullable
116 private volatile Object value = null;
117
118 public LockBasedLazyValue(@NotNull Object lock, @NotNull Computable<T> computable) {
119 this.lock = lock;
120 this.computable = computable;
121 }
122
123 @Override
124 public T compute() {
125 Object _value = value;
126 if (_value != null) return Nulls.unescape(_value);
127
128 synchronized (lock) {
129 _value = value;
130 if (_value != null) return Nulls.unescape(_value);
131
132 T typedValue = computable.compute();
133 value = Nulls.escape(typedValue);
134
135 postCompute(typedValue);
136
137 return typedValue;
138 }
139 }
140
141 protected void postCompute(T value) {
142 // Doing something in post-compute helps prevent infinite recursion
143 }
144 }
145
146 private static class LockBasedNotNullLazyValue<T> extends LockBasedLazyValue<T> implements NotNullLazyValue<T> {
147
148 public LockBasedNotNullLazyValue(@NotNull Object lock, @NotNull Computable<T> computable) {
149 super(lock, computable);
150 }
151
152 @Override
153 @NotNull
154 public T compute() {
155 T result = super.compute();
156 assert result != null : "compute() returned null";
157 return result;
158 }
159 }
160
161 private static class MapBasedMemoizedFunction<K, V> implements MemoizedFunctionToNullable<K, V> {
162 private final Object lock;
163 private final ConcurrentMap<K, Object> cache;
164 private final Function<K, V> compute;
165
166 public MapBasedMemoizedFunction(@NotNull Object lock, @NotNull ConcurrentMap<K, Object> map, @NotNull Function<K, V> compute) {
167 this.lock = lock;
168 this.cache = map;
169 this.compute = compute;
170 }
171
172 @Override
173 @Nullable
174 public V fun(@NotNull K input) {
175 Object value = cache.get(input);
176 if (value != null) return Nulls.unescape(value);
177
178 synchronized (lock) {
179 value = cache.get(input);
180 if (value != null) return Nulls.unescape(value);
181
182 V typedValue = compute.fun(input);
183
184 Object oldValue = cache.put(input, Nulls.escape(typedValue));
185 assert oldValue == null : "Race condition detected";
186
187 return typedValue;
188 }
189 }
190 }
191
192 private static class MapBasedMemoizedFunctionToNotNull<K, V> extends MapBasedMemoizedFunction<K, V> implements MemoizedFunctionToNotNull<K, V> {
193
194 public MapBasedMemoizedFunctionToNotNull(
195 @NotNull Object lock,
196 @NotNull ConcurrentMap<K, Object> map,
197 @NotNull Function<K, V> compute
198 ) {
199 super(lock, map, compute);
200 }
201
202 @NotNull
203 @Override
204 public V fun(@NotNull K input) {
205 V result = super.fun(input);
206 assert result != null : "compute() returned null";
207 return result;
208 }
209 }
210
211 private static class LockProtectedTrace implements BindingTrace {
212 private final Object lock;
213 private final BindingTrace trace;
214
215 public LockProtectedTrace(@NotNull Object lock, @NotNull BindingTrace trace) {
216 this.lock = lock;
217 this.trace = trace;
218 }
219
220 @Override
221 public BindingContext getBindingContext() {
222 synchronized (lock) {
223 return trace.getBindingContext();
224 }
225 }
226
227 @Override
228 public <K, V> void record(WritableSlice<K, V> slice, K key, V value) {
229 synchronized (lock) {
230 trace.record(slice, key, value);
231 }
232 }
233
234 @Override
235 public <K> void record(WritableSlice<K, Boolean> slice, K key) {
236 synchronized (lock) {
237 trace.record(slice, key);
238 }
239 }
240
241 @Override
242 @Nullable
243 public <K, V> V get(ReadOnlySlice<K, V> slice, K key) {
244 synchronized (lock) {
245 return trace.get(slice, key);
246 }
247 }
248
249 @Override
250 @NotNull
251 public <K, V> Collection<K> getKeys(WritableSlice<K, V> slice) {
252 synchronized (lock) {
253 return trace.getKeys(slice);
254 }
255 }
256
257 @Override
258 public void report(@NotNull Diagnostic diagnostic) {
259 synchronized (lock) {
260 trace.report(diagnostic);
261 }
262 }
263 }
264
265 }