Skip to content

Commit

Permalink
[WIP] SecondaryTypes cache refactoring
Browse files Browse the repository at this point in the history
Fixes #1235
  • Loading branch information
iloveeclipse committed Jul 26, 2023
1 parent 38dd2a8 commit 5aff7bd
Showing 1 changed file with 154 additions and 100 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1244,6 +1244,25 @@ public static IJavaElement determineIfOnClasspath(IResource resource, IJavaProje
*/
protected WeakHashMap<AbstractSearchScope, ?> searchScopes = new WeakHashMap<>();

/**
* Current secondary types cache and temporary indexing cache
*
* @param secondaryTypes
* @param indexingSecondaryCache
* the temporary structure used while indexing, previously known as INDEXED_SECONDARY_TYPES entry
*/
private record SecondaryTypesCache(Hashtable<String, Map<String, IType>> secondaryTypes,
Map<IFile, Map<String, Map<String, IType>>> indexingSecondaryCache) {

boolean indexingDone() {
return hasSecondaryTypes() && this.indexingSecondaryCache == null;
}

boolean hasSecondaryTypes() {
return this.secondaryTypes != null;
}
}

public static class PerProjectInfo {
private static final int JAVADOC_CACHE_INITIAL_SIZE = 10;

Expand All @@ -1266,11 +1285,9 @@ public static class PerProjectInfo {

public IEclipsePreferences preferences;
public Hashtable<String, String> options;
public Hashtable<String, Map<String, IType>> secondaryTypes;
/**
* The temporary structure used while indexing, previously known as INDEXED_SECONDARY_TYPES entry
*/
volatile Map<IFile, Map<String, Map<String, IType>>> indexingSecondaryCache;

private volatile SecondaryTypesCache secondaryTypesCache;
private final Object secondaryTypeslock = new Object();


// NB: PackageFragment#getAttachedJavadoc uses this map differently
Expand All @@ -1283,6 +1300,54 @@ public PerProjectInfo(IProject project) {
this.savedState = null;
this.project = project;
this.javadocCache = new LRUCache<>(JAVADOC_CACHE_INITIAL_SIZE);
this.secondaryTypesCache = new SecondaryTypesCache(null, null);
}

private SecondaryTypesCache getSecondaryTypesCache() {
return this.secondaryTypesCache;
}

private void clearAllSecondaryTypesCaches() {
synchronized (this.secondaryTypeslock) {
this.secondaryTypesCache = new SecondaryTypesCache(null, null);
}
}

private void indexingDone() {
synchronized (this.secondaryTypeslock) {
this.secondaryTypesCache = new SecondaryTypesCache(this.secondaryTypesCache.secondaryTypes(), null);
}
}

private SecondaryTypesCache startIndexing() {
synchronized (this.secondaryTypeslock) {
SecondaryTypesCache cache = new SecondaryTypesCache(this.secondaryTypesCache.secondaryTypes(), Collections.synchronizedMap(new HashMap<>(3)));
this.secondaryTypesCache = cache;
return cache;
}
}

private void doneSearching(Hashtable<String, Map<String, IType>> newSecondaryTypes) {
synchronized (this.secondaryTypeslock) {
this.secondaryTypesCache = new SecondaryTypesCache(newSecondaryTypes, this.secondaryTypesCache.indexingSecondaryCache());
}
}

private SecondaryTypesCache createSecondaryTypesCache() {
synchronized (this.secondaryTypeslock) {
SecondaryTypesCache cache = new SecondaryTypesCache(new Hashtable<>(3), Collections.synchronizedMap(new HashMap<>(3)));
this.secondaryTypesCache = cache;
return cache;
}
}

private SecondaryTypesCache getOrCreateSecondaryTypesCache() {
synchronized (this.secondaryTypeslock) {
if (getSecondaryTypesCache().hasSecondaryTypes()) {
return startIndexing();
}
return createSecondaryTypesCache();
}
}

public synchronized IClasspathEntry[] getResolvedClasspath() {
Expand Down Expand Up @@ -4758,17 +4823,8 @@ public void secondaryTypeAdding(String path, char[] typeName, char[] packageName
PerProjectInfo projectInfo = getPerProjectInfoCheckExistence(project);
// Get or create map to cache secondary types while indexing (can be not synchronized as indexing insure a non-concurrent usage)
Map<IFile, Map<String, Map<String, IType>>> indexedSecondaryTypes;
if (projectInfo.secondaryTypes == null) {
projectInfo.secondaryTypes = new Hashtable<>(3);
indexedSecondaryTypes = Collections.synchronizedMap(new HashMap<>(3));
projectInfo.indexingSecondaryCache = indexedSecondaryTypes;
} else {
indexedSecondaryTypes = projectInfo.indexingSecondaryCache;
if (indexedSecondaryTypes == null) {
indexedSecondaryTypes = Collections.synchronizedMap(new HashMap<>(3));
projectInfo.indexingSecondaryCache = indexedSecondaryTypes;
}
}
SecondaryTypesCache secondaryTypesCache = projectInfo.getOrCreateSecondaryTypesCache();
indexedSecondaryTypes = secondaryTypesCache.indexingSecondaryCache();
// Store the secondary type in temporary cache (these are just handles => no problem to create it now...)
Map<String, Map<String, IType>> allTypes = indexedSecondaryTypes.get(resource);
if (allTypes == null) {
Expand All @@ -4791,13 +4847,7 @@ public void secondaryTypeAdding(String path, char[] typeName, char[] packageName
packageTypes.put(typeString, type);
}
if (VERBOSE) {
Util.verbose(" - indexing cache:"); //$NON-NLS-1$
Iterator<Entry<IFile, Map<String, Map<String, IType>>>> entries = indexedSecondaryTypes.entrySet().iterator();
while (entries.hasNext()) {
Entry<IFile, Map<String, Map<String, IType>>> entry = entries.next();
IFile file = entry.getKey();
Util.verbose(" + "+file.getFullPath()+':'+ entry.getValue()); //$NON-NLS-1$
}
dumpIndexingSecondaryTypes(indexedSecondaryTypes);
}
}
catch (JavaModelException jme) {
Expand All @@ -4807,6 +4857,29 @@ public void secondaryTypeAdding(String path, char[] typeName, char[] packageName
}
}

private void dumpIndexingSecondaryTypes(Map<IFile, Map<String, Map<String, IType>>> indexedSecondaryTypes) {
Util.verbose(" - indexing cache:"); //$NON-NLS-1$
synchronized(indexedSecondaryTypes) {
Iterator<Entry<IFile, Map<String, Map<String, IType>>>> entries = indexedSecondaryTypes.entrySet().iterator();
while (entries.hasNext()) {
Entry<IFile, Map<String, Map<String, IType>>> entry = entries.next();
IFile file = entry.getKey();
Util.verbose(" + "+file.getFullPath()+':'+ entry.getValue()); //$NON-NLS-1$
}
}
}

private static void dumpSecondaryTypes(Map<String, Map<String, IType>> secondaryTypes) {
synchronized (secondaryTypes) {
Iterator<Entry<String, Map<String, IType>>> entries = secondaryTypes.entrySet().iterator();
while (entries.hasNext()) {
Entry<String, Map<String, IType>> entry = entries.next();
String packName = entry.getKey();
Util.verbose(" + " + packName + ':' + entry.getValue()); //$NON-NLS-1$
}
}
}

/**
* Get all secondary types for a project and store result in per project info cache.
* <p>
Expand All @@ -4831,22 +4904,20 @@ public void secondaryTypeAdding(String path, char[] typeName, char[] packageName
public Map<String, Map<String, IType>> secondaryTypes(IJavaProject project, boolean waitForIndexes, IProgressMonitor monitor) throws JavaModelException {
if (VERBOSE) {
StringBuilder buffer = new StringBuilder("JavaModelManager.secondaryTypes("); //$NON-NLS-1$
buffer.append(project.getElementName());
buffer.append(',');
buffer.append(waitForIndexes);
buffer.append(')');
buffer.append(project.getElementName()).append(',').append(waitForIndexes).append(')');
Util.verbose(buffer.toString());
}

// Return cache if not empty and there's no new secondary types created during indexing
final PerProjectInfo projectInfo = getPerProjectInfoCheckExistence(project.getProject());
Map<IFile, Map<String, Map<String, IType>>> indexingSecondaryCache = projectInfo.secondaryTypes == null ? null : projectInfo.indexingSecondaryCache;
if (projectInfo.secondaryTypes != null && indexingSecondaryCache == null) {
return projectInfo.secondaryTypes;
SecondaryTypesCache secondaryTypesCache = projectInfo.getSecondaryTypesCache();
Hashtable<String, Map<String, IType>> secondaryTypes = secondaryTypesCache.secondaryTypes();
if (secondaryTypesCache.indexingDone()) {
return secondaryTypes;
}

// Perform search request only if secondary types cache is not initialized yet (this will happen only once!)
if (projectInfo.secondaryTypes == null) {
if (secondaryTypes == null) {
return secondaryTypesSearching(project, waitForIndexes, monitor, projectInfo);
}

Expand All @@ -4856,7 +4927,7 @@ public Map<String, Map<String, IType>> secondaryTypes(IJavaProject project, bool
if (indexing) {
if (!waitForIndexes) {
// Indexing is running but caller cannot wait => return current cache
return projectInfo.secondaryTypes;
return secondaryTypes;
}

// Wait for the end of indexing or a cancel
Expand Down Expand Up @@ -4889,7 +4960,7 @@ public String getJobFamily() {

}, IJob.WaitUntilReady, monitor);
} catch (OperationCanceledException oce) {
return projectInfo.secondaryTypes;
return secondaryTypes;
}
}

Expand All @@ -4901,24 +4972,20 @@ public String getJobFamily() {
* Return secondary types cache merged with new secondary types created while indexing
* Note that merge result is directly stored in given parameter map.
*/
private Map<String, Map<String, IType>> secondaryTypesMerging(PerProjectInfo projectInfo) {
Map<String, Map<String, IType>> secondaryTypes = projectInfo.secondaryTypes;
private static Map<String, Map<String, IType>> secondaryTypesMerging(PerProjectInfo projectInfo) {
synchronized(projectInfo.secondaryTypeslock) {
// Return current cache if there's no indexing cache (double check, this should not happen)
SecondaryTypesCache secondaryTypesCache = projectInfo.getSecondaryTypesCache();
Map<String, Map<String, IType>> secondaryTypes = secondaryTypesCache.secondaryTypes();
if (secondaryTypesCache.indexingDone()) {
return secondaryTypes;
}
Map<IFile, Map<String, Map<String, IType>>> indexedSecondaryTypes = secondaryTypesCache.indexingSecondaryCache();
// XXX projectInfo.indexingSecondaryCache = null;
if (VERBOSE) {
Util.verbose("JavaModelManager.getSecondaryTypesMerged()"); //$NON-NLS-1$
Util.verbose(" - current cache to merge:"); //$NON-NLS-1$
Iterator<Entry<String, Map<String, IType>>> entries = secondaryTypes.entrySet().iterator();
while (entries.hasNext()) {
Entry<String, Map<String, IType>> entry = entries.next();
String packName = entry.getKey();
Util.verbose(" + "+packName+':'+ entry.getValue() ); //$NON-NLS-1$
}
}

// Return current cache if there's no indexing cache (double check, this should not happen)
Map<IFile, Map<String, Map<String, IType>>> indexedSecondaryTypes = projectInfo.indexingSecondaryCache;
projectInfo.indexingSecondaryCache = null;
if (indexedSecondaryTypes == null) {
return secondaryTypes;
dumpSecondaryTypes(secondaryTypes);
}
Map<IFile, Map<String, Map<String, IType>>> indexedSecondaryTypesCopy;
synchronized (indexedSecondaryTypes) {
Expand Down Expand Up @@ -4956,14 +5023,11 @@ private Map<String, Map<String, IType>> secondaryTypesMerging(PerProjectInfo pro
}
if (VERBOSE) {
Util.verbose(" - secondary types cache merged:"); //$NON-NLS-1$
Iterator<Entry<String, Map<String, IType>>> entries2 = secondaryTypes.entrySet().iterator();
while (entries.hasNext()) {
Entry<String, Map<String, IType>> entry = entries2.next();
String packName = entry.getKey();
Util.verbose(" + "+packName+':'+ entry.getValue()); //$NON-NLS-1$
}
dumpSecondaryTypes(secondaryTypes);
}
projectInfo.indexingDone();
return secondaryTypes;
}
}

/*
Expand Down Expand Up @@ -5028,20 +5092,16 @@ public void acceptType(int modifiers, char[] packageName, char[] simpleTypeName,
}

// Store result in per project info cache if still null or there's still an indexing cache (may have been set by another thread...)
if (projectInfo.secondaryTypes == null || projectInfo.indexingSecondaryCache != null) {
projectInfo.secondaryTypes = secondaryTypes;
SecondaryTypesCache secondaryTypesCache = projectInfo.getSecondaryTypesCache();
if (secondaryTypesCache.secondaryTypes() == null || secondaryTypesCache.indexingSecondaryCache() != null) {
projectInfo.doneSearching(secondaryTypes);

if (VERBOSE || BasicSearchEngine.VERBOSE) {
System.out.print(Thread.currentThread() + " -> secondary paths stored in cache: "); //$NON-NLS-1$
System.out.println();
Iterator<Entry<String, Map<String, IType>>> entries = secondaryTypes.entrySet().iterator();
while (entries.hasNext()) {
Entry<String, Map<String, IType>> entry = entries.next();
String qualifiedName = entry.getKey();
Util.verbose(" - "+qualifiedName+'-'+ entry.getValue()); //$NON-NLS-1$
}
Util.verbose(" -> secondary paths stored in cache: "); //$NON-NLS-1$
dumpSecondaryTypes(secondaryTypes);
}
}
return projectInfo.secondaryTypes;
return projectInfo.getSecondaryTypesCache().secondaryTypes();
}

/**
Expand All @@ -5061,27 +5121,33 @@ public void secondaryTypesRemoving(IFile file, boolean cleanIndexCache) {
}
if (file != null) {
PerProjectInfo projectInfo = getPerProjectInfo(file.getProject(), false);
if (projectInfo != null && projectInfo.secondaryTypes != null) {
if (VERBOSE) {
Util.verbose("-> remove file from cache of project: "+file.getProject().getName()); //$NON-NLS-1$
}
if (projectInfo == null) {
return;
}
SecondaryTypesCache secondaryTypesCache = projectInfo.getSecondaryTypesCache();
Hashtable<String, Map<String, IType>> secondaryTypes = secondaryTypesCache.secondaryTypes();
if (secondaryTypes == null) {
return;
}
if (VERBOSE) {
Util.verbose("-> remove file from cache of project: "+file.getProject().getName()); //$NON-NLS-1$
}

// Clean current cache
secondaryTypesRemoving(projectInfo.secondaryTypes, file);
// Clean current cache
secondaryTypesRemoving(secondaryTypes, file);

// Clean indexing cache if necessary
Map<IFile, Map<String, Map<String, IType>>> indexingCache = projectInfo.indexingSecondaryCache;
if (!cleanIndexCache) {
if (indexingCache == null) {
// Need to signify that secondary types indexing will happen before any request happens
// see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=152841
projectInfo.indexingSecondaryCache = Collections.synchronizedMap(new HashMap<>());
}
return;
}
if (indexingCache != null) {
indexingCache.remove(file);
// Clean indexing cache if necessary
Map<IFile, Map<String, Map<String, IType>>> indexingCache = secondaryTypesCache.indexingSecondaryCache();
if (!cleanIndexCache) {
if (indexingCache == null) {
// Need to signify that secondary types indexing will happen before any request happens
// see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=152841
projectInfo.startIndexing();
}
return;
}
if (indexingCache != null) {
indexingCache.remove(file);
}
}
}
Expand All @@ -5090,19 +5156,12 @@ public void secondaryTypesRemoving(IFile file, boolean cleanIndexCache) {
* Remove from a given cache map all secondary types belonging to a given file.
* Note that there can have several secondary types per file...
*/
private void secondaryTypesRemoving(Map<String, Map<String, IType>> secondaryTypesMap, IFile file) {
private static void secondaryTypesRemoving(Map<String, Map<String, IType>> secondaryTypesMap, IFile file) {
if (VERBOSE) {
StringBuilder buffer = new StringBuilder("JavaModelManager.removeSecondaryTypesFromMap("); //$NON-NLS-1$
Iterator<Entry<String, Map<String, IType>>> entries = secondaryTypesMap.entrySet().iterator();
while (entries.hasNext()) {
Entry<String, Map<String, IType>> entry = entries.next();
String qualifiedName = entry.getKey();
buffer.append(qualifiedName+':'+ entry.getValue());
}
buffer.append(',');
buffer.append(file.getFullPath());
buffer.append(')');
buffer.append(',').append(file.getFullPath()).append(')');
Util.verbose(buffer.toString());
dumpSecondaryTypes(secondaryTypesMap);
}
Set<Entry<String, Map<String, IType>>> packageEntries = secondaryTypesMap.entrySet();
int packagesSize = packageEntries.size(), removedPackagesCount = 0;
Expand Down Expand Up @@ -5144,12 +5203,7 @@ private void secondaryTypesRemoving(Map<String, Map<String, IType>> secondaryTyp
}
if (VERBOSE) {
Util.verbose(" - new secondary types map:"); //$NON-NLS-1$
Iterator<Entry<String, Map<String, IType>>> entries = secondaryTypesMap.entrySet().iterator();
while (entries.hasNext()) {
Entry<String, Map<String, IType>> entry = entries.next();
String qualifiedName = entry.getKey();
Util.verbose(" + "+qualifiedName+':'+ entry.getValue()); //$NON-NLS-1$
}
dumpSecondaryTypes(secondaryTypesMap);
}
}

Expand Down Expand Up @@ -5586,7 +5640,7 @@ public void contentTypeChanged(ContentTypeChangeEvent event) {
IJavaProject project = projects[i];
final PerProjectInfo projectInfo = getPerProjectInfo(project.getProject(), false /* don't create info */);
if (projectInfo != null) {
projectInfo.secondaryTypes = null;
projectInfo.clearAllSecondaryTypesCaches();
}
}
}
Expand Down

0 comments on commit 5aff7bd

Please sign in to comment.