CompositeElement.getPsi() and LPE' .createAllPsiForChildren were under different locks which lead to data race

This commit is contained in:
Alexey Kudravtsev
2011-06-20 12:02:54 +04:00
parent d55df65f57
commit 36d8ee303e
2 changed files with 95 additions and 64 deletions
@@ -301,7 +301,7 @@ public class CompositeElement extends TreeElement {
return false;
}
protected int textMatches(CharSequence buffer, int start) {
protected int textMatches(@NotNull CharSequence buffer, int start) {
int curOffset = start;
for (TreeElement child = getFirstChildNode(); child != null; child = child.getTreeNext()) {
curOffset = child.textMatches(buffer, curOffset);
@@ -310,6 +310,36 @@ public class CompositeElement extends TreeElement {
return curOffset;
}
/*
protected int textMatches(final CharSequence buffer, final int start) {
final int[] curOffset = {start};
acceptTree(new RecursiveTreeElementWalkingVisitor() {
@Override
public void visitLeaf(LeafElement leaf) {
matchText(leaf);
}
private void matchText(TreeElement leaf) {
curOffset[0] = leaf.textMatches(buffer, curOffset[0]);
if (curOffset[0] == -1) {
stopWalking();
}
}
@Override
public void visitComposite(CompositeElement composite) {
if (composite instanceof LazyParseableElement && !((LazyParseableElement)composite).isParsed()) {
matchText(composite);
}
else {
super.visitComposite(composite);
}
}
});
return curOffset[0];
}
*/
@Nullable
public final PsiElement findChildByRoleAsPsiElement(int role) {
ASTNode element = findChildByRole(role);
@@ -635,7 +665,7 @@ public class CompositeElement extends TreeElement {
public void replaceAllChildrenToChildrenOf(final ASTNode anotherParent) {
TreeUtil.ensureParsed(getFirstChildNode());
TreeUtil.ensureParsed(anotherParent.getFirstChildNode());
final ASTNode firstChild1 = anotherParent.getFirstChildNode();
final ASTNode firstChild = anotherParent.getFirstChildNode();
ChangeUtil.prepareAndRunChangeAction(new ChangeUtil.ChangeAction(){
public void makeChange(TreeChangeEvent destinationTreeChange) {
destinationTreeChange.addElementaryChange(anotherParent, ChangeInfoImpl.create(ChangeInfo.CONTENTS_CHANGED, anotherParent));
@@ -643,7 +673,7 @@ public class CompositeElement extends TreeElement {
}
}, (TreeElement)anotherParent);
if (firstChild1 != null) {
if (firstChild != null) {
ChangeUtil.prepareAndRunChangeAction(new ChangeUtil.ChangeAction(){
public void makeChange(TreeChangeEvent destinationTreeChange) {
if(getTreeParent() != null){
@@ -651,12 +681,12 @@ public class CompositeElement extends TreeElement {
changeInfo.setOldLength(getTextLength());
destinationTreeChange.addElementaryChange(CompositeElement.this, changeInfo);
rawRemoveAllChildren();
rawAddChildren((TreeElement)firstChild1);
rawAddChildren((TreeElement)firstChild);
}
else{
final TreeElement first = getFirstChildNode();
remove(destinationTreeChange, first, null);
add(destinationTreeChange, CompositeElement.this, (TreeElement)firstChild1);
add(destinationTreeChange, CompositeElement.this, (TreeElement)firstChild);
repairRemovedElement(CompositeElement.this, first);
}
}
@@ -702,7 +732,7 @@ public class CompositeElement extends TreeElement {
return LeafElement.getPsi(clazz, getPsi(), LOG);
}
final PsiElement createAndStorePsi() {
private PsiElement createAndStorePsi() {
PsiElement psi = createPsiNoLock();
myWrapper = psi;
return psi;
@@ -741,7 +771,7 @@ public class CompositeElement extends TreeElement {
last.rawInsertAfterMe(first);
}
if (DebugUtil.CHECK) DebugUtil.checkTreeStructure(this);
DebugUtil.checkTreeStructure(this);
}
public void rawRemoveAllChildren() {
@@ -751,6 +781,23 @@ public class CompositeElement extends TreeElement {
}
}
void createAllChildrenPsi() {
synchronized (PsiLock.LOCK) { // guard for race condition with getPsi()
acceptTree(CREATE_CHILDREN_PSI);
}
}
private static final RecursiveTreeElementWalkingVisitor CREATE_CHILDREN_PSI = new RecursiveTreeElementWalkingVisitor(false) {
@Override
public void visitLeaf(LeafElement leaf) {
}
@Override
public void visitComposite(CompositeElement composite) {
composite.createAndStorePsi();
super.visitComposite(composite);
}
};
private static void repairRemovedElement(final CompositeElement oldParent, final TreeElement oldChild) {
if(oldChild == null) return;
final FileElement treeElement = DummyHolderFactory.createHolder(oldParent.getManager(), null, false).getTreeElement();
@@ -797,7 +844,7 @@ public class CompositeElement extends TreeElement {
final TreeElement oldChild,
final TreeElement newChild) {
oldChild.rawReplaceWithList(newChild);
final ReplaceChangeInfoImpl change = (ReplaceChangeInfoImpl)ChangeInfoImpl.create(ChangeInfo.REPLACE, newChild);
final ReplaceChangeInfoImpl change = new ReplaceChangeInfoImpl(newChild);
sourceTreeChange.addElementaryChange(newChild, change);
change.setReplaced(oldChild);
}
@@ -27,34 +27,23 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.ILazyParseableElementType;
import com.intellij.util.text.CharArrayUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
public class LazyParseableElement extends CompositeElement {
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.tree.LazyParseableElement");
private static final RecursiveTreeElementWalkingVisitor CREATE_PSI = new RecursiveTreeElementWalkingVisitor(false) {
@Override
public void visitLeaf(LeafElement leaf) {
}
@Override
public void visitComposite(CompositeElement composite) {
composite.createAndStorePsi();
super.visitComposite(composite);
}
};
private static class ChameleonLock {
private ChameleonLock() {}
@NonNls
@Override
public String toString() {
return "chameleon parsing lock";
}
}
private final Object lock = new ChameleonLock();
private final ChameleonLock lock = new ChameleonLock();
private CharSequence myText;
public LazyParseableElement(@NotNull IElementType type, CharSequence text) {
@@ -80,74 +69,68 @@ public class LazyParseableElement extends CompositeElement {
@NotNull
@Override
public String getText() {
synchronized (lock) {
if (myText != null) {
return myText.toString();
}
CharSequence text = myText();
if (text != null) {
return text.toString();
}
return super.getText();
}
@NotNull
public CharSequence getChars() {
synchronized (lock) {
if (myText != null) {
return myText;
}
CharSequence text = myText();
if (text != null) {
return text;
}
return getText();
return super.getText();
}
@Override
public int getTextLength() {
synchronized (lock) {
if (myText != null) {
return myText.length();
}
CharSequence text = myText();
if (text != null) {
return text.length();
}
return super.getTextLength();
}
@Override
public int getNotCachedLength() {
synchronized (lock) {
if (myText != null) {
return myText.length();
}
CharSequence text = myText();
if (text != null) {
return text.length();
}
return super.getNotCachedLength();
}
@Override
public int hc() {
synchronized (lock) {
if (myText != null) {
return LeafElement.leafHC(myText);
}
else {
return super.hc();
}
}
CharSequence text = myText();
return text == null ? super.hc() : LeafElement.leafHC(text);
}
@Override
protected int textMatches(CharSequence buffer, int start) {
synchronized (lock) {
if (myText != null) {
return LeafElement.leafTextMatches(myText, buffer, start);
}
protected int textMatches(@NotNull CharSequence buffer, int start) {
CharSequence text = myText();
if (text != null) {
return LeafElement.leafTextMatches(text, buffer, start);
}
return super.textMatches(buffer, start);
}
public boolean isParsed() {
return myText() == null;
}
private CharSequence myText() {
synchronized (lock) {
return myText == null;
return myText;
}
}
@Override
public void setFirstChildNode(TreeElement child) {
if (myText != null) {
if (myText() != null) {
LOG.error("Mutating collapsed chameleon");
}
super.setFirstChildNode(child);
@@ -155,7 +138,7 @@ public class LazyParseableElement extends CompositeElement {
@Override
public void setLastChildNode(TreeElement child) {
if (myText != null) {
if (myText() != null) {
LOG.error("Mutating collapsed chameleon");
}
super.setLastChildNode(child);
@@ -199,14 +182,16 @@ public class LazyParseableElement extends CompositeElement {
//}
//ensure PSI is created all at once, to reduce contention of PsiLock in CompositeElement.getPsi()
((TreeElement)parsedNode).acceptTree(CREATE_PSI);
if (parsedNode instanceof CompositeElement) {
((CompositeElement)parsedNode).createAllChildrenPsi();
}
parsedNode.getTreeNext();
}
}
@Override
public void rawAddChildren(@NotNull TreeElement first) {
if (myText != null) {
if (myText() != null) {
LOG.error("Mutating collapsed chameleon");
}
super.rawAddChildren(first);
@@ -229,13 +214,12 @@ public class LazyParseableElement extends CompositeElement {
}
public int copyTo(char[] buffer, int start) {
synchronized (lock) {
if (myText == null) return -1;
if (buffer != null) {
CharArrayUtil.getChars(myText, buffer, start);
}
return start + myText.length();
CharSequence text = myText();
if (text == null) return -1;
if (buffer != null) {
CharArrayUtil.getChars(text, buffer, start);
}
return start + text.length();
}
}