From f3bd514f16dcd42b67ae7345647a5047523db32c Mon Sep 17 00:00:00 2001 From: Nikolay Krasko Date: Thu, 25 Jul 2013 14:32:19 +0400 Subject: [PATCH] Thread safe lazy import scope --- .../lang/resolve/lazy/LazyImportScope.java | 210 +++++++++++------- .../lazy/storage/LockBasedStorageManager.java | 7 + .../resolve/lazy/storage/StorageManager.java | 2 + 3 files changed, 141 insertions(+), 78 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/LazyImportScope.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/LazyImportScope.java index b57418678ae..51bebcfbcb2 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/LazyImportScope.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/LazyImportScope.java @@ -17,8 +17,9 @@ package org.jetbrains.jet.lang.resolve.lazy; import com.google.common.collect.Lists; -import com.google.common.collect.Maps; import com.google.common.collect.Sets; +import com.intellij.openapi.util.Computable; +import com.intellij.util.Function; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.*; @@ -26,16 +27,28 @@ import org.jetbrains.jet.lang.psi.JetFile; import org.jetbrains.jet.lang.psi.JetImportDirective; import org.jetbrains.jet.lang.resolve.BindingTrace; import org.jetbrains.jet.lang.resolve.Importer; +import org.jetbrains.jet.lang.resolve.lazy.storage.MemoizedFunctionToNotNull; import org.jetbrains.jet.lang.resolve.name.FqName; import org.jetbrains.jet.lang.resolve.name.LabelName; import org.jetbrains.jet.lang.resolve.name.Name; import org.jetbrains.jet.lang.resolve.scopes.*; -import java.util.*; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Set; import static org.jetbrains.jet.lang.resolve.QualifiedExpressionResolver.LookupMode; +import static org.jetbrains.jet.lang.resolve.lazy.storage.StorageManager.ReferenceKind.STRONG; public class LazyImportScope implements JetScope { + private final ResolveSession resolveSession; + private final NamespaceDescriptor packageDescriptor; + private final ImportsProvider importsProvider; + private final JetScope rootScope; + private final BindingTrace traceForImportResolve; + private final String debugName; + private static class ImportResolveStatus { private final LookupMode lookupMode; private final JetScope scope; @@ -46,14 +59,62 @@ public class LazyImportScope implements JetScope { } } - private final ResolveSession resolveSession; - private final NamespaceDescriptor packageDescriptor; - private final ImportsProvider importsProvider; - private final JetScope rootScope; - private final BindingTrace traceForImportResolve; - private final String debugName; + private class ImportDirectiveResolveCache { + private final JetImportDirective directive; + + @Nullable + private volatile ImportResolveStatus importResolveStatus; + + private ImportDirectiveResolveCache(JetImportDirective directive) { + this.directive = directive; + } + + private JetScope scopeForMode(final LookupMode mode) { + ImportResolveStatus status = importResolveStatus; + if (status != null && (status.lookupMode == mode || status.lookupMode == LookupMode.EVERYTHING)) { + return status.scope; + } + + return resolveSession.getStorageManager().compute(new Computable() { + @Override + public JetScope compute() { + ImportResolveStatus cachedStatus = importResolveStatus; + if (cachedStatus != null && (cachedStatus.lookupMode == mode || cachedStatus.lookupMode == LookupMode.EVERYTHING)) { + return cachedStatus.scope; + } + + WritableScope directiveImportScope = new WritableScopeImpl( + JetScope.EMPTY, packageDescriptor, RedeclarationHandler.DO_NOTHING, + "Scope for import '" + directive.getText() + "' resolve in " + toString()); + directiveImportScope.changeLockLevel(WritableScope.LockLevel.BOTH); + + Importer.StandardImporter importer = new Importer.StandardImporter(directiveImportScope); + directiveUnderResolve = directive; + + try { + resolveSession.getInjector().getQualifiedExpressionResolver().processImportReference( + directive, + rootScope, + packageDescriptor.getMemberScope(), + importer, + traceForImportResolve, + resolveSession.getRootModuleDescriptor(), + mode); + } + finally { + directiveUnderResolve = null; + directiveImportScope.changeLockLevel(WritableScope.LockLevel.READING); + } + + importResolveStatus = new ImportResolveStatus(mode, directiveImportScope); + return directiveImportScope; + } + }); + } + } + + private final MemoizedFunctionToNotNull importedScopesProvider; - private final Map importedScopes = Maps.newHashMap(); private JetImportDirective directiveUnderResolve = null; public LazyImportScope( @@ -69,6 +130,13 @@ public class LazyImportScope implements JetScope { this.traceForImportResolve = traceForImportResolve; this.debugName = debugName; + this.importedScopesProvider = resolveSession.getStorageManager().createMemoizedFunction(new Function() { + @Override + public ImportDirectiveResolveCache fun(JetImportDirective directive) { + return new ImportDirectiveResolveCache(directive); + } + }, STRONG); + NamespaceDescriptor rootPackageDescriptor = resolveSession.getPackageDescriptorByFqName(FqName.ROOT); if (rootPackageDescriptor == null) { throw new IllegalStateException("Root package not found"); @@ -93,94 +161,80 @@ public class LazyImportScope implements JetScope { @Nullable private D selectFirstFromImports( - Name name, - LookupMode lookupMode, - JetScopeSelectorUtil.ScopeByNameSelector descriptorSelector + final Name name, + final LookupMode lookupMode, + final JetScopeSelectorUtil.ScopeByNameSelector descriptorSelector ) { - for (JetImportDirective directive : importsProvider.getImports(name)) { - if (directive == directiveUnderResolve) { - // This is the recursion in imports analysis + return resolveSession.getStorageManager().compute(new Computable() { + @Override + public D compute() { + for (JetImportDirective directive : importsProvider.getImports(name)) { + if (directive == directiveUnderResolve) { + // This is the recursion in imports analysis + return null; + } + + D foundDescriptor = descriptorSelector.get(getImportScope(directive, lookupMode), name); + if (foundDescriptor != null) { + return foundDescriptor; + } + } + return null; } - - D foundDescriptor = descriptorSelector.get(getImportScope(directive, lookupMode), name); - if (foundDescriptor != null) { - return foundDescriptor; - } - } - - return null; + }); } @NotNull private Collection collectFromImports( - Name name, - LookupMode lookupMode, - JetScopeSelectorUtil.ScopeByNameMultiSelector descriptorsSelector + final Name name, + final LookupMode lookupMode, + final JetScopeSelectorUtil.ScopeByNameMultiSelector descriptorsSelector ) { - Set descriptors = Sets.newHashSet(); - for (JetImportDirective directive : importsProvider.getImports(name)) { - if (directive == directiveUnderResolve) { - // This is the recursion in imports analysis - throw new IllegalStateException("Recursion while resolving many imports: " + directive.getText()); + return resolveSession.getStorageManager().compute(new Computable>() { + @Override + public Collection compute() { + Set descriptors = Sets.newHashSet(); + for (JetImportDirective directive : importsProvider.getImports(name)) { + if (directive == directiveUnderResolve) { + // This is the recursion in imports analysis + throw new IllegalStateException("Recursion while resolving many imports: " + directive.getText()); + } + + descriptors.addAll(descriptorsSelector.get(getImportScope(directive, lookupMode), name)); + } + + return descriptors; } - - descriptors.addAll(descriptorsSelector.get(getImportScope(directive, lookupMode), name)); - } - - return descriptors; + }); } @NotNull private Collection collectFromImports( - LookupMode lookupMode, - JetScopeSelectorUtil.ScopeDescriptorSelector descriptorsSelector + final LookupMode lookupMode, + final JetScopeSelectorUtil.ScopeDescriptorSelector descriptorsSelector ) { - Set descriptors = Sets.newHashSet(); - for (JetImportDirective directive : importsProvider.getAllImports()) { - if (directive == directiveUnderResolve) { - // This is the recursion in imports analysis - throw new IllegalStateException("Recursion while resolving many imports: " + directive.getText()); + return resolveSession.getStorageManager().compute(new Computable>() { + @Override + public Collection compute() { + Set descriptors = Sets.newHashSet(); + for (JetImportDirective directive : importsProvider.getAllImports()) { + if (directive == directiveUnderResolve) { + // This is the recursion in imports analysis + throw new IllegalStateException("Recursion while resolving many imports: " + directive.getText()); + } + + descriptors.addAll(descriptorsSelector.get(getImportScope(directive, lookupMode))); + } + + return descriptors; } - - descriptors.addAll(descriptorsSelector.get(getImportScope(directive, lookupMode))); - } - - return descriptors; + }); } @NotNull private JetScope getImportScope(JetImportDirective directive, LookupMode lookupMode) { - ImportResolveStatus status = importedScopes.get(directive); - if (status != null && (lookupMode == status.lookupMode || status.lookupMode == LookupMode.EVERYTHING)) { - return status.scope; - } - - WritableScope directiveImportScope = new WritableScopeImpl( - JetScope.EMPTY, packageDescriptor, RedeclarationHandler.DO_NOTHING, - "Scope for import '" + directive.getText() + "' resolve in " + toString()); - directiveImportScope.changeLockLevel(WritableScope.LockLevel.BOTH); - - Importer.StandardImporter importer = new Importer.StandardImporter(directiveImportScope); - directiveUnderResolve = directive; - - try { - resolveSession.getInjector().getQualifiedExpressionResolver().processImportReference( - directive, - rootScope, - packageDescriptor.getMemberScope(), - importer, - traceForImportResolve, - resolveSession.getRootModuleDescriptor(), - lookupMode); - } - finally { - directiveUnderResolve = null; - directiveImportScope.changeLockLevel(WritableScope.LockLevel.READING); - importedScopes.put(directive, new ImportResolveStatus(lookupMode, directiveImportScope)); - } - - return directiveImportScope; + return importedScopesProvider.fun(directive).scopeForMode(lookupMode); } @Nullable diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/storage/LockBasedStorageManager.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/storage/LockBasedStorageManager.java index 37709d0bb5e..e022979d5cd 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/storage/LockBasedStorageManager.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/storage/LockBasedStorageManager.java @@ -108,6 +108,13 @@ public class LockBasedStorageManager implements StorageManager { return new LockProtectedTrace(lock, originalTrace); } + @Override + public T compute(@NotNull Computable computable) { + synchronized (lock) { + return computable.compute(); + } + } + private static class LockBasedLazyValue implements NullableLazyValue { private final Object lock; private final Computable computable; diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/storage/StorageManager.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/storage/StorageManager.java index 45b1367f85e..e17d0f86727 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/storage/StorageManager.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/storage/StorageManager.java @@ -59,6 +59,8 @@ public interface StorageManager { @NotNull BindingTrace createSafeTrace(@NotNull BindingTrace originalTrace); + T compute(@NotNull Computable computable); + enum ReferenceKind { STRONG, WEAK