7031b396c50c8fa12159146d8ef819e3824b1c4b
[yangtools.git] / third-party / triemap / src / main / java / org / opendaylight / yangtools / triemap / INode.java
1 /*
2  * (C) Copyright 2016 Pantheon Technologies, s.r.o. and others.
3  *
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
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package org.opendaylight.yangtools.triemap;
17
18 import static org.opendaylight.yangtools.triemap.LookupResult.RESTART;
19 import static org.opendaylight.yangtools.triemap.PresencePredicate.ABSENT;
20 import static org.opendaylight.yangtools.triemap.PresencePredicate.PRESENT;
21
22 import java.util.Optional;
23 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
24
25 final class INode<K, V> extends BasicNode {
26     @SuppressWarnings("rawtypes")
27     private static final AtomicReferenceFieldUpdater<INode, MainNode> MAINNODE_UPDATER =
28             AtomicReferenceFieldUpdater.newUpdater(INode.class, MainNode.class, "mainnode");
29
30     private final Gen gen;
31
32     private volatile MainNode<K, V> mainnode;
33
34     INode(final Gen gen, final MainNode<K, V> mainnode) {
35         this.gen = gen;
36         this.mainnode = mainnode;
37     }
38
39     MainNode<K, V> gcasRead(final TrieMap<?, ?> ct) {
40         return GCAS_READ(ct);
41     }
42
43     private MainNode<K, V> GCAS_READ(final TrieMap<?, ?> ct) {
44         MainNode<K, V> m = /* READ */ mainnode;
45         MainNode<K, V> prevval = /* READ */ m.READ_PREV();
46         if (prevval == null) {
47             return m;
48         }
49
50         return GCAS_Complete(m, ct);
51     }
52
53     private MainNode<K, V> GCAS_Complete(MainNode<K, V> m, final TrieMap<?, ?> ct) {
54         while (true) {
55             if (m == null) {
56                 return null;
57             }
58
59             // complete the GCAS
60             final MainNode<K, V> prev = /* READ */ m.READ_PREV();
61             final Gen rdgen = ct.readRoot(true).gen;
62             if (prev == null) {
63                 return m;
64             }
65
66             if (prev instanceof FailedNode) {
67                 // try to commit to previous value
68                 FailedNode<K, V> fn = (FailedNode<K, V>) prev;
69                 if (MAINNODE_UPDATER.compareAndSet(this, m, fn.READ_PREV())) {
70                     return fn.READ_PREV();
71                 }
72
73                 // Tail recursion: return GCAS_Complete (/* READ */ mainnode, ct);
74                 m = /* READ */ mainnode;
75                 continue;
76             }
77
78             // Assume that you've read the root from the generation G.
79             // Assume that the snapshot algorithm is correct.
80             // ==> you can only reach nodes in generations <= G.
81             // ==> `gen` is <= G.
82             // We know that `ctr.gen` is >= G.
83             // ==> if `ctr.gen` = `gen` then they are both equal to G.
84             // ==> otherwise, we know that either `ctr.gen` > G, `gen` < G,
85             // or both
86             if (rdgen == gen && !ct.isReadOnly()) {
87                 // try to commit
88                 if (m.CAS_PREV(prev, null)) {
89                     return m;
90                 }
91
92                 // Tail recursion: return GCAS_Complete (m, ct);
93                 continue;
94             }
95
96             // try to abort
97             m.CAS_PREV(prev, new FailedNode<>(prev));
98
99             // Tail recursion: return GCAS_Complete(/* READ */ mainnode, ct);
100             m = /* READ */ mainnode;
101         }
102     }
103
104     private boolean GCAS(final MainNode<K, V> old, final MainNode<K, V> n, final TrieMap<?, ?> ct) {
105         n.WRITE_PREV(old);
106         if (MAINNODE_UPDATER.compareAndSet(this, old, n)) {
107             GCAS_Complete(n, ct);
108             return /* READ */ n.READ_PREV() == null;
109         }
110
111         return false;
112     }
113
114     private INode<K, V> inode(final MainNode<K, V> cn) {
115         return new INode<>(gen, cn);
116     }
117
118     INode<K, V> copyToGen(final Gen ngen, final TrieMap<?, ?> ct) {
119         return new INode<>(ngen, GCAS_READ(ct));
120     }
121
122     /**
123      * Inserts a key value pair, overwriting the old pair if the keys match.
124      *
125      * @return true if successful, false otherwise
126      */
127     boolean rec_insert(final K k, final V v, final int hc, final int lev, final INode<K, V> parent,
128             final TrieMap<K, V> ct) {
129         return rec_insert(k, v, hc, lev, parent, gen, ct);
130     }
131
132     private boolean rec_insert(final K k, final V v, final int hc, final int lev, final INode<K, V> parent,
133             final Gen startgen, final TrieMap<K, V> ct) {
134         while (true) {
135             final MainNode<K, V> m = GCAS_READ(ct); // use -Yinline!
136
137             if (m instanceof CNode) {
138                 // 1) a multiway node
139                 final CNode<K, V> cn = (CNode<K, V>) m;
140                 final int idx = (hc >>> lev) & 0x1f;
141                 final int flag = 1 << idx;
142                 final int bmp = cn.bitmap;
143                 final int mask = flag - 1;
144                 final int pos = Integer.bitCount(bmp & mask);
145
146                 if ((bmp & flag) != 0) {
147                     // 1a) insert below
148                     final BasicNode cnAtPos = cn.array[pos];
149                     if (cnAtPos instanceof INode) {
150                         final INode<K, V> in = (INode<K, V>) cnAtPos;
151                         if (startgen == in.gen) {
152                             return in.rec_insert(k, v, hc, lev + 5, this, startgen, ct);
153                         }
154                         if (GCAS(cn, cn.renewed(startgen, ct), ct)) {
155                             // Tail recursion: return rec_insert (k, v, hc, lev, parent, startgen, ct);
156                             continue;
157                         }
158
159                         return false;
160                     } else if (cnAtPos instanceof SNode) {
161                         final SNode<K, V> sn = (SNode<K, V>) cnAtPos;
162                         if (sn.hc == hc && ct.equal(sn.k, k)) {
163                             return GCAS(cn, cn.updatedAt(pos, new SNode<>(k, v, hc), gen), ct);
164                         }
165
166                         final CNode<K, V> rn = (cn.gen == gen) ? cn : cn.renewed(gen, ct);
167                         final MainNode<K, V> nn = rn.updatedAt(pos, inode(CNode.dual(sn, k, v, hc, lev + 5, gen)), gen);
168                         return GCAS (cn, nn, ct);
169                     }
170                 } else {
171                     final CNode<K, V> rn = (cn.gen == gen) ? cn : cn.renewed(gen, ct);
172                     final MainNode<K, V> ncnode = rn.insertedAt(pos, flag, new SNode<>(k, v, hc), gen);
173                     return GCAS (cn, ncnode, ct);
174                 }
175             } else if (m instanceof TNode) {
176                 clean(parent, ct, lev - 5);
177                 return false;
178             } else if (m instanceof LNode) {
179                 final LNode<K, V> ln = (LNode<K, V>) m;
180                 final LNodeEntry<K, V> entry = ln.get(ct.equiv(), k);
181                 return entry != null ? replaceln(ln, entry, v, ct) : insertln(ln, k, v, ct);
182             } else {
183                 throw new IllegalStateException("Unhandled node " + m);
184             }
185
186             throw new RuntimeException ("Should not happen");
187         }
188     }
189
190     /**
191      * Inserts a new key value pair, given that a specific condition is met.
192      *
193      * @param cond
194      *            null - don't care if the key was there
195      *            KEY_ABSENT - key wasn't there
196      *            KEY_PRESENT - key was there
197      *            other value `v` - key must be bound to `v`
198      * @return null if unsuccessful, Option[V] otherwise (indicating
199      *         previous value bound to the key)
200      */
201     Optional<V> rec_insertif(final K k, final V v, final int hc, final Object cond, final int lev,
202             final INode<K, V> parent, final TrieMap<K, V> ct) {
203         return rec_insertif(k, v, hc, cond, lev, parent, gen, ct);
204     }
205
206     private Optional<V> insertDual(final TrieMap<K, V> ct, final CNode<K, V> cn, final int pos, final SNode<K, V> sn,
207             final K k, final V v, final int hc, final int lev) {
208         final CNode<K, V> rn = (cn.gen == gen) ? cn : cn.renewed(gen, ct);
209         final MainNode<K, V> nn = rn.updatedAt(pos, inode(CNode.dual(sn, k, v, hc, lev + 5, gen)), gen);
210         return GCAS(cn, nn, ct) ? Optional.empty() : null;
211     }
212
213     private Optional<V> rec_insertif(final K k, final V v, final int hc, final Object cond, final int lev,
214             final INode<K, V> parent, final Gen startgen, final TrieMap<K, V> ct) {
215         while (true) {
216             final MainNode<K, V> m = GCAS_READ(ct); // use -Yinline!
217
218             if (m instanceof CNode) {
219                 // 1) a multiway node
220                 final CNode<K, V> cn = (CNode<K, V>) m;
221                 final int idx = (hc >>> lev) & 0x1f;
222                 final int flag = 1 << idx;
223                 final int bmp = cn.bitmap;
224                 final int mask = flag - 1;
225                 final int pos = Integer.bitCount(bmp & mask);
226
227                 if ((bmp & flag) != 0) {
228                     // 1a) insert below
229                     final BasicNode cnAtPos = cn.array[pos];
230                     if (cnAtPos instanceof INode) {
231                         final INode<K, V> in = (INode<K, V>) cnAtPos;
232                         if (startgen == in.gen) {
233                             return in.rec_insertif(k, v, hc, cond, lev + 5, this, startgen, ct);
234                         }
235
236                         if (GCAS(cn, cn.renewed(startgen, ct), ct)) {
237                             // Tail recursion: return rec_insertif (k, v, hc, cond, lev, parent, startgen, ct);
238                             continue;
239                         }
240
241                         return null;
242                     } else if (cnAtPos instanceof SNode) {
243                         final SNode<K, V> sn = (SNode<K, V>) cnAtPos;
244                         if (cond == null) {
245                             if (sn.hc == hc && ct.equal(sn.k, k)) {
246                                 if (GCAS(cn, cn.updatedAt(pos, new SNode<>(k, v, hc), gen), ct)) {
247                                     return Optional.of(sn.v);
248                                 }
249
250                                 return null;
251                             }
252
253                             return insertDual(ct, cn, pos, sn, k, v, hc, lev);
254                         } else if (cond == ABSENT) {
255                             if (sn.hc == hc && ct.equal(sn.k, k)) {
256                                 return Optional.of(sn.v);
257                             }
258
259                             return insertDual(ct, cn, pos, sn, k, v, hc, lev);
260                         } else if (cond == PRESENT) {
261                             if (sn.hc == hc && ct.equal(sn.k, k)) {
262                                 if (GCAS(cn, cn.updatedAt(pos, new SNode<>(k, v, hc), gen), ct)) {
263                                     return Optional.of(sn.v);
264                                 }
265                                 return null;
266                             }
267
268                             return Optional.empty();
269                         } else {
270                             if (sn.hc == hc && ct.equal(sn.k, k) && cond.equals(sn.v)) {
271                                 if (GCAS(cn, cn.updatedAt(pos, new SNode<>(k, v, hc), gen), ct)) {
272                                     return Optional.of(sn.v);
273                                 }
274
275                                 return null;
276                             }
277
278                             return Optional.empty();
279                         }
280                     }
281                 } else if (cond == null || cond == ABSENT) {
282                     final CNode<K, V> rn = (cn.gen == gen) ? cn : cn.renewed(gen, ct);
283                     final CNode<K, V> ncnode = rn.insertedAt (pos, flag, new SNode<>(k, v, hc), gen);
284                     if (GCAS(cn, ncnode, ct)) {
285                         return Optional.empty();
286                     }
287
288                     return null;
289                 } else {
290                     return Optional.empty();
291                 }
292             } else if (m instanceof TNode) {
293                 clean(parent, ct, lev - 5);
294                 return null;
295             } else if (m instanceof LNode) {
296                 // 3) an l-node
297                 final LNode<K, V> ln = (LNode<K, V>) m;
298                 final LNodeEntry<K, V> entry = ln.get(ct.equiv(), k);
299
300                 if (cond == null) {
301                     if (entry != null) {
302                         return replaceln(ln, entry, v, ct) ? Optional.of(entry.getValue()) : null;
303                     }
304
305                     return insertln(ln, k, v, ct) ? Optional.empty() : null;
306                 } else if (cond == ABSENT) {
307                     if (entry != null) {
308                         return Optional.of(entry.getValue());
309                     }
310
311                     return insertln(ln, k, v, ct) ? Optional.empty() : null;
312                 } else if (cond == PRESENT) {
313                     if (entry == null) {
314                         return Optional.empty();
315                     }
316
317                     return replaceln(ln, entry, v, ct) ? Optional.of(entry.getValue()) : null;
318                 } else {
319                     if (entry == null || !cond.equals(entry.getValue())) {
320                         return Optional.empty();
321                     }
322
323                     return replaceln(ln, entry, v, ct) ? Optional.of(entry.getValue()) : null;
324                 }
325             } else {
326                 throw new IllegalStateException("Unhandled node " + m);
327             }
328
329             throw new RuntimeException("Should never happen");
330         }
331     }
332
333     private boolean insertln(final LNode<K, V> ln, final K k, final V v, final TrieMap<K, V> ct) {
334         return GCAS(ln, ln.insertChild(k, v), ct);
335     }
336
337     private boolean replaceln(final LNode<K, V> ln, final LNodeEntry<K, V> entry, final V v, final TrieMap<K, V> ct) {
338         return GCAS(ln, ln.replaceChild(entry, v), ct);
339     }
340
341     /**
342      * Looks up the value associated with the key.
343      *
344      * @return null if no value has been found, RESTART if the operation
345      *         wasn't successful, or any other value otherwise
346      */
347     Object rec_lookup(final K k, final int hc, final int lev, final INode<K, V> parent, final TrieMap<K, V> ct) {
348         return rec_lookup(k, hc, lev, parent, gen, ct);
349     }
350
351     private Object rec_lookup(final K k, final int hc, final int lev, final INode<K, V> parent, final Gen startgen,
352             final TrieMap<K, V> ct) {
353         while (true) {
354             final MainNode<K, V> m = GCAS_READ(ct); // use -Yinline!
355
356             if (m instanceof CNode) {
357                 // 1) a multinode
358                 final CNode<K, V> cn = (CNode<K, V>) m;
359                 final int idx = (hc >>> lev) & 0x1f;
360                 final int flag = 1 << idx;
361                 final int bmp = cn.bitmap;
362
363                 if ((bmp & flag) == 0) {
364                     // 1a) bitmap shows no binding
365                     return null;
366                 }
367
368                 // 1b) bitmap contains a value - descend
369                 final int pos = (bmp == 0xffffffff) ? idx : Integer.bitCount(bmp & (flag - 1));
370                 final BasicNode sub = cn.array[pos];
371                 if (sub instanceof INode) {
372                     final INode<K, V> in = (INode<K, V>) sub;
373                     if (ct.isReadOnly() || (startgen == in.gen)) {
374                         return in.rec_lookup(k, hc, lev + 5, this, startgen, ct);
375                     }
376
377                     if (GCAS(cn, cn.renewed(startgen, ct), ct)) {
378                         // Tail recursion: return rec_lookup(k, hc, lev, parent, startgen, ct);
379                         continue;
380                     }
381
382                     return RESTART;
383                 } else if (sub instanceof SNode) {
384                     // 2) singleton node
385                     final SNode<K, V> sn = (SNode<K, V>) sub;
386                     if (sn.hc == hc && ct.equal(sn.k, k)) {
387                         return sn.v;
388                     }
389
390                     return null;
391                 }
392             } else if (m instanceof TNode) {
393                 // 3) non-live node
394                 return cleanReadOnly((TNode<K, V>) m, lev, parent, ct, k, hc);
395             } else if (m instanceof LNode) {
396                 // 5) an l-node
397                 final LNodeEntry<K, V> entry = ((LNode<K, V>) m).get(ct.equiv(), k);
398                 return entry != null ? entry.getValue() : null;
399             } else {
400                 throw new IllegalStateException("Unhandled node " + m);
401             }
402
403             throw new RuntimeException ("Should not happen");
404         }
405     }
406
407     private Object cleanReadOnly(final TNode<K, V> tn, final int lev, final INode<K, V> parent,
408             final TrieMap<K, V> ct, final K k, final int hc) {
409         if (ct.isReadOnly()) {
410             if (tn.hc == hc && ct.equal(tn.k, k)) {
411                 return tn.v;
412             }
413
414             return null;
415         }
416
417         clean(parent, ct, lev - 5);
418         return RESTART;
419     }
420
421     /**
422      * Removes the key associated with the given value.
423      *
424      * @param cond
425      *            if null, will remove the key regardless of the value;
426      *            otherwise removes only if binding contains that exact key
427      *            and value
428      * @return null if not successful, an Optional indicating the previous
429      *         value otherwise
430      */
431     Optional<V> rec_remove(final K k, final Object cond, final int hc, final int lev, final INode<K, V> parent,
432             final TrieMap<K, V> ct) {
433         return rec_remove(k, cond, hc, lev, parent, gen, ct);
434     }
435
436     private Optional<V> rec_remove(final K k, final Object cond, final int hc, final int lev, final INode<K, V> parent,
437             final Gen startgen, final TrieMap<K, V> ct) {
438         final MainNode<K, V> m = GCAS_READ(ct); // use -Yinline!
439
440         if (m instanceof CNode) {
441             final CNode<K, V> cn = (CNode<K, V>) m;
442             final int idx = (hc >>> lev) & 0x1f;
443             final int bmp = cn.bitmap;
444             final int flag = 1 << idx;
445             if ((bmp & flag) == 0) {
446                 return Optional.empty();
447             }
448
449             final int pos = Integer.bitCount(bmp & (flag - 1));
450             final BasicNode sub = cn.array[pos];
451             Optional<V> res = null;
452             if (sub instanceof INode) {
453                 final INode<K, V> in = (INode<K, V>) sub;
454                 if (startgen == in.gen) {
455                     res = in.rec_remove(k, cond, hc, lev + 5, this, startgen, ct);
456                 } else {
457                     if (GCAS(cn, cn.renewed (startgen, ct), ct)) {
458                         res = rec_remove(k, cond, hc, lev, parent, startgen, ct);
459                     } else {
460                         res = null;
461                     }
462                 }
463
464             } else if (sub instanceof SNode) {
465                 final SNode<K, V> sn = (SNode<K, V>) sub;
466                 if (sn.hc == hc && ct.equal(sn.k, k) && (cond == null || cond.equals(sn.v))) {
467                     final MainNode<K, V> ncn = cn.removedAt(pos, flag, gen).toContracted(lev);
468                     if (GCAS(cn, ncn, ct)) {
469                         res = Optional.of(sn.v);
470                     } else {
471                         res = null;
472                     }
473                 } else {
474                     res = Optional.empty();
475                 }
476             }
477
478             if (res == null || !res.isPresent()) {
479                 return res;
480             }
481
482             if (parent != null) {
483                 // never tomb at root
484                 final MainNode<K, V> n = GCAS_READ(ct);
485                 if (n instanceof TNode) {
486                     cleanParent(n, parent, ct, hc, lev, startgen);
487                 }
488             }
489
490             return res;
491         } else if (m instanceof TNode) {
492             clean(parent, ct, lev - 5);
493             return null;
494         } else if (m instanceof LNode) {
495             final LNode<K, V> ln = (LNode<K, V>) m;
496             final LNodeEntry<K, V> entry = ln.get(ct.equiv(), k);
497             if (entry == null) {
498                 // Key was not found, hence no modification is needed
499                 return Optional.empty();
500             }
501
502             final V value = entry.getValue();
503             if (cond != null && !cond.equals(value)) {
504                 // Value does not match
505                 return Optional.empty();
506             }
507
508             return GCAS(ln, ln.removeChild(entry, hc), ct) ? Optional.of(value) : null;
509         } else {
510             throw new IllegalStateException("Unhandled node " + m);
511         }
512     }
513
514     private void cleanParent(final Object nonlive, final INode<K, V> parent, final TrieMap<K, V> ct, final int hc,
515             final int lev, final Gen startgen) {
516         while (true) {
517             final MainNode<K, V> pm = parent.GCAS_READ(ct);
518             if ((!(pm instanceof CNode))) {
519                 // parent is no longer a cnode, we're done
520                 return;
521             }
522
523             final CNode<K, V> cn = (CNode<K, V>) pm;
524             final int idx = (hc >>> (lev - 5)) & 0x1f;
525             final int bmp = cn.bitmap;
526             final int flag = 1 << idx;
527             if ((bmp & flag) == 0) {
528                 // somebody already removed this i-node, we're done
529                 return;
530             }
531
532             final int pos = Integer.bitCount(bmp & (flag - 1));
533             final BasicNode sub = cn.array[pos];
534             if (sub == this) {
535                 if (nonlive instanceof TNode) {
536                     final TNode<?, ?> tn = (TNode<?, ?>) nonlive;
537                     final MainNode<K, V> ncn = cn.updatedAt(pos, tn.copyUntombed(), gen).toContracted(lev - 5);
538                     if (!parent.GCAS(cn, ncn, ct)) {
539                         if (ct.readRoot().gen == startgen) {
540                             // Tail recursion: cleanParent(nonlive, parent, ct, hc, lev, startgen);
541                             continue;
542                         }
543                     }
544                 }
545             }
546             break;
547         }
548     }
549
550     private void clean(final INode<K, V> nd, final TrieMap<K, V> ct, final int lev) {
551         final MainNode<K, V> m = nd.GCAS_READ(ct);
552         if (m instanceof CNode) {
553             final CNode<K, V> cn = (CNode<K, V>) m;
554             nd.GCAS(cn, cn.toCompressed(ct, lev, gen), ct);
555         }
556     }
557
558     int cachedSize(final TrieMap<?, ?> ct) {
559         return GCAS_READ(ct).cachedSize(ct);
560     }
561
562     // /* this is a quiescent method! */
563     // def string(lev: Int) = "%sINode -> %s".format("  " * lev, mainnode
564     // match {
565     // case null => "<null>"
566     // case tn: TNode[_, _] => "TNode(%s, %s, %d, !)".format(tn.k, tn.v,
567     // tn.hc)
568     // case cn: CNode[_, _] => cn.string(lev)
569     // case ln: LNode[_, _] => ln.string(lev)
570     // case x => "<elem: %s>".format(x)
571     // })
572
573     @Override
574     String string(final int lev) {
575         return "INode";
576     }
577 }