2 * (C) Copyright 2016 Pantheon Technologies, s.r.o. and others.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package org.opendaylight.yangtools.triemap;
18 import java.util.Optional;
19 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
21 final class INode<K, V> extends BasicNode {
22 static final Object KEY_PRESENT = new Object ();
23 static final Object KEY_ABSENT = new Object ();
26 * Virtual result for lookup methods indicating that the lookup needs to be restarted. This is a faster version
27 * of throwing a checked exception to control the restart.
29 static final Object RESTART = new Object();
31 @SuppressWarnings("rawtypes")
32 private static final AtomicReferenceFieldUpdater<INode, MainNode> MAINNODE_UPDATER =
33 AtomicReferenceFieldUpdater.newUpdater(INode.class, MainNode.class, "mainnode");
35 private final Gen gen;
37 private volatile MainNode<K, V> mainnode;
39 INode(final Gen gen, final MainNode<K, V> mainnode) {
41 this.mainnode = mainnode;
44 MainNode<K, V> gcasRead(final TrieMap<K, V> ct) {
48 MainNode<K, V> GCAS_READ(final TrieMap<K, V> ct) {
49 MainNode<K, V> m = /* READ */ mainnode;
50 MainNode<K, V> prevval = /* READ */ m.READ_PREV();
51 if (prevval == null) {
55 return GCAS_Complete(m, ct);
58 private MainNode<K, V> GCAS_Complete(MainNode<K, V> m, final TrieMap<K, V> ct) {
65 final MainNode<K, V> prev = /* READ */ m.READ_PREV();
66 final INode<K, V> ctr = ct.readRoot(true);
71 if (prev instanceof FailedNode) {
72 // try to commit to previous value
73 FailedNode<K, V> fn = (FailedNode<K, V>) prev;
74 if (MAINNODE_UPDATER.compareAndSet(this, m, fn.READ_PREV())) {
75 return fn.READ_PREV();
78 // Tail recursion: return GCAS_Complete (/* READ */ mainnode, ct);
79 m = /* READ */ mainnode;
83 // Assume that you've read the root from the generation G.
84 // Assume that the snapshot algorithm is correct.
85 // ==> you can only reach nodes in generations <= G.
87 // We know that `ctr.gen` is >= G.
88 // ==> if `ctr.gen` = `gen` then they are both equal to G.
89 // ==> otherwise, we know that either `ctr.gen` > G, `gen` < G,
91 if (ctr.gen == gen && !ct.isReadOnly()) {
93 if (m.CAS_PREV(prev, null)) {
97 // Tail recursion: return GCAS_Complete (m, ct);
102 m.CAS_PREV(prev, new FailedNode<>(prev));
104 // Tail recursion: return GCAS_Complete(/* READ */ mainnode, ct);
105 m = /* READ */ mainnode;
109 private boolean GCAS(final MainNode<K, V> old, final MainNode<K, V> n, final TrieMap<K, V> ct) {
111 if (MAINNODE_UPDATER.compareAndSet(this, old, n)) {
112 GCAS_Complete(n, ct);
113 return /* READ */ n.READ_PREV() == null;
119 private INode<K, V> inode(final MainNode<K, V> cn) {
120 return new INode<>(gen, cn);
123 INode<K, V> copyToGen(final Gen ngen, final TrieMap<K, V> ct) {
124 return new INode<>(ngen, GCAS_READ(ct));
128 * Inserts a key value pair, overwriting the old pair if the keys match.
130 * @return true if successful, false otherwise
132 boolean rec_insert(final K k, final V v, final int hc, final int lev, final INode<K, V> parent,
133 final TrieMap<K, V> ct) {
134 return rec_insert(k, v, hc, lev, parent, gen, ct);
137 private boolean rec_insert(final K k, final V v, final int hc, final int lev, final INode<K, V> parent,
138 final Gen startgen, final TrieMap<K, V> ct) {
140 final MainNode<K, V> m = GCAS_READ (ct); // use -Yinline!
142 if (m instanceof CNode) {
143 // 1) a multiway node
144 final CNode<K, V> cn = (CNode<K, V>) m;
145 final int idx = (hc >>> lev) & 0x1f;
146 final int flag = 1 << idx;
147 final int bmp = cn.bitmap;
148 final int mask = flag - 1;
149 final int pos = Integer.bitCount(bmp & mask);
150 if ((bmp & flag) != 0) {
152 final BasicNode cnAtPos = cn.array[pos];
153 if (cnAtPos instanceof INode) {
154 final INode<K, V> in = (INode<K, V>) cnAtPos;
155 if (startgen == in.gen) {
156 return in.rec_insert(k, v, hc, lev + 5, this, startgen, ct);
158 if (GCAS(cn, cn.renewed(startgen, ct), ct)) {
159 // Tail recursion: return rec_insert (k, v, hc, lev, parent, startgen, ct);
164 } else if (cnAtPos instanceof SNode) {
165 final SNode<K, V> sn = (SNode<K, V>) cnAtPos;
166 if (sn.hc == hc && ct.equal(sn.k, k)) {
167 return GCAS(cn, cn.updatedAt(pos, new SNode<>(k, v, hc), gen), ct);
170 final CNode<K, V> rn = (cn.gen == gen) ? cn : cn.renewed(gen, ct);
171 final MainNode<K, V> nn = rn.updatedAt(pos, inode(CNode.dual(sn, sn.hc, new SNode<>(k, v, hc),
172 hc, lev + 5, gen)), gen);
173 return GCAS (cn, nn, ct);
176 CNode<K, V> rn = (cn.gen == gen) ? cn : cn.renewed(gen, ct);
177 MainNode<K, V> ncnode = rn.insertedAt(pos, flag, new SNode<> (k, v, hc), gen);
178 return GCAS (cn, ncnode, ct);
180 } else if (m instanceof TNode) {
181 clean(parent, ct, lev - 5);
183 } else if (m instanceof LNode) {
184 LNode<K, V> ln = (LNode<K, V>) m;
185 MainNode<K, V> nn = ln.inserted(k, v);
186 return GCAS(ln, nn, ct);
188 throw new IllegalStateException("Unhandled node " + m);
191 throw new RuntimeException ("Should not happen");
196 * Inserts a new key value pair, given that a specific condition is met.
199 * null - don't care if the key was there
200 * KEY_ABSENT - key wasn't there
201 * KEY_PRESENT - key was there
202 * other value `v` - key must be bound to `v`
203 * @return null if unsuccessful, Option[V] otherwise (indicating
204 * previous value bound to the key)
206 Optional<V> rec_insertif(final K k, final V v, final int hc, final Object cond, final int lev,
207 final INode<K, V> parent, final TrieMap<K, V> ct) {
208 return rec_insertif(k, v, hc, cond, lev, parent, gen, ct);
211 private Optional<V> rec_insertif(final K k, final V v, final int hc, final Object cond, final int lev,
212 final INode<K, V> parent, final Gen startgen, final TrieMap<K, V> ct) {
214 final MainNode<K, V> m = GCAS_READ(ct); // use -Yinline!
216 if (m instanceof CNode) {
217 // 1) a multiway node
218 final CNode<K, V> cn = (CNode<K, V>) m;
219 final int idx = (hc >>> lev) & 0x1f;
220 final int flag = 1 << idx;
221 final int bmp = cn.bitmap;
222 final int mask = flag - 1;
223 final int pos = Integer.bitCount(bmp & mask);
225 if ((bmp & flag) != 0) {
227 final BasicNode cnAtPos = cn.array[pos];
228 if (cnAtPos instanceof INode) {
229 final INode<K, V> in = (INode<K, V>) cnAtPos;
230 if (startgen == in.gen) {
231 return in.rec_insertif(k, v, hc, cond, lev + 5, this, startgen, ct);
234 if (GCAS(cn, cn.renewed(startgen, ct), ct)) {
235 // Tail recursion: return rec_insertif (k, v, hc, cond, lev, parent, startgen, ct);
240 } else if (cnAtPos instanceof SNode) {
241 final SNode<K, V> sn = (SNode<K, V>) cnAtPos;
243 if (sn.hc == hc && ct.equal(sn.k, k)) {
244 if (GCAS(cn, cn.updatedAt(pos, new SNode<>(k, v, hc), gen), ct)) {
245 return Optional.of(sn.v);
251 final CNode<K, V> rn = (cn.gen == gen) ? cn : cn.renewed(gen, ct);
252 final MainNode<K, V> nn = rn.updatedAt(pos, inode (CNode.dual(sn, sn.hc,
253 new SNode<>(k, v, hc), hc, lev + 5, gen)), gen);
254 if (GCAS(cn, nn, ct)) {
255 return Optional.empty();
259 } else if (cond == INode.KEY_ABSENT) {
260 if (sn.hc == hc && ct.equal(sn.k, k)) {
261 return Optional.of(sn.v);
264 final CNode<K, V> rn = (cn.gen == gen) ? cn : cn.renewed(gen, ct);
265 final MainNode<K, V> nn = rn.updatedAt(pos, inode (CNode.dual(sn, sn.hc,
266 new SNode<>(k, v, hc), hc, lev + 5, gen)), gen);
267 if (GCAS(cn, nn, ct)) {
268 return Optional.empty();
272 } else if (cond == INode.KEY_PRESENT) {
273 if (sn.hc == hc && ct.equal(sn.k, k)) {
274 if (GCAS(cn, cn.updatedAt(pos, new SNode<>(k, v, hc), gen), ct)) {
275 return Optional.of(sn.v);
280 return Optional.empty();
282 if (sn.hc == hc && ct.equal(sn.k, k) && cond.equals(sn.v)) {
283 if (GCAS(cn, cn.updatedAt(pos, new SNode<>(k, v, hc), gen), ct)) {
284 return Optional.of(sn.v);
290 return Optional.empty();
293 } else if (cond == null || cond == INode.KEY_ABSENT) {
294 final CNode<K, V> rn = (cn.gen == gen) ? cn : cn.renewed(gen, ct);
295 final CNode<K, V> ncnode = rn.insertedAt (pos, flag, new SNode<>(k, v, hc), gen);
296 if (GCAS(cn, ncnode, ct)) {
297 return Optional.empty();
301 } else if (cond == INode.KEY_PRESENT) {
302 return Optional.empty();
304 return Optional.empty();
306 } else if (m instanceof TNode) {
307 clean(parent, ct, lev - 5);
309 } else if (m instanceof LNode) {
311 final LNode<K, V> ln = (LNode<K, V>) m;
313 final Optional<V> optv = ln.get(k);
314 if (insertln(ln, k, v, ct)) {
318 } else if (cond == INode.KEY_ABSENT) {
319 final Optional<V> t = ln.get(k);
323 if (insertln(ln, k, v, ct)) {
324 return Optional.empty();
327 } else if (cond == INode.KEY_PRESENT) {
328 final Optional<V> t = ln.get(k);
329 if (!t.isPresent()) {
332 if (insertln(ln, k, v, ct)) {
337 final Optional<V> t = ln.get(k);
339 if (cond.equals(t.get())) {
340 if (insertln(ln, k, v, ct)) {
341 // Difference from Scala: we choose to reuse the object returned from LNode,
342 // as the identity of the value does not matter in this call graph.
350 return Optional.empty();
353 throw new IllegalStateException("Unhandled node " + m);
356 throw new RuntimeException("Should never happen");
360 boolean insertln(final LNode<K, V> ln, final K k, final V v, final TrieMap<K, V> ct) {
361 final LNode<K, V> nn = ln.inserted (k, v);
362 return GCAS(ln, nn, ct);
366 * Looks up the value associated with the key.
368 * @return null if no value has been found, RESTART if the operation
369 * wasn't successful, or any other value otherwise
371 Object rec_lookup(final K k, final int hc, final int lev, final INode<K, V> parent, final TrieMap<K, V> ct) {
372 return rec_lookup(k, hc, lev, parent, gen, ct);
375 private Object rec_lookup(final K k, final int hc, final int lev, final INode<K, V> parent, final Gen startgen,
376 final TrieMap<K, V> ct) {
378 final MainNode<K, V> m = GCAS_READ(ct); // use -Yinline!
380 if (m instanceof CNode) {
382 final CNode<K, V> cn = (CNode<K, V>) m;
383 final int idx = (hc >>> lev) & 0x1f;
384 final int flag = 1 << idx;
385 final int bmp = cn.bitmap;
386 if ((bmp & flag) == 0) {
387 // 1a) bitmap shows no binding
391 // 1b) bitmap contains a value - descend
392 final int pos = (bmp == 0xffffffff) ? idx : Integer.bitCount(bmp & (flag - 1));
393 final BasicNode sub = cn.array[pos];
394 if (sub instanceof INode) {
395 final INode<K, V> in = (INode<K, V>) sub;
396 if (ct.isReadOnly() || (startgen == in.gen)) {
397 return in.rec_lookup(k, hc, lev + 5, this, startgen, ct);
400 if (GCAS(cn, cn.renewed(startgen, ct), ct)) {
401 // Tail recursion: return rec_lookup(k, hc, lev, parent, startgen, ct);
405 // used to be throw RestartException
407 } else if (sub instanceof SNode) {
409 final SNode<K, V> sn = (SNode<K, V>) sub;
410 if (sn.hc == hc && ct.equal(sn.k, k)) {
416 } else if (m instanceof TNode) {
418 return cleanReadOnly((TNode<K, V>) m, lev, parent, ct, k, hc);
419 } else if (m instanceof LNode) {
421 return ((LNode<K, V>) m).get(k).orElse(null);
423 throw new IllegalStateException("Unhandled node " + m);
426 throw new RuntimeException ("Should not happen");
430 private Object cleanReadOnly(final TNode<K, V> tn, final int lev, final INode<K, V> parent,
431 final TrieMap<K, V> ct, final K k, final int hc) {
432 if (ct.isReadOnly()) {
433 if (tn.hc == hc && ct.equal(tn.k, k)) {
440 // used to be throw RestartException
441 clean(parent, ct, lev - 5);
446 * Removes the key associated with the given value.
449 * if null, will remove the key regardless of the value;
450 * otherwise removes only if binding contains that exact key
452 * @return null if not successful, an Option[V] indicating the previous
455 Optional<V> rec_remove(final K k, final V v, final int hc, final int lev, final INode<K, V> parent,
456 final TrieMap<K, V> ct) {
457 return rec_remove(k, v, hc, lev, parent, gen, ct);
460 private Optional<V> rec_remove(final K k, final V v, final int hc, final int lev, final INode<K, V> parent,
461 final Gen startgen, final TrieMap<K, V> ct) {
462 final MainNode<K, V> m = GCAS_READ(ct); // use -Yinline!
464 if (m instanceof CNode) {
465 final CNode<K, V> cn = (CNode<K, V>) m;
466 final int idx = (hc >>> lev) & 0x1f;
467 final int bmp = cn.bitmap;
468 final int flag = 1 << idx;
469 if ((bmp & flag) == 0) {
470 return Optional.empty();
473 final int pos = Integer.bitCount(bmp & (flag - 1));
474 final BasicNode sub = cn.array[pos];
475 Optional<V> res = null;
476 if (sub instanceof INode) {
477 final INode<K, V> in = (INode<K, V>) sub;
478 if (startgen == in.gen) {
479 res = in.rec_remove(k, v, hc, lev + 5, this, startgen, ct);
481 if (GCAS(cn, cn.renewed (startgen, ct), ct)) {
482 res = rec_remove(k, v, hc, lev, parent, startgen, ct);
488 } else if (sub instanceof SNode) {
489 final SNode<K, V> sn = (SNode<K, V>) sub;
490 if (sn.hc == hc && ct.equal(sn.k, k) && (v == null || v.equals(sn.v))) {
491 final MainNode<K, V> ncn = cn.removedAt(pos, flag, gen).toContracted(lev);
492 if (GCAS(cn, ncn, ct)) {
493 res = Optional.of(sn.v);
498 res = Optional.empty();
502 if (res == null || !res.isPresent()) {
506 if (parent != null) {
507 // never tomb at root
508 final MainNode<K, V> n = GCAS_READ(ct);
509 if (n instanceof TNode) {
510 cleanParent(n, parent, ct, hc, lev, startgen);
515 } else if (m instanceof TNode) {
516 clean(parent, ct, lev - 5);
518 } else if (m instanceof LNode) {
519 final LNode<K, V> ln = (LNode<K, V>) m;
521 final Optional<V> optv = ln.get(k);
522 final MainNode<K, V> nn = ln.removed(k, ct);
523 if (GCAS(ln, nn, ct)) {
530 final Optional<V> tmp = ln.get(k);
531 if (tmp.isPresent() && v.equals(tmp.get())) {
532 final MainNode<K, V> nn = ln.removed(k, ct);
533 if (GCAS(ln, nn, ct)) {
540 // Key not found or value does not match: we have not removed anything
541 return Optional.empty();
543 throw new IllegalStateException("Unhandled node " + m);
547 private void cleanParent(final Object nonlive, final INode<K, V> parent, final TrieMap<K, V> ct, final int hc,
548 final int lev, final Gen startgen) {
550 final MainNode<K, V> pm = parent.GCAS_READ(ct);
551 if ((!(pm instanceof CNode))) {
552 // parent is no longer a cnode, we're done
556 final CNode<K, V> cn = (CNode<K, V>) pm;
557 final int idx = (hc >>> (lev - 5)) & 0x1f;
558 final int bmp = cn.bitmap;
559 final int flag = 1 << idx;
560 if ((bmp & flag) == 0) {
561 // somebody already removed this i-node, we're done
565 final int pos = Integer.bitCount(bmp & (flag - 1));
566 final BasicNode sub = cn.array[pos];
568 if (nonlive instanceof TNode) {
569 final TNode<K, V> tn = (TNode<K, V>) nonlive;
570 MainNode<K, V> ncn = cn.updatedAt(pos, tn.copyUntombed(), gen).toContracted(lev - 5);
571 if (!parent.GCAS(cn, ncn, ct)) {
572 if (ct.readRoot().gen == startgen) {
573 // Tail recursion: cleanParent(nonlive, parent, ct, hc, lev, startgen);
583 private void clean(final INode<K, V> nd, final TrieMap<K, V> ct, final int lev) {
584 final MainNode<K, V> m = nd.GCAS_READ(ct);
585 if (m instanceof CNode) {
586 final CNode<K, V> cn = (CNode<K, V>) m;
587 nd.GCAS(cn, cn.toCompressed(ct, lev, gen), ct);
591 int cachedSize(final TrieMap<K, V> ct) {
592 MainNode<K, V> m = GCAS_READ(ct);
593 return m.cachedSize(ct);
596 // /* this is a quiescent method! */
597 // def string(lev: Int) = "%sINode -> %s".format(" " * lev, mainnode
599 // case null => "<null>"
600 // case tn: TNode[_, _] => "TNode(%s, %s, %d, !)".format(tn.k, tn.v,
602 // case cn: CNode[_, _] => cn.string(lev)
603 // case ln: LNode[_, _] => ln.string(lev)
604 // case x => "<elem: %s>".format(x)
608 String string(final int lev) {