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 017package org.jetbrains.jet.lang.resolve.lazy.storage; 018 019import com.intellij.openapi.util.Computable; 020import com.intellij.util.Consumer; 021import com.intellij.util.Function; 022import com.intellij.util.containers.ConcurrentWeakValueHashMap; 023import org.jetbrains.annotations.NotNull; 024import org.jetbrains.annotations.Nullable; 025import org.jetbrains.jet.lang.diagnostics.Diagnostic; 026import org.jetbrains.jet.lang.resolve.BindingContext; 027import org.jetbrains.jet.lang.resolve.BindingTrace; 028import org.jetbrains.jet.util.slicedmap.ReadOnlySlice; 029import org.jetbrains.jet.util.slicedmap.WritableSlice; 030import org.jetbrains.jet.utils.Nulls; 031 032import java.util.Collection; 033import java.util.concurrent.ConcurrentHashMap; 034import java.util.concurrent.ConcurrentMap; 035 036public 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}