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 static com.google.common.base.Preconditions.checkState;
19 import static java.util.Objects.requireNonNull;
20 import static org.opendaylight.yangtools.triemap.PresencePredicate.ABSENT;
21 import static org.opendaylight.yangtools.triemap.PresencePredicate.PRESENT;
23 import com.google.common.annotations.Beta;
24 import com.google.common.base.Verify;
25 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
26 import java.util.Optional;
27 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
32 * @author Robert Varga
34 * @param <K> the type of keys maintained by this map
35 * @param <V> the type of mapped values
38 public final class MutableTrieMap<K, V> extends TrieMap<K, V> {
39 private static final long serialVersionUID = 1L;
41 @SuppressWarnings("rawtypes")
42 private static final AtomicReferenceFieldUpdater<MutableTrieMap, Object> ROOT_UPDATER =
43 AtomicReferenceFieldUpdater.newUpdater(MutableTrieMap.class, Object.class, "root");
45 private volatile Object root;
47 MutableTrieMap(final Equivalence<? super K> equiv) {
48 this(equiv, newRootNode());
51 MutableTrieMap(final Equivalence<? super K> equiv, final INode<K, V> root) {
53 this.root = requireNonNull(root);
60 r = RDCSS_READ_ROOT();
61 } while (!RDCSS_ROOT(r, r.gcasRead(this), newRootNode()));
65 public V put(final K key, final V value) {
66 final K k = requireNonNull(key);
67 return toNullable(insertifhc(k, computeHash(k), requireNonNull(value), null));
71 public V putIfAbsent(final K key, final V value) {
72 final K k = requireNonNull(key);
73 return toNullable(insertifhc(k, computeHash(k), requireNonNull(value), ABSENT));
77 public V remove(final Object key) {
78 @SuppressWarnings("unchecked")
79 final K k = (K) requireNonNull(key);
80 return toNullable(removehc(k, null, computeHash(k)));
83 @SuppressFBWarnings(value = "NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE",
84 justification = "API contract allows null value, but we do not")
86 public boolean remove(final Object key, final Object value) {
87 @SuppressWarnings("unchecked")
88 final K k = (K) requireNonNull(key);
89 return removehc(k, requireNonNull(value), computeHash(k)).isPresent();
93 public boolean replace(final K key, final V oldValue, final V newValue) {
94 final K k = requireNonNull(key);
95 return insertifhc(k, computeHash(k), requireNonNull(newValue), requireNonNull(oldValue)).isPresent();
99 public V replace(final K key, final V value) {
100 final K k = requireNonNull(key);
101 return toNullable(insertifhc(k, computeHash(k), requireNonNull(value), PRESENT));
106 return immutableSnapshot().size();
109 private INode<K, V> snapshot() {
112 r = RDCSS_READ_ROOT();
113 } while (!RDCSS_ROOT(r, r.gcasRead(this), r.copyToGen(new Gen(), this)));
119 public ImmutableTrieMap<K, V> immutableSnapshot() {
120 return new ImmutableTrieMap<>(snapshot(), equiv());
124 public MutableTrieMap<K, V> mutableSnapshot() {
125 return new MutableTrieMap<>(equiv(), snapshot().copyToGen(new Gen(), this));
129 MutableEntrySet<K, V> createEntrySet() {
130 // FIXME: it would be nice to have a ReadWriteTrieMap with read-only iterator
131 // if (readOnlyEntrySet) return ImmutableEntrySet(this);
132 return new MutableEntrySet<>(this);
136 MutableKeySet<K> createKeySet() {
137 return new MutableKeySet<>(this);
141 MutableIterator<K, V> iterator() {
142 return new MutableIterator<>(this);
146 boolean isReadOnly() {
151 @SuppressWarnings("unchecked")
152 INode<K, V> RDCSS_READ_ROOT(final boolean abort) {
153 final Object r = /* READ */ root;
154 if (r instanceof INode) {
155 return (INode<K, V>) r;
158 checkState(r instanceof RDCSS_Descriptor, "Unhandled root %s", r);
159 return RDCSS_Complete(abort);
162 void add(final K key, final V value) {
163 final K k = requireNonNull(key);
164 inserthc(k, computeHash(k), requireNonNull(value));
167 private static <K,V> INode<K, V> newRootNode() {
168 final Gen gen = new Gen();
169 return new INode<>(gen, new CNode<>(gen));
172 private void inserthc(final K key, final int hc, final V value) {
173 // TODO: this is called from serialization only, which means we should not be observing any races,
174 // hence we should not need to pass down the entire tree, just equality (I think).
175 final boolean success = RDCSS_READ_ROOT().recInsert(key, value, hc, 0, null, this);
176 Verify.verify(success, "Concurrent modification during serialization of map %s", this);
179 private Optional<V> insertifhc(final K key, final int hc, final V value, final Object cond) {
182 // Keep looping as long as we do not get a reply
183 res = RDCSS_READ_ROOT().recInsertIf(key, value, hc, cond, 0, null, this);
184 } while (res == null);
189 private Optional<V> removehc(final K key, final Object cond, final int hc) {
192 // Keep looping as long as we do not get a reply
193 res = RDCSS_READ_ROOT().recRemove(key, cond, hc, 0, null, this);
194 } while (res == null);
199 private boolean CAS_ROOT(final Object ov, final Object nv) {
200 return ROOT_UPDATER.compareAndSet(this, ov, nv);
203 private boolean RDCSS_ROOT(final INode<K, V> ov, final MainNode<K, V> expectedmain, final INode<K, V> nv) {
204 final RDCSS_Descriptor<K, V> desc = new RDCSS_Descriptor<>(ov, expectedmain, nv);
205 if (CAS_ROOT(ov, desc)) {
206 RDCSS_Complete(false);
207 return /* READ */desc.committed;
213 @SuppressWarnings("unchecked")
214 private INode<K, V> RDCSS_Complete(final boolean abort) {
216 final Object r = /* READ */ root;
217 if (r instanceof INode) {
218 return (INode<K, V>) r;
221 checkState(r instanceof RDCSS_Descriptor, "Unhandled root %s", r);
222 final RDCSS_Descriptor<K, V> desc = (RDCSS_Descriptor<K, V>) r;
223 final INode<K, V> ov = desc.old;
224 final MainNode<K, V> exp = desc.expectedmain;
225 final INode<K, V> nv = desc.nv;
228 if (CAS_ROOT(desc, ov)) {
232 // Tail recursion: return RDCSS_Complete(abort);
236 final MainNode<K, V> oldmain = ov.gcasRead(this);
237 if (oldmain == exp) {
238 if (CAS_ROOT(desc, nv)) {
239 desc.committed = true;
243 // Tail recursion: return RDCSS_Complete(abort);
247 if (CAS_ROOT(desc, ov)) {
251 // Tail recursion: return RDCSS_Complete(abort);
255 private static final class RDCSS_Descriptor<K, V> {
256 final INode<K, V> old;
257 final MainNode<K, V> expectedmain;
258 final INode<K, V> nv;
260 volatile boolean committed = false;
262 RDCSS_Descriptor(final INode<K, V> old, final MainNode<K, V> expectedmain, final INode<K, V> nv) {
264 this.expectedmain = expectedmain;