1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-23 22:52:11 +02:00

Fix ASTCacheTests

This commit is contained in:
Anton Leherbauer 2007-04-04 13:38:20 +00:00
parent 625b82284d
commit f7b80bf306
2 changed files with 106 additions and 86 deletions

View file

@ -17,6 +17,7 @@ import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.dom.IPDOMManager; import org.eclipse.cdt.core.dom.IPDOMManager;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.index.IIndex; import org.eclipse.cdt.core.index.IIndex;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.model.ICProject; import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.model.ITranslationUnit; import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.core.testplugin.CProjectHelper; import org.eclipse.cdt.core.testplugin.CProjectHelper;
@ -37,26 +38,30 @@ public class ASTCacheTests extends BaseTestCase {
private static int fgReconcilerCount; private static int fgReconcilerCount;
public class MockReconciler extends Thread { public class MockReconciler extends Thread {
private ITranslationUnit fTU; private final ITranslationUnit fTU;
private ASTCache fCache; private final ASTCache fCache;
public volatile boolean fStopped; public volatile boolean fStopped;
private IASTTranslationUnit fAST; public IASTTranslationUnit fAST;
public MockReconciler(ITranslationUnit tu, ASTCache cache) { public MockReconciler(ITranslationUnit tu, ASTCache cache) {
super("MockReconciler-"+fgReconcilerCount++); super("MockReconciler-"+fgReconcilerCount++);
fTU= tu; fTU= tu;
fCache= cache; fCache= cache;
setDaemon(true);
} }
public void run() { public void run() {
while (!fStopped) { while (!fStopped) {
try { try {
Thread.sleep(200);
fCache.aboutToBeReconciled(fTU);
synchronized (this) { synchronized (this) {
notifyAll(); fCache.aboutToBeReconciled(fTU);
fAST= null;
notify();
}
Thread.sleep(100);
synchronized (this) {
fAST= fCache.createAST(fTU, fIndex, null);
fCache.reconciled(fAST, fTU);
} }
fAST= fCache.createAST(fTU, fIndex, null);
fCache.reconciled(fAST, fTU);
} catch (InterruptedException exc) { } catch (InterruptedException exc) {
fStopped= true; fStopped= true;
break; break;
@ -84,17 +89,18 @@ public class ASTCacheTests extends BaseTestCase {
public void setUp() throws Exception { public void setUp() throws Exception {
super.setUp(); super.setUp();
IProgressMonitor npm= new NullProgressMonitor();
fProject= createProject("ASTCacheTest"); fProject= createProject("ASTCacheTest");
assertNotNull(fProject); assertNotNull(fProject);
IFile file1= createFile(fProject.getProject(), "source1.cpp", SOURCE1); IFile file1= createFile(fProject.getProject(), "source1.cpp", SOURCE1);
assertNotNull(file1); assertNotNull(file1);
fTU1= CProjectHelper.findTranslationUnit(fProject, file1.getName());
assertNotNull(fTU1);
IFile file2= createFile(fProject.getProject(), "source2.cpp", SOURCE2); IFile file2= createFile(fProject.getProject(), "source2.cpp", SOURCE2);
assertNotNull(file2); assertNotNull(file2);
fTU2= CProjectHelper.findTranslationUnit(fProject, file2.getName()); fTU1= (ITranslationUnit) CoreModel.getDefault().create(file1);
assertNotNull(fTU1);
fTU2= (ITranslationUnit) CoreModel.getDefault().create(file2);
assertNotNull(fTU2); assertNotNull(fTU2);
CCorePlugin.getIndexManager().joinIndexer(5000, new NullProgressMonitor()); CCorePlugin.getIndexManager().joinIndexer(5000, npm);
fIndex= CCorePlugin.getIndexManager().getIndex(fProject); fIndex= CCorePlugin.getIndexManager().getIndex(fProject);
fIndex.acquireReadLock(); fIndex.acquireReadLock();
} }
@ -153,37 +159,47 @@ public class ASTCacheTests extends BaseTestCase {
private void checkAccessWithSequentialReconciler() throws Exception { private void checkAccessWithSequentialReconciler() throws Exception {
ASTCache cache= new ASTCache(); ASTCache cache= new ASTCache();
cache.setActiveElement(fTU1);
MockReconciler reconciler1= new MockReconciler(fTU1, cache); MockReconciler reconciler1= new MockReconciler(fTU1, cache);
MockReconciler reconciler2= null; MockReconciler reconciler2= new MockReconciler(fTU2, cache);
try { try {
cache.setActiveElement(fTU1);
assertFalse(cache.isReconciling(fTU1)); assertFalse(cache.isReconciling(fTU1));
synchronized (reconciler1) { synchronized (reconciler1) {
reconciler1.start(); reconciler1.start();
reconciler1.wait(); reconciler1.wait();
assertNull(reconciler1.fAST);
assertTrue(cache.isActiveElement(fTU1));
assertTrue(cache.isReconciling(fTU1));
} }
reconciler1.fStopped= true;
IASTTranslationUnit ast; IASTTranslationUnit ast;
ast= cache.getAST(fTU1, fIndex, true, null); ast= cache.getAST(fTU1, fIndex, true, null);
assertNotNull(ast); assertNotNull(ast);
assertTrue(cache.isActiveElement(fTU1));
assertFalse(cache.isReconciling(fTU1));
assertSame(ast, reconciler1.fAST); assertSame(ast, reconciler1.fAST);
// change active element // change active element
cache.setActiveElement(fTU2); cache.setActiveElement(fTU2);
reconciler2= new MockReconciler(fTU2, cache); assertFalse(cache.isReconciling(fTU2));
synchronized (reconciler2) { synchronized (reconciler2) {
reconciler2.start(); reconciler2.start();
reconciler2.wait(); reconciler2.wait();
assertNull(reconciler2.fAST);
assertTrue(cache.isActiveElement(fTU2));
assertTrue(cache.isReconciling(fTU2));
} }
reconciler2.fStopped= true;
ast= cache.getAST(fTU2, fIndex, true, null); ast= cache.getAST(fTU2, fIndex, true, null);
assertNotNull(ast); assertNotNull(ast);
assertTrue(cache.isActiveElement(fTU2));
assertFalse(cache.isReconciling(fTU2));
assertSame(ast, reconciler2.fAST); assertSame(ast, reconciler2.fAST);
} finally { } finally {
reconciler1.fStopped= true; reconciler1.fStopped= true;
reconciler1.join(1000); reconciler1.join(1000);
if (reconciler2 != null) { reconciler2.fStopped= true;
reconciler2.fStopped= true; reconciler2.join(1000);
reconciler2.join(1000);
}
} }
} }
@ -192,37 +208,44 @@ public class ASTCacheTests extends BaseTestCase {
MockReconciler reconciler1= new MockReconciler(fTU1, cache); MockReconciler reconciler1= new MockReconciler(fTU1, cache);
MockReconciler reconciler2= new MockReconciler(fTU2, cache); MockReconciler reconciler2= new MockReconciler(fTU2, cache);
reconciler1.start(); reconciler1.start();
Thread.sleep(100); Thread.sleep(50);
reconciler2.start(); reconciler2.start();
try { try {
for (int i= 0; i < 10; i++) { int cacheHits= 0;
int iterations= 0;
while (cacheHits < 4 && iterations < 10) {
++iterations;
IASTTranslationUnit ast; IASTTranslationUnit ast;
cache.setActiveElement(fTU1); cache.setActiveElement(fTU1);
Thread.sleep(100);
ast= cache.getAST(fTU1, fIndex, false, null); ast= cache.getAST(fTU1, fIndex, false, null);
if (ast != null) { if (ast != null) {
assertSame(ast, reconciler1.fAST); assertSame(ast, reconciler1.fAST);
++cacheHits;
} else {
ast= cache.getAST(fTU1, fIndex, true, null);
assertNotNull(ast);
assertEquals("void foo1() {}", ast.getDeclarations()[0].getRawSignature());
} }
ast= cache.getAST(fTU1, fIndex, true, null);
assertNotNull(ast);
assertEquals("void foo1() {}", ast.getDeclarations()[0].getRawSignature());
// change active element // change active element
cache.setActiveElement(fTU2); cache.setActiveElement(fTU2);
Thread.sleep(100);
ast= cache.getAST(fTU1, fIndex, false, null); ast= cache.getAST(fTU1, fIndex, false, null);
if (ast != null) { if (ast != null) {
assertSame(ast, reconciler2.fAST); assertSame(ast, reconciler2.fAST);
++cacheHits;
} else {
ast= cache.getAST(fTU2, fIndex, true, null);
assertNotNull(ast);
assertEquals("void foo2() {}", ast.getDeclarations()[0].getRawSignature());
} }
ast= cache.getAST(fTU2, fIndex, true, null);
assertNotNull(ast);
assertEquals("void foo2() {}", ast.getDeclarations()[0].getRawSignature());
} }
} finally { } finally {
reconciler1.fStopped= true; reconciler1.fStopped= true;
reconciler1.join(1000); reconciler1.join(1000);
if (reconciler2 != null) { reconciler2.fStopped= true;
reconciler2.fStopped= true; reconciler2.join(1000);
reconciler2.join(1000);
}
} }
} }

View file

@ -13,7 +13,6 @@ import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.dom.ast.IASTNode; import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.index.IIndex; import org.eclipse.cdt.core.index.IIndex;
import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.core.model.ITranslationUnit; import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IProgressMonitor;
@ -70,7 +69,7 @@ public class ASTCache {
*/ */
private long fLastWriteOnIndex; private long fLastWriteOnIndex;
/** Inidicates whether the AST is currenty being computed */ /** Inidicates whether the AST is currenty being computed */
private volatile boolean fIsReconciling; private boolean fIsReconciling;
/** /**
* Create a new AST cache. * Create a new AST cache.
@ -133,7 +132,7 @@ public class ASTCache {
if (DEBUG) if (DEBUG)
System.out.println(DEBUG_PREFIX + getThreadName() + "waiting for AST for: " + tUnit.getElementName()); //$NON-NLS-1$ System.out.println(DEBUG_PREFIX + getThreadName() + "waiting for AST for: " + tUnit.getElementName()); //$NON-NLS-1$
fCacheMutex.wait(); fCacheMutex.wait();
// Check whether active element is still valid ( // Check whether active element is still valid
if (fAST != null) { if (fAST != null) {
if (DEBUG) if (DEBUG)
System.out.println(DEBUG_PREFIX + getThreadName() + "...got AST for: " + tUnit.getElementName()); //$NON-NLS-1$ System.out.println(DEBUG_PREFIX + getThreadName() + "...got AST for: " + tUnit.getElementName()); //$NON-NLS-1$
@ -219,25 +218,24 @@ public class ASTCache {
* @param tUnit the translation unit * @param tUnit the translation unit
*/ */
private void cache(IASTTranslationUnit ast, ITranslationUnit tUnit) { private void cache(IASTTranslationUnit ast, ITranslationUnit tUnit) {
synchronized (fCacheMutex) { assert Thread.holdsLock(fCacheMutex);
if (fActiveTU != null && !fActiveTU.equals(tUnit)) { if (fActiveTU != null && !fActiveTU.equals(tUnit)) {
if (DEBUG && tUnit != null) // don't report call from disposeAST() if (DEBUG && tUnit != null) // don't report call from disposeAST()
System.out.println(DEBUG_PREFIX + getThreadName() + "don't cache AST for inactive: " + toString(tUnit)); //$NON-NLS-1$ System.out.println(DEBUG_PREFIX + getThreadName() + "don't cache AST for inactive: " + toString(tUnit)); //$NON-NLS-1$
return; return;
}
if (DEBUG && (tUnit != null || ast != null)) // don't report call from disposeAST()
System.out.println(DEBUG_PREFIX + getThreadName() + "caching AST: " + toString(ast) + " for: " + toString(tUnit)); //$NON-NLS-1$ //$NON-NLS-2$
if (fAST != null)
disposeAST();
fAST= ast;
fLastWriteOnIndex= fAST == null ? 0 : fAST.getIndex().getLastWriteAccess();
// Signal AST change
fCacheMutex.notifyAll();
} }
if (DEBUG && (tUnit != null || ast != null)) // don't report call from disposeAST()
System.out.println(DEBUG_PREFIX + getThreadName() + "caching AST: " + toString(ast) + " for: " + toString(tUnit)); //$NON-NLS-1$ //$NON-NLS-2$
if (fAST != null)
disposeAST();
fAST= ast;
fLastWriteOnIndex= fAST == null ? 0 : fAST.getIndex().getLastWriteAccess();
// Signal AST change
fCacheMutex.notifyAll();
} }
/** /**
@ -259,10 +257,10 @@ public class ASTCache {
/** /**
* Creates a new translation unit AST. * Creates a new translation unit AST.
* *
* @param tUnit the C element for which to create the AST * @param tUnit the translation unit for which to create the AST
* @param index for AST generation, needs to be read-locked. * @param index the index for AST generation, needs to be read-locked.
* @param progressMonitor the progress monitor * @param progressMonitor a progress monitor, may be <code>null</code>
* @return AST * @return an AST for the translation unit, or <code>null</code> if the operation was cancelled
*/ */
public IASTTranslationUnit createAST(final ITranslationUnit tUnit, final IIndex index, final IProgressMonitor progressMonitor) { public IASTTranslationUnit createAST(final ITranslationUnit tUnit, final IIndex index, final IProgressMonitor progressMonitor) {
if (progressMonitor != null && progressMonitor.isCanceled()) if (progressMonitor != null && progressMonitor.isCanceled())
@ -294,14 +292,14 @@ public class ASTCache {
/** /**
* Set the given translation unit as active element to cache an AST for. * Set the given translation unit as active element to cache an AST for.
* *
* @param tUnit * @param tUnit the translation unit
*/ */
public void setActiveElement(ITranslationUnit tUnit) { public void setActiveElement(ITranslationUnit tUnit) {
if (tUnit == fActiveTU) { if (tUnit == fActiveTU) {
return; return;
} }
fIsReconciling= false;
synchronized (fCacheMutex) { synchronized (fCacheMutex) {
fIsReconciling= false;
fActiveTU= tUnit; fActiveTU= tUnit;
cache(null, tUnit); cache(null, tUnit);
} }
@ -312,7 +310,7 @@ public class ASTCache {
/** /**
* Check whether the given translation unit is the active element of this cache. * Check whether the given translation unit is the active element of this cache.
* *
* @param tUnit * @param tUnit the translation unit
* @return <code>true</code>, if this cache manages the given translation unit * @return <code>true</code>, if this cache manages the given translation unit
*/ */
public boolean isActiveElement(ITranslationUnit tUnit) { public boolean isActiveElement(ITranslationUnit tUnit) {
@ -325,43 +323,42 @@ public class ASTCache {
* Informs that reconciling (computation of the AST) for the given element * Informs that reconciling (computation of the AST) for the given element
* is about to be started. * is about to be started.
* *
* @param tUnit the C element * @param tUnit the translation unit
* @see org.eclipse.cdt.internal.ui.text.ICReconcilingListener#aboutToBeReconciled()
*/ */
public void aboutToBeReconciled(ITranslationUnit tUnit) { public void aboutToBeReconciled(ITranslationUnit tUnit) {
if (tUnit == null) if (tUnit == null)
return; return;
if (fActiveTU == null || !fActiveTU.equals(tUnit)) {
return; synchronized (fCacheMutex) {
if (fActiveTU == null || !fActiveTU.equals(tUnit)) {
return;
}
if (DEBUG)
System.out.println(DEBUG_PREFIX + getThreadName() + "about to reconcile: " + toString(tUnit)); //$NON-NLS-1$
fIsReconciling= true;
cache(null, tUnit);
} }
if (DEBUG)
System.out.println(DEBUG_PREFIX + getThreadName() + "about to reconcile: " + toString(tUnit)); //$NON-NLS-1$
fIsReconciling= true;
cache(null, tUnit);
} }
/** /**
* Informs that reconciling of the AST of the given translation unit has finished. * Informs that reconciling of the AST of the given translation unit has finished.
* *
* @param ast * @param ast the translation unit AST
* @param tUnit * @param tUnit the translation unit
*/ */
public void reconciled(IASTTranslationUnit ast, ITranslationUnit tUnit) { public void reconciled(IASTTranslationUnit ast, ITranslationUnit tUnit) {
synchronized (fCacheMutex) { synchronized (fCacheMutex) {
if (tUnit == null || !tUnit.equals(fActiveTU)) {
if (DEBUG)
System.out.println(DEBUG_PREFIX + getThreadName() + "ignoring AST of out-dated element"); //$NON-NLS-1$
return;
}
if (DEBUG) if (DEBUG)
System.out.println(DEBUG_PREFIX + getThreadName() + "reconciled: " + toString(tUnit) + ", AST: " + toString(ast)); //$NON-NLS-1$ //$NON-NLS-2$ System.out.println(DEBUG_PREFIX + getThreadName() + "reconciled: " + toString(tUnit) + ", AST: " + toString(ast)); //$NON-NLS-1$ //$NON-NLS-2$
fIsReconciling= false; fIsReconciling= false;
if (tUnit == null || !tUnit.equals(fActiveTU)) {
if (DEBUG)
System.out.println(DEBUG_PREFIX + getThreadName() + " ignoring AST of out-dated element"); //$NON-NLS-1$
// Signal - threads might wait for wrong element
fCacheMutex.notifyAll();
return;
}
cache(ast, tUnit); cache(ast, tUnit);
} }
} }
@ -370,7 +367,7 @@ public class ASTCache {
* Tells whether the given C element is the one * Tells whether the given C element is the one
* reported as currently being reconciled. * reported as currently being reconciled.
* *
* @param tUnit the C element * @param tUnit the translation unit
* @return <code>true</code> if reported as currently being reconciled * @return <code>true</code> if reported as currently being reconciled
*/ */
public boolean isReconciling(ITranslationUnit tUnit) { public boolean isReconciling(ITranslationUnit tUnit) {
@ -393,21 +390,21 @@ public class ASTCache {
/** /**
* Returns a string for the given C element used for debugging. * Returns a string for the given C element used for debugging.
* *
* @param cElement the translation unit AST * @param tUnit the translation unit
* @return a string used for debugging * @return a string used for debugging
*/ */
private static String toString(ICElement cElement) { private static String toString(ITranslationUnit tUnit) {
if (cElement == null) if (tUnit == null)
return "null"; //$NON-NLS-1$ return "null"; //$NON-NLS-1$
else else
return cElement.getElementName(); return tUnit.getElementName();
} }
/** /**
* Returns a string for the given AST used for debugging. * Returns a string for the given AST used for debugging.
* *
* @param ast the translation unit AST * @param ast the translation unit AST
* @return a string used for debugging * @return a string used for debugging
*/ */
private static String toString(IASTTranslationUnit ast) { private static String toString(IASTTranslationUnit ast) {