*/
package org.opendaylight.yangtools.triemap;
+import static org.opendaylight.yangtools.triemap.Constants.LEVEL_BITS;
+import static org.opendaylight.yangtools.triemap.LookupResult.RESTART;
+import static org.opendaylight.yangtools.triemap.PresencePredicate.ABSENT;
+import static org.opendaylight.yangtools.triemap.PresencePredicate.PRESENT;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.Optional;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
-final class INode<K, V> extends INodeBase<K, V> {
- static final Object KEY_PRESENT = new Object ();
- static final Object KEY_ABSENT = new Object ();
+final class INode<K, V> extends BasicNode {
+ @SuppressWarnings("rawtypes")
+ private static final AtomicReferenceFieldUpdater<INode, MainNode> MAINNODE_UPDATER =
+ AtomicReferenceFieldUpdater.newUpdater(INode.class, MainNode.class, "mainnode");
- /**
- * Virtual result for lookup methods indicating that the lookup needs to be restarted. This is a faster version
- * of throwing a checked exception to control the restart.
- */
- static final Object RESTART = new Object();
+ private final Gen gen;
+
+ private volatile MainNode<K, V> mainnode;
- INode(final Gen gen, final MainNode<K, V> bn) {
- super(gen, bn);
+ INode(final Gen gen, final MainNode<K, V> mainnode) {
+ this.gen = gen;
+ this.mainnode = mainnode;
}
- MainNode<K, V> gcasRead(final TrieMap<K, V> ct) {
+ MainNode<K, V> gcasRead(final TrieMap<?, ?> ct) {
return GCAS_READ(ct);
}
- MainNode<K, V> GCAS_READ(final TrieMap<K, V> ct) {
- MainNode<K, V> m = /* READ */ READ();
+ private MainNode<K, V> GCAS_READ(final TrieMap<?, ?> ct) {
+ MainNode<K, V> m = /* READ */ mainnode;
MainNode<K, V> prevval = /* READ */ m.READ_PREV();
if (prevval == null) {
return m;
return GCAS_Complete(m, ct);
}
- private MainNode<K, V> GCAS_Complete(MainNode<K, V> m, final TrieMap<K, V> ct) {
+ private MainNode<K, V> GCAS_Complete(MainNode<K, V> m, final TrieMap<?, ?> ct) {
while (true) {
if (m == null) {
return null;
// complete the GCAS
final MainNode<K, V> prev = /* READ */ m.READ_PREV();
- final INode<K, V> ctr = ct.readRoot(true);
+ final Gen rdgen = ct.readRoot(true).gen;
if (prev == null) {
return m;
}
if (prev instanceof FailedNode) {
// try to commit to previous value
FailedNode<K, V> fn = (FailedNode<K, V>) prev;
- if (CAS(m, fn.READ_PREV())) {
+ if (MAINNODE_UPDATER.compareAndSet(this, m, fn.READ_PREV())) {
return fn.READ_PREV();
}
- // Tail recursion: return GCAS_Complete (/* READ */ READ(), ct);
- m = /* READ */ READ();
+ // Tail recursion: return GCAS_Complete (/* READ */ mainnode, ct);
+ m = /* READ */ mainnode;
continue;
}
// ==> if `ctr.gen` = `gen` then they are both equal to G.
// ==> otherwise, we know that either `ctr.gen` > G, `gen` < G,
// or both
- if ((ctr.gen == gen) && ct.nonReadOnly()) {
+ if (rdgen == gen && !ct.isReadOnly()) {
// try to commit
if (m.CAS_PREV(prev, null)) {
return m;
// try to abort
m.CAS_PREV(prev, new FailedNode<>(prev));
- // Tail recursion: return GCAS_Complete(/* READ */ READ(), ct);
- m = /* READ */ READ();
+ // Tail recursion: return GCAS_Complete(/* READ */ mainnode, ct);
+ m = /* READ */ mainnode;
}
}
- private boolean GCAS(final MainNode<K, V> old, final MainNode<K, V> n, final TrieMap<K, V> ct) {
+ private boolean GCAS(final MainNode<K, V> old, final MainNode<K, V> n, final TrieMap<?, ?> ct) {
n.WRITE_PREV(old);
- if (CAS(old, n)) {
+ if (MAINNODE_UPDATER.compareAndSet(this, old, n)) {
GCAS_Complete(n, ct);
return /* READ */ n.READ_PREV() == null;
}
return new INode<>(gen, cn);
}
- INode<K, V> copyToGen(final Gen ngen, final TrieMap<K, V> ct) {
+ INode<K, V> copyToGen(final Gen ngen, final TrieMap<?, ?> ct) {
return new INode<>(ngen, GCAS_READ(ct));
}
*
* @return true if successful, false otherwise
*/
- boolean rec_insert(final K k, final V v, final int hc, final int lev, final INode<K, V> parent, final Gen startgen,
+ boolean rec_insert(final K k, final V v, final int hc, final int lev, final INode<K, V> parent,
final TrieMap<K, V> ct) {
+ return rec_insert(k, v, hc, lev, parent, gen, ct);
+ }
+
+ private boolean rec_insert(final K k, final V v, final int hc, final int lev, final INode<K, V> parent,
+ final Gen startgen, final TrieMap<K, V> ct) {
while (true) {
- final MainNode<K, V> m = GCAS_READ (ct); // use -Yinline!
+ final MainNode<K, V> m = GCAS_READ(ct); // use -Yinline!
if (m instanceof CNode) {
// 1) a multiway node
final int bmp = cn.bitmap;
final int mask = flag - 1;
final int pos = Integer.bitCount(bmp & mask);
+
if ((bmp & flag) != 0) {
// 1a) insert below
final BasicNode cnAtPos = cn.array[pos];
if (cnAtPos instanceof INode) {
final INode<K, V> in = (INode<K, V>) cnAtPos;
if (startgen == in.gen) {
- return in.rec_insert(k, v, hc, lev + 5, this, startgen, ct);
+ return in.rec_insert(k, v, hc, lev + LEVEL_BITS, this, startgen, ct);
}
if (GCAS(cn, cn.renewed(startgen, ct), ct)) {
// Tail recursion: return rec_insert (k, v, hc, lev, parent, startgen, ct);
}
final CNode<K, V> rn = (cn.gen == gen) ? cn : cn.renewed(gen, ct);
- final MainNode<K, V> nn = rn.updatedAt(pos, inode(CNode.dual(sn, sn.hc, new SNode<>(k, v, hc),
- hc, lev + 5, gen)), gen);
+ final MainNode<K, V> nn = rn.updatedAt(pos, inode(CNode.dual(sn, k, v, hc, lev + LEVEL_BITS, gen)), gen);
return GCAS (cn, nn, ct);
}
} else {
- CNode<K, V> rn = (cn.gen == gen) ? cn : cn.renewed(gen, ct);
- MainNode<K, V> ncnode = rn.insertedAt(pos, flag, new SNode<> (k, v, hc), gen);
+ final CNode<K, V> rn = (cn.gen == gen) ? cn : cn.renewed(gen, ct);
+ final MainNode<K, V> ncnode = rn.insertedAt(pos, flag, new SNode<>(k, v, hc), gen);
return GCAS (cn, ncnode, ct);
}
} else if (m instanceof TNode) {
- clean(parent, ct, lev - 5);
+ clean(parent, ct, lev - LEVEL_BITS);
return false;
} else if (m instanceof LNode) {
- LNode<K, V> ln = (LNode<K, V>) m;
- MainNode<K, V> nn = ln.inserted(k, v);
- return GCAS(ln, nn, ct);
+ final LNode<K, V> ln = (LNode<K, V>) m;
+ final LNodeEntry<K, V> entry = ln.get(ct.equiv(), k);
+ return entry != null ? replaceln(ln, entry, v, ct) : insertln(ln, k, v, ct);
} else {
throw new IllegalStateException("Unhandled node " + m);
}
* previous value bound to the key)
*/
Optional<V> rec_insertif(final K k, final V v, final int hc, final Object cond, final int lev,
+ final INode<K, V> parent, final TrieMap<K, V> ct) {
+ return rec_insertif(k, v, hc, cond, lev, parent, gen, ct);
+ }
+
+ @SuppressFBWarnings(value = "NP_OPTIONAL_RETURN_NULL",
+ justification = "Returning null Optional indicates the need to restart.")
+ private Optional<V> insertDual(final TrieMap<K, V> ct, final CNode<K, V> cn, final int pos, final SNode<K, V> sn,
+ final K k, final V v, final int hc, final int lev) {
+ final CNode<K, V> rn = (cn.gen == gen) ? cn : cn.renewed(gen, ct);
+ final MainNode<K, V> nn = rn.updatedAt(pos, inode(CNode.dual(sn, k, v, hc, lev + LEVEL_BITS, gen)), gen);
+ return GCAS(cn, nn, ct) ? Optional.empty() : null;
+ }
+
+ @SuppressFBWarnings(value = "NP_OPTIONAL_RETURN_NULL",
+ justification = "Returning null Optional indicates the need to restart.")
+ private Optional<V> rec_insertif(final K k, final V v, final int hc, final Object cond, final int lev,
final INode<K, V> parent, final Gen startgen, final TrieMap<K, V> ct) {
while (true) {
final MainNode<K, V> m = GCAS_READ(ct); // use -Yinline!
if (cnAtPos instanceof INode) {
final INode<K, V> in = (INode<K, V>) cnAtPos;
if (startgen == in.gen) {
- return in.rec_insertif(k, v, hc, cond, lev + 5, this, startgen, ct);
+ return in.rec_insertif(k, v, hc, cond, lev + LEVEL_BITS, this, startgen, ct);
}
if (GCAS(cn, cn.renewed(startgen, ct), ct)) {
return null;
}
- final CNode<K, V> rn = (cn.gen == gen) ? cn : cn.renewed(gen, ct);
- final MainNode<K, V> nn = rn.updatedAt(pos, inode (CNode.dual(sn, sn.hc,
- new SNode<>(k, v, hc), hc, lev + 5, gen)), gen);
- if (GCAS(cn, nn, ct)) {
- return Optional.empty();
- }
-
- return null;
- } else if (cond == INode.KEY_ABSENT) {
+ return insertDual(ct, cn, pos, sn, k, v, hc, lev);
+ } else if (cond == ABSENT) {
if (sn.hc == hc && ct.equal(sn.k, k)) {
return Optional.of(sn.v);
}
- final CNode<K, V> rn = (cn.gen == gen) ? cn : cn.renewed(gen, ct);
- final MainNode<K, V> nn = rn.updatedAt(pos, inode (CNode.dual(sn, sn.hc,
- new SNode<>(k, v, hc), hc, lev + 5, gen)), gen);
- if (GCAS(cn, nn, ct)) {
- return Optional.empty();
- }
-
- return null;
- } else if (cond == INode.KEY_PRESENT) {
+ return insertDual(ct, cn, pos, sn, k, v, hc, lev);
+ } else if (cond == PRESENT) {
if (sn.hc == hc && ct.equal(sn.k, k)) {
if (GCAS(cn, cn.updatedAt(pos, new SNode<>(k, v, hc), gen), ct)) {
return Optional.of(sn.v);
return Optional.empty();
}
}
- } else if (cond == null || cond == INode.KEY_ABSENT) {
+ } else if (cond == null || cond == ABSENT) {
final CNode<K, V> rn = (cn.gen == gen) ? cn : cn.renewed(gen, ct);
final CNode<K, V> ncnode = rn.insertedAt (pos, flag, new SNode<>(k, v, hc), gen);
if (GCAS(cn, ncnode, ct)) {
}
return null;
- } else if (cond == INode.KEY_PRESENT) {
- return Optional.empty();
} else {
return Optional.empty();
}
} else if (m instanceof TNode) {
- clean(parent, ct, lev - 5);
+ clean(parent, ct, lev - LEVEL_BITS);
return null;
} else if (m instanceof LNode) {
// 3) an l-node
final LNode<K, V> ln = (LNode<K, V>) m;
+ final LNodeEntry<K, V> entry = ln.get(ct.equiv(), k);
+
if (cond == null) {
- final Optional<V> optv = ln.get(k);
- if (insertln(ln, k, v, ct)) {
- return optv;
+ if (entry != null) {
+ return replaceln(ln, entry, v, ct) ? Optional.of(entry.getValue()) : null;
}
- return null;
- } else if (cond == INode.KEY_ABSENT) {
- final Optional<V> t = ln.get(k);
- if (t.isPresent()) {
- return t;
+
+ return insertln(ln, k, v, ct) ? Optional.empty() : null;
+ } else if (cond == ABSENT) {
+ if (entry != null) {
+ return Optional.of(entry.getValue());
}
- if (insertln(ln, k, v, ct)) {
+
+ return insertln(ln, k, v, ct) ? Optional.empty() : null;
+ } else if (cond == PRESENT) {
+ if (entry == null) {
return Optional.empty();
}
- return null;
- } else if (cond == INode.KEY_PRESENT) {
- final Optional<V> t = ln.get(k);
- if (!t.isPresent()) {
- return t;
- }
- if (insertln(ln, k, v, ct)) {
- return t;
- }
- return null;
- } else {
- final Optional<V> t = ln.get(k);
- if (t.isPresent()) {
- if (cond.equals(t.get())) {
- if (insertln(ln, k, v, ct)) {
- // Difference from Scala: we choose to reuse the object returned from LNode,
- // as the identity of the value does not matter in this call graph.
- return t;
- }
- return null;
- }
+ return replaceln(ln, entry, v, ct) ? Optional.of(entry.getValue()) : null;
+ } else {
+ if (entry == null || !cond.equals(entry.getValue())) {
+ return Optional.empty();
}
- return Optional.empty();
+ return replaceln(ln, entry, v, ct) ? Optional.of(entry.getValue()) : null;
}
} else {
throw new IllegalStateException("Unhandled node " + m);
}
}
- boolean insertln(final LNode<K, V> ln, final K k, final V v, final TrieMap<K, V> ct) {
- final LNode<K, V> nn = ln.inserted (k, v);
- return GCAS(ln, nn, ct);
+ private boolean insertln(final LNode<K, V> ln, final K k, final V v, final TrieMap<K, V> ct) {
+ return GCAS(ln, ln.insertChild(k, v), ct);
+ }
+
+ private boolean replaceln(final LNode<K, V> ln, final LNodeEntry<K, V> entry, final V v, final TrieMap<K, V> ct) {
+ return GCAS(ln, ln.replaceChild(entry, v), ct);
}
/**
* @return null if no value has been found, RESTART if the operation
* wasn't successful, or any other value otherwise
*/
- Object rec_lookup(final K k, final int hc, final int lev, final INode<K, V> parent, final Gen startgen,
+ Object rec_lookup(final K k, final int hc, final int lev, final INode<K, V> parent, final TrieMap<K, V> ct) {
+ return rec_lookup(k, hc, lev, parent, gen, ct);
+ }
+
+ private Object rec_lookup(final K k, final int hc, final int lev, final INode<K, V> parent, final Gen startgen,
final TrieMap<K, V> ct) {
while (true) {
final MainNode<K, V> m = GCAS_READ(ct); // use -Yinline!
final int idx = (hc >>> lev) & 0x1f;
final int flag = 1 << idx;
final int bmp = cn.bitmap;
+
if ((bmp & flag) == 0) {
// 1a) bitmap shows no binding
return null;
if (sub instanceof INode) {
final INode<K, V> in = (INode<K, V>) sub;
if (ct.isReadOnly() || (startgen == in.gen)) {
- return in.rec_lookup(k, hc, lev + 5, this, startgen, ct);
+ return in.rec_lookup(k, hc, lev + LEVEL_BITS, this, startgen, ct);
}
if (GCAS(cn, cn.renewed(startgen, ct), ct)) {
continue;
}
- // used to be throw RestartException
return RESTART;
} else if (sub instanceof SNode) {
// 2) singleton node
return cleanReadOnly((TNode<K, V>) m, lev, parent, ct, k, hc);
} else if (m instanceof LNode) {
// 5) an l-node
- return ((LNode<K, V>) m).get(k).orElse(null);
+ final LNodeEntry<K, V> entry = ((LNode<K, V>) m).get(ct.equiv(), k);
+ return entry != null ? entry.getValue() : null;
} else {
throw new IllegalStateException("Unhandled node " + m);
}
private Object cleanReadOnly(final TNode<K, V> tn, final int lev, final INode<K, V> parent,
final TrieMap<K, V> ct, final K k, final int hc) {
- if (ct.nonReadOnly()) {
- // used to be throw RestartException
- clean(parent, ct, lev - 5);
- return RESTART;
- }
+ if (ct.isReadOnly()) {
+ if (tn.hc == hc && ct.equal(tn.k, k)) {
+ return tn.v;
+ }
- if (tn.hc == hc && ct.equal(tn.k, k)) {
- return tn.v;
+ return null;
}
- return null;
+ clean(parent, ct, lev - LEVEL_BITS);
+ return RESTART;
}
/**
* Removes the key associated with the given value.
*
- * @param v
+ * @param cond
* if null, will remove the key regardless of the value;
* otherwise removes only if binding contains that exact key
* and value
- * @return null if not successful, an Option[V] indicating the previous
+ * @return null if not successful, an Optional indicating the previous
* value otherwise
*/
- Optional<V> rec_remove(final K k, final V v, final int hc, final int lev, final INode<K, V> parent,
+ Optional<V> rec_remove(final K k, final Object cond, final int hc, final int lev, final INode<K, V> parent,
+ final TrieMap<K, V> ct) {
+ return rec_remove(k, cond, hc, lev, parent, gen, ct);
+ }
+
+ @SuppressFBWarnings(value = "NP_OPTIONAL_RETURN_NULL",
+ justification = "Returning null Optional indicates the need to restart.")
+ private Optional<V> rec_remove(final K k, final Object cond, final int hc, final int lev, final INode<K, V> parent,
final Gen startgen, final TrieMap<K, V> ct) {
final MainNode<K, V> m = GCAS_READ(ct); // use -Yinline!
if (sub instanceof INode) {
final INode<K, V> in = (INode<K, V>) sub;
if (startgen == in.gen) {
- res = in.rec_remove(k, v, hc, lev + 5, this, startgen, ct);
+ res = in.rec_remove(k, cond, hc, lev + LEVEL_BITS, this, startgen, ct);
} else {
if (GCAS(cn, cn.renewed (startgen, ct), ct)) {
- res = rec_remove(k, v, hc, lev, parent, startgen, ct);
+ res = rec_remove(k, cond, hc, lev, parent, startgen, ct);
} else {
res = null;
}
} else if (sub instanceof SNode) {
final SNode<K, V> sn = (SNode<K, V>) sub;
- if (sn.hc == hc && ct.equal(sn.k, k) && (v == null || v.equals(sn.v))) {
+ if (sn.hc == hc && ct.equal(sn.k, k) && (cond == null || cond.equals(sn.v))) {
final MainNode<K, V> ncn = cn.removedAt(pos, flag, gen).toContracted(lev);
if (GCAS(cn, ncn, ct)) {
res = Optional.of(sn.v);
return res;
} else if (m instanceof TNode) {
- clean(parent, ct, lev - 5);
+ clean(parent, ct, lev - LEVEL_BITS);
return null;
} else if (m instanceof LNode) {
final LNode<K, V> ln = (LNode<K, V>) m;
- if (v == null) {
- final Optional<V> optv = ln.get(k);
- final MainNode<K, V> nn = ln.removed(k, ct);
- if (GCAS(ln, nn, ct)) {
- return optv;
- }
-
- return null;
+ final LNodeEntry<K, V> entry = ln.get(ct.equiv(), k);
+ if (entry == null) {
+ // Key was not found, hence no modification is needed
+ return Optional.empty();
}
- final Optional<V> tmp = ln.get(k);
- if (tmp.isPresent() && v.equals(tmp.get())) {
- final MainNode<K, V> nn = ln.removed(k, ct);
- if (GCAS(ln, nn, ct)) {
- return tmp;
- }
-
- return null;
+ final V value = entry.getValue();
+ if (cond != null && !cond.equals(value)) {
+ // Value does not match
+ return Optional.empty();
}
- // Key not found or value does not match: we have not removed anything
- return Optional.empty();
+ return GCAS(ln, ln.removeChild(entry, hc), ct) ? Optional.of(value) : null;
} else {
throw new IllegalStateException("Unhandled node " + m);
}
}
final CNode<K, V> cn = (CNode<K, V>) pm;
- final int idx = (hc >>> (lev - 5)) & 0x1f;
+ final int idx = (hc >>> (lev - LEVEL_BITS)) & 0x1f;
final int bmp = cn.bitmap;
final int flag = 1 << idx;
if ((bmp & flag) == 0) {
final BasicNode sub = cn.array[pos];
if (sub == this) {
if (nonlive instanceof TNode) {
- final TNode<K, V> tn = (TNode<K, V>) nonlive;
- MainNode<K, V> ncn = cn.updatedAt(pos, tn.copyUntombed(), gen).toContracted(lev - 5);
+ final TNode<?, ?> tn = (TNode<?, ?>) nonlive;
+ final MainNode<K, V> ncn = cn.updatedAt(pos, tn.copyUntombed(), gen).toContracted(lev - LEVEL_BITS);
if (!parent.GCAS(cn, ncn, ct)) {
if (ct.readRoot().gen == startgen) {
// Tail recursion: cleanParent(nonlive, parent, ct, hc, lev, startgen);
}
}
- int cachedSize(final TrieMap<K, V> ct) {
- MainNode<K, V> m = GCAS_READ(ct);
- return m.cachedSize(ct);
+ int cachedSize(final TrieMap<?, ?> ct) {
+ return GCAS_READ(ct).cachedSize(ct);
}
// /* this is a quiescent method! */