mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
data races
This commit is contained in:
@@ -21,6 +21,7 @@ import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.SmartList;
|
||||
import com.intellij.util.WalkingState;
|
||||
import com.intellij.util.concurrency.AtomicFieldUpdater;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import gnu.trove.TLongHashSet;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
@@ -54,9 +55,7 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
|
||||
protected int maxEnd; // max of all intervalEnd()s among all children.
|
||||
protected int delta; // delta of startOffset. getStartOffset() = myStartOffset + Sum of deltas up to root
|
||||
|
||||
private volatile int modCount; // if it equals to the com.intellij.openapi.editor.impl.RedBlackTree.modCount then deltaUpToRoot can be used, otherwise it is expired
|
||||
private volatile int deltaUpToRoot; // sum of all deltas up to the root (including this node' delta). Has valid value only if modCount == IntervalTreeImpl.this.modCount
|
||||
private volatile boolean allDeltasUpAreNull; // true if all deltas up the tree (including this node) are 0. Has valid value only if modCount == IntervalTreeImpl.this.modCount
|
||||
private volatile long cachedDeltaUpToRoot; // field (packed to long for atomicity) containing deltaUpToRoot, node modCount and allDeltasUpAreNull flag
|
||||
private final IntervalTreeImpl<E> myIntervalTree;
|
||||
|
||||
public IntervalNode(IntervalTreeImpl<E> intervalTree, @NotNull E key, int start, int end) {
|
||||
@@ -117,7 +116,7 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
|
||||
|
||||
// removes interval and the node, if node became empty
|
||||
// returns true if node was removed
|
||||
public boolean removeInterval(@NotNull E key) {
|
||||
private boolean removeInterval(@NotNull E key) {
|
||||
myIntervalTree.checkBelongsToTheTree(key, true);
|
||||
myIntervalTree.assertUnderWriteLock();
|
||||
for (int i = intervals.size() - 1; i >= 0; i--) {
|
||||
@@ -154,79 +153,81 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
|
||||
}
|
||||
|
||||
protected int computeDeltaUpToRoot() {
|
||||
return computeDeltaUpToRoot(new NodeCachedOffsets());
|
||||
}
|
||||
protected int computeDeltaUpToRoot(NodeCachedOffsets cached) {
|
||||
restart:
|
||||
while (true) { // have to restart on failure to update cached offsets in case of concurrent modification
|
||||
if (!isValid()) return 0;
|
||||
int treeModCount = myIntervalTree.modCount;
|
||||
if (modCount == treeModCount) {
|
||||
return deltaUpToRoot;
|
||||
unpackCachedValuesTo(cached);
|
||||
if (cached.modCount == treeModCount) {
|
||||
return cached.deltaUpToRoot;
|
||||
}
|
||||
IntervalNode<E> node = this;
|
||||
IntervalNode<E> treeRoot = myIntervalTree.getRoot();
|
||||
if (treeRoot == null) return delta; // someone modified the tree in the meantime
|
||||
int deltaUp = 0;
|
||||
boolean allDeltasAreNull = true;
|
||||
int height = 0;
|
||||
long path = 0; // path to this node from the root; 0 bit means we choose left subtree, 1 bit means we choose right subtree
|
||||
while (node != treeRoot) {
|
||||
if (node.isValid() && node.modCount == treeModCount) {
|
||||
deltaUp = node.deltaUpToRoot - node.delta;
|
||||
allDeltasAreNull = node.allDeltasUpAreNull;
|
||||
break;
|
||||
}
|
||||
IntervalNode<E> parent = node.getParent();
|
||||
if (parent == null) {
|
||||
break; // can happen when remove node and explicitly set valid to true (e.g. in RangeMarkerTree)
|
||||
}
|
||||
path = (path << 1) | (parent.getLeft() == node ? 0 : 1);
|
||||
node = parent;
|
||||
height++;
|
||||
}
|
||||
// path to this node fits to long
|
||||
assert height < 63 : height;
|
||||
try {
|
||||
myIntervalTree.l.readLock().lock();
|
||||
|
||||
// cache deltas in every node from the root down this
|
||||
while (true) {
|
||||
if (node.isValid()) {
|
||||
int nodeDelta = node.delta;
|
||||
deltaUp += nodeDelta;
|
||||
allDeltasAreNull &= nodeDelta == 0;
|
||||
if (!setCachedOffsetsAtomically(deltaUp, allDeltasAreNull, treeModCount)) {
|
||||
continue restart;
|
||||
IntervalNode<E> node = this;
|
||||
IntervalNode<E> treeRoot = myIntervalTree.getRoot();
|
||||
if (treeRoot == null) return delta; // someone modified the tree in the meantime
|
||||
int deltaUp = 0;
|
||||
boolean allDeltasAreNull = true;
|
||||
int height = 0;
|
||||
long path = 0; // path to this node from the root; 0 bit means we choose left subtree, 1 bit means we choose right subtree
|
||||
while (node != treeRoot) {
|
||||
node.unpackCachedValuesTo(cached);
|
||||
if (node.isValid() && cached.modCount == treeModCount) {
|
||||
deltaUp = cached.deltaUpToRoot - node.delta;
|
||||
allDeltasAreNull = cached.allDeltasUpAreNull;
|
||||
break;
|
||||
}
|
||||
IntervalNode<E> parent = node.getParent();
|
||||
if (parent == null) {
|
||||
break; // can happen when remove node and explicitly set valid to true (e.g. in RangeMarkerTree)
|
||||
}
|
||||
path = (path << 1) | (parent.getLeft() == node ? 0 : 1);
|
||||
node = parent;
|
||||
height++;
|
||||
}
|
||||
// path to this node fits to long
|
||||
assert height < 63 : height;
|
||||
|
||||
// cache deltas in every node from the root down this
|
||||
while (true) {
|
||||
if (node.isValid()) {
|
||||
int nodeDelta = node.delta;
|
||||
deltaUp += nodeDelta;
|
||||
allDeltasAreNull &= nodeDelta == 0;
|
||||
if (!node.tryToSetCachedValues(deltaUp, allDeltasAreNull, treeModCount)) {
|
||||
continue restart;
|
||||
}
|
||||
}
|
||||
|
||||
if (node == this) break;
|
||||
node = (path & 1) == 0 ? node.getLeft() : node.getRight();
|
||||
path >>= 1;
|
||||
if (node == null) continue restart; // can only happen in case of concurrently modification
|
||||
}
|
||||
|
||||
if (node == this) break;
|
||||
node = (path & 1) == 0 ? node.getLeft() : node.getRight();
|
||||
path >>= 1;
|
||||
if (node == null) continue restart; // can only happen in case of concurrently modification
|
||||
assert deltaUp == 0 || !allDeltasAreNull;
|
||||
return deltaUp;
|
||||
}
|
||||
finally {
|
||||
myIntervalTree.l.readLock().unlock();
|
||||
}
|
||||
|
||||
assert deltaUp == 0 || !allDeltasAreNull;
|
||||
return deltaUp;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean setCachedOffsetsAtomically(int deltaUpToRoot, boolean allDeltasUpAreNull, int treeModCount) {
|
||||
synchronized (intervals) { // synchronize for mutual exclusion
|
||||
if (myIntervalTree.modCount != treeModCount) return false;
|
||||
this.deltaUpToRoot = deltaUpToRoot;
|
||||
this.allDeltasUpAreNull = allDeltasUpAreNull;
|
||||
modCount = treeModCount; //set modCount last
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
protected int changeDelta(int change) {
|
||||
if (change != 0) {
|
||||
modCount = 0; // deltaUpToRoot is not valid anymore
|
||||
setCachedValues(0, false, 0); // deltaUpToRoot is not valid anymore
|
||||
return delta += change;
|
||||
}
|
||||
return delta;
|
||||
}
|
||||
protected void clearDelta() {
|
||||
if (delta != 0) {
|
||||
modCount = 0; // deltaUpToRoot is not valid anymore
|
||||
setCachedValues(0, false, 0); // deltaUpToRoot is not valid anymore
|
||||
delta = 0;
|
||||
}
|
||||
}
|
||||
@@ -264,6 +265,44 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
|
||||
public IntervalTreeImpl<E> getTree() {
|
||||
return myIntervalTree;
|
||||
}
|
||||
|
||||
/**
|
||||
* packing/unpacking cachedDeltaUpToRoot field parts
|
||||
* Bits layout:
|
||||
* XXXXXXXXNMMMMMMMM where
|
||||
* XXXXXXXX - 31bit int containing cached delta up to root
|
||||
* N - 1bit flag. if set then all deltas up to root are null
|
||||
* MMMMMMMM - 32bit int containing this node modification count
|
||||
*/
|
||||
private static AtomicFieldUpdater<IntervalNode, Long> cachedDeltaUpdater = AtomicFieldUpdater.forLongField(IntervalNode.class);
|
||||
|
||||
private void setCachedValues(int deltaUpToRoot, boolean allDeltaUpToRootAreNull, int modCount) {
|
||||
cachedDeltaUpToRoot = packValues(deltaUpToRoot, allDeltaUpToRootAreNull, modCount);
|
||||
}
|
||||
|
||||
private static long packValues(long deltaUpToRoot, boolean allDeltaUpToRootAreNull, int modCount) {
|
||||
return deltaUpToRoot << 33 | (allDeltaUpToRootAreNull ? 0x100000000L : 0) | modCount;
|
||||
}
|
||||
|
||||
private boolean tryToSetCachedValues(int deltaUpToRoot, boolean allDeltasUpAreNull, int treeModCount) {
|
||||
if (myIntervalTree.modCount != treeModCount) return false;
|
||||
long newValue = packValues(deltaUpToRoot, allDeltasUpAreNull, treeModCount);
|
||||
long oldValue = cachedDeltaUpToRoot;
|
||||
return cachedDeltaUpdater.compareAndSetLong(this, oldValue, newValue);
|
||||
}
|
||||
|
||||
public void unpackCachedValuesTo(NodeCachedOffsets t) {
|
||||
long value = cachedDeltaUpToRoot;
|
||||
t.deltaUpToRoot = (int)(value >> 33);
|
||||
t.modCount = (int)value;
|
||||
t.allDeltasUpAreNull = ((value >> 32) & 1) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
static class NodeCachedOffsets {
|
||||
private int modCount; // if it equals to the com.intellij.openapi.editor.impl.RedBlackTree.modCount then deltaUpToRoot can be used, otherwise it is expired
|
||||
private int deltaUpToRoot; // sum of all deltas up to the root (including this node' delta). Has valid value only if modCount == IntervalTreeImpl.this.modCount
|
||||
private boolean allDeltasUpAreNull; // true if all deltas up the tree (including this node) are 0. Has valid value only if modCount == IntervalTreeImpl.this.modCount
|
||||
}
|
||||
|
||||
private void assertUnderWriteLock() {
|
||||
@@ -274,11 +313,12 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
|
||||
return s.contains("Locked by thread");
|
||||
}
|
||||
|
||||
private void pushDeltaFromRoot(IntervalNode<T> node) {
|
||||
private void pushDeltaFromRoot(IntervalNode<T> node, NodeCachedOffsets cached) {
|
||||
if (node != null) {
|
||||
if (node.allDeltasUpAreNull && node.isValid() && node.modCount == modCount) return;
|
||||
pushDeltaFromRoot(node.getParent());
|
||||
pushDelta(node);
|
||||
node.unpackCachedValuesTo(cached);
|
||||
if (cached.allDeltasUpAreNull && node.isValid() && cached.modCount == modCount) return;
|
||||
pushDeltaFromRoot(node.getParent(), cached);
|
||||
pushDelta(node, cached);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -469,33 +509,39 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
|
||||
public boolean hasNext() {
|
||||
if (current != null) return true;
|
||||
if (currentNode == null) return false;
|
||||
try {
|
||||
l.readLock().lock();
|
||||
|
||||
if (modCount != modCountBefore) throw new ConcurrentModificationException();
|
||||
while (indexInCurrentList != currentNode.intervals.size()) {
|
||||
T t = currentNode.intervals.get(indexInCurrentList++).get();
|
||||
if (t != null) {
|
||||
current = t;
|
||||
return true;
|
||||
if (modCount != modCountBefore) throw new ConcurrentModificationException();
|
||||
while (indexInCurrentList != currentNode.intervals.size()) {
|
||||
T t = currentNode.intervals.get(indexInCurrentList++).get();
|
||||
if (t != null) {
|
||||
current = t;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
indexInCurrentList = 0;
|
||||
while (true) {
|
||||
currentNode = nextNode(currentNode);
|
||||
if (currentNode == null) {
|
||||
return false;
|
||||
}
|
||||
if (overlaps(currentNode, startOffset, endOffset, deltaUpToRootExclusive)) {
|
||||
assert currentNode.intervalStart() + deltaUpToRootExclusive + currentNode.delta >= firstOverlapStart;
|
||||
indexInCurrentList = 0;
|
||||
while (indexInCurrentList != currentNode.intervals.size()) {
|
||||
T t = currentNode.intervals.get(indexInCurrentList++).get();
|
||||
if (t != null) {
|
||||
current = t;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
indexInCurrentList = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
indexInCurrentList = 0;
|
||||
while (true) {
|
||||
currentNode = nextNode(currentNode);
|
||||
if (currentNode == null) {
|
||||
return false;
|
||||
}
|
||||
if (overlaps(currentNode, startOffset, endOffset, deltaUpToRootExclusive)) {
|
||||
assert currentNode.intervalStart() + deltaUpToRootExclusive + currentNode.delta >= firstOverlapStart;
|
||||
indexInCurrentList = 0;
|
||||
while (indexInCurrentList != currentNode.intervals.size()) {
|
||||
T t = currentNode.intervals.get(indexInCurrentList++).get();
|
||||
if (t != null) {
|
||||
current = t;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
indexInCurrentList = 0;
|
||||
}
|
||||
finally {
|
||||
l.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -517,7 +563,7 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
|
||||
assert root.isValid();
|
||||
int delta = deltaUpToRootExclusive + root.delta;
|
||||
int myMaxEnd = maxEndOf(root, deltaUpToRootExclusive);
|
||||
assert startOffset <= myMaxEnd;
|
||||
if (startOffset > myMaxEnd) return null; // tree changed
|
||||
|
||||
// try to go right down
|
||||
IntervalNode<T> right = root.getRight();
|
||||
@@ -588,13 +634,14 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
|
||||
node.setRight(null);
|
||||
|
||||
List<IntervalNode<T>> gced = new SmartList<IntervalNode<T>>();
|
||||
NodeCachedOffsets cached = new NodeCachedOffsets();
|
||||
if (root == null) {
|
||||
root = node;
|
||||
}
|
||||
else {
|
||||
IntervalNode<T> current = getRoot();
|
||||
while (true) {
|
||||
pushDelta(current);
|
||||
pushDelta(current, cached);
|
||||
int compResult = compareNodes(node, 0, current, 0, gced);
|
||||
if (compResult == 0) {
|
||||
return current;
|
||||
@@ -616,10 +663,9 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
|
||||
}
|
||||
node.setParent(current);
|
||||
}
|
||||
node.setCachedOffsetsAtomically(0, true, modCount);
|
||||
correctMaxUp(node);
|
||||
node.setCachedValues(0, true, modCount);
|
||||
correctMaxUp(node, cached);
|
||||
onInsertNode();
|
||||
assertUnderWriteLock();
|
||||
keySize += node.intervals.size();
|
||||
insertCase1(node);
|
||||
verifyProperties();
|
||||
@@ -698,9 +744,11 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
|
||||
int[] nodeCounter,
|
||||
TLongHashSet ids, boolean allDeltasUpAreNull) {
|
||||
if (root == null) return Trinity.create(Integer.MAX_VALUE,Integer.MIN_VALUE,Integer.MIN_VALUE);
|
||||
if (root.modCount == modCount) {
|
||||
assert root.allDeltasUpAreNull == (root.delta == 0 && allDeltasUpAreNull);
|
||||
assert root.deltaUpToRoot == root.delta + deltaUpToRootExclusive;
|
||||
NodeCachedOffsets cached = new NodeCachedOffsets();
|
||||
root.unpackCachedValuesTo(cached);
|
||||
if (cached.modCount == modCount) {
|
||||
assert cached.allDeltasUpAreNull == (root.delta == 0 && allDeltasUpAreNull);
|
||||
assert cached.deltaUpToRoot == root.delta + deltaUpToRootExclusive;
|
||||
}
|
||||
T liveInterval = null;
|
||||
for (int i = root.intervals.size() - 1; i >= 0; i--) {
|
||||
@@ -753,11 +801,12 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
|
||||
@Override
|
||||
protected Node<T> maximumNode(Node<T> n) {
|
||||
IntervalNode<T> root = (IntervalNode<T>)n;
|
||||
pushDelta(root.getParent());
|
||||
pushDelta(root);
|
||||
NodeCachedOffsets cached = new NodeCachedOffsets();
|
||||
pushDelta(root.getParent(), cached);
|
||||
pushDelta(root, cached);
|
||||
while (root.getRight() != null) {
|
||||
root = root.getRight();
|
||||
pushDelta(root);
|
||||
pushDelta(root, cached);
|
||||
}
|
||||
return root;
|
||||
}
|
||||
@@ -819,15 +868,16 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
|
||||
void removeNode(@NotNull IntervalNode<T> node) {
|
||||
deleteNode(node);
|
||||
IntervalNode<T> parent = node.getParent();
|
||||
correctMaxUp(parent);
|
||||
correctMaxUp(parent, new NodeCachedOffsets());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deleteNode(@NotNull Node<T> n) {
|
||||
assertUnderWriteLock();
|
||||
IntervalNode<T> node = (IntervalNode<T>)n;
|
||||
pushDeltaFromRoot(node);
|
||||
assertAllDeltasAreNull(node);
|
||||
NodeCachedOffsets cached = new NodeCachedOffsets();
|
||||
pushDeltaFromRoot(node, cached);
|
||||
assertAllDeltasAreNull(node, cached);
|
||||
super.deleteNode(n);
|
||||
|
||||
keySize -= node.intervals.size();
|
||||
@@ -840,12 +890,12 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
|
||||
}
|
||||
|
||||
// returns true if all deltas involved are still 0
|
||||
protected boolean pushDelta(IntervalNode<T> root) {
|
||||
protected boolean pushDelta(IntervalNode<T> root, NodeCachedOffsets cached) {
|
||||
if (root == null || !root.isValid()) return true;
|
||||
IntervalNode<T> parent = root.getParent();
|
||||
assertAllDeltasAreNull(parent);
|
||||
assertAllDeltasAreNull(parent, cached);
|
||||
int delta = root.delta;
|
||||
root.modCount = 0;
|
||||
root.setCachedValues(0, true, 0);
|
||||
if (delta != 0) {
|
||||
root.setIntervalStart(root.intervalStart() + delta);
|
||||
root.setIntervalEnd(root.intervalEnd() + delta);
|
||||
@@ -856,7 +906,7 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
|
||||
incDelta(root.getLeft(), delta) &
|
||||
incDelta(root.getRight(), delta);
|
||||
}
|
||||
root.setCachedOffsetsAtomically(0, true, modCount);
|
||||
root.setCachedValues(0, true, modCount);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -896,7 +946,7 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
|
||||
//correctMaxUp(a);
|
||||
a.color = dcolor;
|
||||
d.color = acolor;
|
||||
correctMaxUp(a);
|
||||
correctMaxUp(a, new NodeCachedOffsets());
|
||||
|
||||
checkMax(false);
|
||||
assert a.delta == 0 : a.delta;
|
||||
@@ -956,8 +1006,8 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
|
||||
node.maxEnd = realMax - deltaUpToRoot;
|
||||
}
|
||||
|
||||
private void correctMaxUp(IntervalNode<T> node) {
|
||||
int delta = node == null ? 0 : node.computeDeltaUpToRoot();
|
||||
private void correctMaxUp(IntervalNode<T> node, NodeCachedOffsets cached) {
|
||||
int delta = node == null ? 0 : node.computeDeltaUpToRoot(cached);
|
||||
assert delta == 0 : delta;
|
||||
while (node != null) {
|
||||
if (node.isValid()) {
|
||||
@@ -977,11 +1027,12 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
|
||||
IntervalNode<T> node2 = node1.getLeft();
|
||||
IntervalNode<T> node3 = node1.getRight();
|
||||
|
||||
NodeCachedOffsets cached = new NodeCachedOffsets();
|
||||
IntervalNode<T> parent = node1.getParent();
|
||||
int deltaUp = parent == null ? 0 : parent.computeDeltaUpToRoot();
|
||||
pushDelta(node1);
|
||||
pushDelta(node2);
|
||||
pushDelta(node3);
|
||||
int deltaUp = parent == null ? 0 : parent.computeDeltaUpToRoot(cached);
|
||||
pushDelta(node1, cached);
|
||||
pushDelta(node2, cached);
|
||||
pushDelta(node3, cached);
|
||||
|
||||
super.rotateRight(node1);
|
||||
|
||||
@@ -990,9 +1041,9 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
|
||||
}
|
||||
correctMax(node1, deltaUp);
|
||||
correctMax(node2, deltaUp);
|
||||
assertAllDeltasAreNull(node1);
|
||||
assertAllDeltasAreNull(node2);
|
||||
assertAllDeltasAreNull(node3);
|
||||
assertAllDeltasAreNull(node1, cached);
|
||||
assertAllDeltasAreNull(node2, cached);
|
||||
assertAllDeltasAreNull(node3, cached);
|
||||
checkMax(false);
|
||||
}
|
||||
|
||||
@@ -1003,11 +1054,12 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
|
||||
IntervalNode<T> node2 = node1.getLeft();
|
||||
IntervalNode<T> node3 = node1.getRight();
|
||||
|
||||
NodeCachedOffsets cached = new NodeCachedOffsets();
|
||||
IntervalNode<T> parent = node1.getParent();
|
||||
int deltaUp = parent == null ? 0 : parent.computeDeltaUpToRoot();
|
||||
pushDelta(node1);
|
||||
pushDelta(node2);
|
||||
pushDelta(node3);
|
||||
int deltaUp = parent == null ? 0 : parent.computeDeltaUpToRoot(cached);
|
||||
pushDelta(node1, cached);
|
||||
pushDelta(node2, cached);
|
||||
pushDelta(node3, cached);
|
||||
checkMax(false);
|
||||
super.rotateLeft(node1);
|
||||
|
||||
@@ -1016,9 +1068,9 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
|
||||
}
|
||||
correctMax(node1, deltaUp);
|
||||
correctMax(node3, deltaUp);
|
||||
assertAllDeltasAreNull(node1);
|
||||
assertAllDeltasAreNull(node2);
|
||||
assertAllDeltasAreNull(node3);
|
||||
assertAllDeltasAreNull(node1, cached);
|
||||
assertAllDeltasAreNull(node2, cached);
|
||||
assertAllDeltasAreNull(node3, cached);
|
||||
|
||||
checkMax(false);
|
||||
}
|
||||
@@ -1026,8 +1078,9 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
|
||||
@Override
|
||||
protected void replaceNode(@NotNull Node<T> node, Node<T> child) {
|
||||
IntervalNode<T> myNode = (IntervalNode<T>)node;
|
||||
pushDelta(myNode);
|
||||
pushDelta((IntervalNode<T>)child);
|
||||
NodeCachedOffsets cached = new NodeCachedOffsets();
|
||||
pushDelta(myNode, cached);
|
||||
pushDelta((IntervalNode<T>)child, cached);
|
||||
|
||||
super.replaceNode(node, child);
|
||||
if (child != null && myNode.isValid()) {
|
||||
@@ -1036,11 +1089,12 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
|
||||
}
|
||||
}
|
||||
|
||||
private void assertAllDeltasAreNull(IntervalNode<T> node) {
|
||||
private void assertAllDeltasAreNull(IntervalNode<T> node, NodeCachedOffsets cached) {
|
||||
if (node == null) return;
|
||||
if (!node.isValid()) return;
|
||||
assert node.delta == 0;
|
||||
assert node.modCount != modCount || node.allDeltasUpAreNull;
|
||||
node.unpackCachedValuesTo(cached);
|
||||
assert cached.modCount != modCount || cached.allDeltasUpAreNull;
|
||||
}
|
||||
|
||||
private IntervalNode<T> findMinOverlappingWith(IntervalNode<T> root, Interval interval, int modCountBefore, int deltaUpToRootExclusive) {
|
||||
|
||||
@@ -29,7 +29,7 @@ public class RangeMarkerImpl extends UserDataHolderBase implements RangeMarkerEx
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.editor.impl.RangeMarkerImpl");
|
||||
|
||||
protected final DocumentEx myDocument;
|
||||
protected RangeMarkerTree.RMNode myNode;
|
||||
protected RangeMarkerTree<RangeMarkerEx>.RMNode myNode;
|
||||
|
||||
private final long myId;
|
||||
private static final StripedIDGenerator counter = new StripedIDGenerator();
|
||||
@@ -41,10 +41,10 @@ public class RangeMarkerImpl extends UserDataHolderBase implements RangeMarkerEx
|
||||
if (start < 0) {
|
||||
throw new IllegalArgumentException("Wrong start: " + start+"; end="+end);
|
||||
}
|
||||
else if (end > document.getTextLength()) {
|
||||
if (end > document.getTextLength()) {
|
||||
throw new IllegalArgumentException("Wrong end: " + end+ "; document length="+document.getTextLength()+"; start="+start);
|
||||
}
|
||||
else if (start > end){
|
||||
if (start > end){
|
||||
throw new IllegalArgumentException("start > end: start=" + start+"; end="+end);
|
||||
}
|
||||
|
||||
|
||||
@@ -148,7 +148,7 @@ public class RangeMarkerTree<T extends RangeMarkerEx> extends IntervalTreeImpl<T
|
||||
|
||||
@Override
|
||||
protected void setNode(@NotNull T key, IntervalNode<T> intervalNode) {
|
||||
((RangeMarkerImpl)key).myNode = (RMNode)intervalNode;
|
||||
((RangeMarkerImpl)key).myNode = (RangeMarkerTree.RMNode)intervalNode;
|
||||
}
|
||||
|
||||
public class RMNode extends IntervalTreeImpl.IntervalNode<T> {
|
||||
@@ -183,7 +183,7 @@ public class RangeMarkerTree<T extends RangeMarkerEx> extends IntervalTreeImpl<T
|
||||
|
||||
modCount++;
|
||||
List<IntervalNode<T>> affected = new SmartList<IntervalNode<T>>();
|
||||
collectAffectedMarkersAndShiftSubtrees(getRoot(), e, affected);
|
||||
collectAffectedMarkersAndShiftSubtrees(getRoot(), e, affected, new NodeCachedOffsets());
|
||||
checkMax(false);
|
||||
|
||||
if (!affected.isEmpty()) {
|
||||
@@ -250,9 +250,11 @@ public class RangeMarkerTree<T extends RangeMarkerEx> extends IntervalTreeImpl<T
|
||||
}
|
||||
|
||||
// returns true if all deltas involved are still 0
|
||||
private boolean collectAffectedMarkersAndShiftSubtrees(IntervalNode<T> root, @NotNull DocumentEvent e, @NotNull List<IntervalNode<T>> affected) {
|
||||
private boolean collectAffectedMarkersAndShiftSubtrees(IntervalNode<T> root,
|
||||
@NotNull DocumentEvent e,
|
||||
@NotNull List<IntervalNode<T>> affected, NodeCachedOffsets cached) {
|
||||
if (root == null) return true;
|
||||
boolean norm = pushDelta(root);
|
||||
boolean norm = pushDelta(root, cached);
|
||||
|
||||
int maxEnd = root.maxEnd;
|
||||
assert root.isValid();
|
||||
@@ -277,8 +279,8 @@ public class RangeMarkerTree<T extends RangeMarkerEx> extends IntervalTreeImpl<T
|
||||
int newL = left.changeDelta(-lengthDelta);
|
||||
norm &= newL == 0;
|
||||
}
|
||||
norm &= pushDelta(root);
|
||||
norm &= collectAffectedMarkersAndShiftSubtrees(left, e, affected);
|
||||
norm &= pushDelta(root, cached);
|
||||
norm &= collectAffectedMarkersAndShiftSubtrees(left, e, affected, cached);
|
||||
correctMax(root, 0);
|
||||
}
|
||||
else {
|
||||
@@ -288,8 +290,8 @@ public class RangeMarkerTree<T extends RangeMarkerEx> extends IntervalTreeImpl<T
|
||||
root.setValid(false); //make invisible
|
||||
}
|
||||
|
||||
norm &= collectAffectedMarkersAndShiftSubtrees(root.getLeft(), e, affected);
|
||||
norm &= collectAffectedMarkersAndShiftSubtrees(root.getRight(), e, affected);
|
||||
norm &= collectAffectedMarkersAndShiftSubtrees(root.getLeft(), e, affected, cached);
|
||||
norm &= collectAffectedMarkersAndShiftSubtrees(root.getRight(), e, affected, cached);
|
||||
correctMax(root,0);
|
||||
}
|
||||
return norm;
|
||||
|
||||
+1
-1
@@ -87,7 +87,7 @@ class RangeHighlighterImpl extends RangeMarkerImpl implements RangeHighlighterEx
|
||||
}
|
||||
|
||||
public int getLayer() {
|
||||
RangeHighlighterTree.RHNode node = (RangeHighlighterTree.RHNode)myNode;
|
||||
RangeHighlighterTree.RHNode node = (RangeHighlighterTree.RHNode)(Object)myNode;
|
||||
return node == null ? -1 : node.myLayer;
|
||||
}
|
||||
|
||||
|
||||
@@ -60,6 +60,10 @@ public class AtomicFieldUpdater<T,V> {
|
||||
public static <T,V> AtomicFieldUpdater<T,V> forFieldOfType(@NotNull Class<T> ownerClass, @NotNull Class<V> fieldType) {
|
||||
return new AtomicFieldUpdater<T,V>(ownerClass, fieldType);
|
||||
}
|
||||
@NotNull
|
||||
public static <T> AtomicFieldUpdater<T,Long> forLongField(@NotNull Class<T> ownerClass) {
|
||||
return new AtomicFieldUpdater<T,Long>(ownerClass, long.class);
|
||||
}
|
||||
|
||||
private AtomicFieldUpdater(Class<T> ownerClass, Class<V> fieldType) {
|
||||
Field[] declaredFields = ownerClass.getDeclaredFields();
|
||||
@@ -82,7 +86,7 @@ public class AtomicFieldUpdater<T,V> {
|
||||
throw new IllegalArgumentException("Field "+found+" in the "+ownerClass+" must be volatile");
|
||||
}
|
||||
if ((found.getModifiers() & (Modifier.STATIC | Modifier.FINAL)) != 0) {
|
||||
throw new IllegalArgumentException("Field "+found+" in the "+ownerClass+" must non-final instance");
|
||||
throw new IllegalArgumentException("Field "+found+" in the "+ownerClass+" must be non-final non-static");
|
||||
}
|
||||
offset = unsafe.objectFieldOffset(found);
|
||||
}
|
||||
@@ -90,4 +94,7 @@ public class AtomicFieldUpdater<T,V> {
|
||||
public boolean compareAndSet(@NotNull T owner, V expected, V newValue) {
|
||||
return unsafe.compareAndSwapObject(owner, offset, expected, newValue);
|
||||
}
|
||||
public boolean compareAndSetLong(@NotNull T owner, long expected, long newValue) {
|
||||
return unsafe.compareAndSwapLong(owner, offset, expected, newValue);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user