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.storage;
018    
019    import com.google.common.collect.ImmutableMap;
020    import com.intellij.util.containers.ConcurrentWeakValueHashMap;
021    import kotlin.Function0;
022    import kotlin.Function1;
023    import kotlin.Unit;
024    import org.jetbrains.annotations.NotNull;
025    import org.jetbrains.annotations.Nullable;
026    import org.jetbrains.annotations.TestOnly;
027    import org.jetbrains.jet.lang.diagnostics.Diagnostic;
028    import org.jetbrains.jet.lang.resolve.BindingContext;
029    import org.jetbrains.jet.lang.resolve.BindingTrace;
030    import org.jetbrains.jet.lang.resolve.Diagnostics;
031    import org.jetbrains.jet.util.slicedmap.ReadOnlySlice;
032    import org.jetbrains.jet.util.slicedmap.WritableSlice;
033    
034    import java.util.Collection;
035    import java.util.concurrent.locks.Lock;
036    
037    // This class is kept under the same package as LockBasedStorageManager to get access to its protected members
038    // Otherwise wed have to expose the lock which is worse than have such a hackish class placement
039    public class LockBasedLazyResolveStorageManager implements LazyResolveStorageManager {
040    
041        private final LockBasedStorageManager storageManager;
042    
043        public LockBasedLazyResolveStorageManager(@NotNull LockBasedStorageManager storageManager) {
044            this.storageManager = storageManager;
045        }
046    
047        @Override
048        @NotNull
049        public <K, V> MemoizedFunctionToNotNull<K, V> createWeaklyRetainedMemoizedFunction(
050                @NotNull Function1<K, V> compute
051        ) {
052            return storageManager.createMemoizedFunction(compute, new ConcurrentWeakValueHashMap<K, Object>());
053        }
054    
055        @NotNull
056        @Override
057        public <K, V> MemoizedFunctionToNullable<K, V> createWeaklyRetainedMemoizedFunctionWithNullableValues(
058                @NotNull Function1<K, V> compute
059        ) {
060            return storageManager.createMemoizedFunctionWithNullableValues(compute, new ConcurrentWeakValueHashMap<K, Object>());
061        }
062    
063        @NotNull
064        @Override
065        public BindingTrace createSafeTrace(@NotNull BindingTrace originalTrace) {
066            // It seems safe to have a separate lock for traces:
067            // no other locks will be acquired inside the trace operations
068            return new LockProtectedTrace(storageManager.lock, originalTrace);
069        }
070    
071        @NotNull
072        @Override
073        public <K, V> MemoizedFunctionToNotNull<K, V> createMemoizedFunction(@NotNull Function1<? super K, ? extends V> compute) {
074            return storageManager.createMemoizedFunction(compute);
075        }
076    
077        @NotNull
078        @Override
079        public <K, V> MemoizedFunctionToNullable<K, V> createMemoizedFunctionWithNullableValues(@NotNull Function1<? super K, ? extends V> compute) {
080            return storageManager.createMemoizedFunctionWithNullableValues(compute);
081        }
082    
083        @NotNull
084        @Override
085        public <T> NotNullLazyValue<T> createLazyValue(@NotNull Function0<? extends T> computable) {
086            return storageManager.createLazyValue(computable);
087        }
088    
089        @NotNull
090        @Override
091        public <T> NotNullLazyValue<T> createRecursionTolerantLazyValue(
092                @NotNull Function0<? extends T> computable,
093                @NotNull T onRecursiveCall
094        ) {
095            return storageManager.createRecursionTolerantLazyValue(computable, onRecursiveCall);
096        }
097    
098        @NotNull
099        @Override
100        public <T> NotNullLazyValue<T> createLazyValueWithPostCompute(
101                @NotNull Function0<? extends T> computable,
102                @Nullable Function1<? super Boolean, ? extends T> onRecursiveCall,
103                @NotNull Function1<? super T, ? extends Unit> postCompute
104        ) {
105            return storageManager.createLazyValueWithPostCompute(computable, onRecursiveCall, postCompute);
106        }
107    
108        @NotNull
109        @Override
110        public <T> NullableLazyValue<T> createNullableLazyValue(@NotNull Function0<? extends T> computable) {
111            return storageManager.createNullableLazyValue(computable);
112        }
113    
114        @NotNull
115        @Override
116        public <T> NullableLazyValue<T> createRecursionTolerantNullableLazyValue(
117                @NotNull Function0<? extends T> computable,
118                T onRecursiveCall
119        ) {
120            return storageManager.createRecursionTolerantNullableLazyValue(computable, onRecursiveCall);
121        }
122    
123        @NotNull
124        @Override
125        public <T> NullableLazyValue<T> createNullableLazyValueWithPostCompute(
126                @NotNull Function0<? extends T> computable,
127                @NotNull Function1<? super T, ? extends Unit> postCompute
128        ) {
129            return storageManager.createNullableLazyValueWithPostCompute(computable, postCompute);
130        }
131    
132        @Override
133        public <T> T compute(@NotNull Function0<? extends T> computable) {
134            return storageManager.compute(computable);
135        }
136    
137        private static class LockProtectedContext implements BindingContext {
138            private final Lock lock;
139            private final BindingContext context;
140    
141            private LockProtectedContext(Lock lock, BindingContext context) {
142                this.lock = lock;
143                this.context = context;
144            }
145    
146            @NotNull
147            @Override
148            public Diagnostics getDiagnostics() {
149                lock.lock();
150                try {
151                    return context.getDiagnostics();
152                }
153                finally {
154                    lock.unlock();
155                }
156            }
157    
158            @Nullable
159            @Override
160            public <K, V> V get(ReadOnlySlice<K, V> slice, K key) {
161                lock.lock();
162                try {
163                    return context.get(slice, key);
164                }
165                finally {
166                    lock.unlock();
167                }
168            }
169    
170            @NotNull
171            @Override
172            public <K, V> Collection<K> getKeys(WritableSlice<K, V> slice) {
173                lock.lock();
174                try {
175                    return context.getKeys(slice);
176                }
177                finally {
178                    lock.unlock();
179                }
180            }
181    
182            @NotNull
183            @Override
184            @TestOnly
185            public <K, V> ImmutableMap<K, V> getSliceContents(@NotNull ReadOnlySlice<K, V> slice) {
186                lock.lock();
187                try {
188                    return context.getSliceContents(slice);
189                }
190                finally {
191                    lock.unlock();
192                }
193            }
194        }
195    
196        private static class LockProtectedTrace implements BindingTrace {
197            private final Lock lock;
198            private final BindingTrace trace;
199            private final BindingContext context;
200    
201            public LockProtectedTrace(@NotNull Lock lock, @NotNull BindingTrace trace) {
202                this.lock = lock;
203                this.trace = trace;
204                this.context = new LockProtectedContext(lock, trace.getBindingContext());
205            }
206    
207            @NotNull
208            @Override
209            public BindingContext getBindingContext() {
210                return context;
211            }
212    
213            @Override
214            public <K, V> void record(WritableSlice<K, V> slice, K key, V value) {
215                lock.lock();
216                try {
217                    trace.record(slice, key, value);
218                }
219                finally {
220                    lock.unlock();
221                }
222            }
223    
224            @Override
225            public <K> void record(WritableSlice<K, Boolean> slice, K key) {
226                lock.lock();
227                try {
228                    trace.record(slice, key);
229                }
230                finally {
231                    lock.unlock();
232                }
233            }
234    
235            @Override
236            @Nullable
237            public <K, V> V get(ReadOnlySlice<K, V> slice, K key) {
238                lock.lock();
239                try {
240                    return trace.get(slice, key);
241                }
242                finally {
243                    lock.unlock();
244                }
245            }
246    
247            @Override
248            @NotNull
249            public <K, V> Collection<K> getKeys(WritableSlice<K, V> slice) {
250                lock.lock();
251                try {
252                    return trace.getKeys(slice);
253                }
254                finally {
255                    lock.unlock();
256                }
257            }
258    
259            @Override
260            public void report(@NotNull Diagnostic diagnostic) {
261                lock.lock();
262                try {
263                    trace.report(diagnostic);
264                }
265                finally {
266                    lock.unlock();
267                }
268            }
269        }
270    }