import com.google.common.base.Verify;
import java.util.concurrent.ThreadLocalRandom;
-import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
final class CNode<K, V> extends MainNode<K, V> {
- @SuppressWarnings("rawtypes")
- private static final AtomicIntegerFieldUpdater<CNode> CSIZE_UPDATER = AtomicIntegerFieldUpdater.newUpdater(
- CNode.class, "csize");
-
private static final BasicNode[] EMPTY_ARRAY = new BasicNode[0];
- private static final int NO_SIZE = -1;
final int bitmap;
final BasicNode[] array;
final Gen gen;
+ // Since concurrent computation should lead to same results we can update this field without any synchronization.
private volatile int csize = NO_SIZE;
private CNode(final Gen gen, final int bitmap, final BasicNode... array) {
return xidx < yidx ? new CNode<>(gen, bmp, x, y) : new CNode<>(gen, bmp, y, x);
}
- // this should only be called from within read-only snapshots
@Override
- int cachedSize(final TrieMap<?, ?> ct) {
- int sz = csize;
- if (sz == NO_SIZE) {
- // We have not computed the size yet, do that now
- sz = computeSize(ct);
- if (!CSIZE_UPDATER.compareAndSet(this, NO_SIZE, sz)) {
- // We have been pre-empted by some else: read the result
- sz = csize;
- }
- }
+ int trySize() {
+ return csize;
+ }
- return sz;
+ @Override
+ int size(final ImmutableTrieMap<?, ?> ct) {
+ int sz;
+ return (sz = csize) != NO_SIZE ? sz : (csize = computeSize(ct));
}
// lends itself towards being parallelizable by choosing
// => if there are concurrent size computations, they start
// at different positions, so they are more likely to
// to be independent
- private int computeSize(final TrieMap<?, ?> ct) {
+ private int computeSize(final ImmutableTrieMap<?, ?> ct) {
final int len = array.length;
switch (len) {
case 0:
}
}
- private static int elementSize(final BasicNode elem, final TrieMap<?, ?> ct) {
+ private static int elementSize(final BasicNode elem, final ImmutableTrieMap<?, ?> ct) {
if (elem instanceof SNode) {
return 1;
} else if (elem instanceof INode) {
- return ((INode<?, ?>) elem).cachedSize(ct);
+ return ((INode<?, ?>) elem).size(ct);
} else {
throw new IllegalStateException("Unhandled element " + elem);
}
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
abstract class MainNode<K, V> extends BasicNode {
+ static final int NO_SIZE = -1;
+
@SuppressWarnings("rawtypes")
private static final AtomicReferenceFieldUpdater<MainNode, MainNode> PREV_UPDATER =
AtomicReferenceFieldUpdater.newUpdater(MainNode.class, MainNode.class, "prev");
this.prev = prev;
}
- abstract int cachedSize(TrieMap<?, ?> ct);
+ /**
+ * Return the number of entries in this node, or {@link #NO_SIZE} if it is not known.
+ */
+ abstract int trySize();
+
+ /**
+ * Return the number of entries in this node, traversing it if need be. This method should be invoked only
+ * on immutable snapshots.
+ *
+ * @param ct TrieMap reference
+ * @return The actual number of entries.
+ */
+ abstract int size(ImmutableTrieMap<?, ?> ct);
final boolean CAS_PREV(final MainNode<K, V> oldval, final MainNode<K, V> nval) {
return PREV_UPDATER.compareAndSet(this, oldval, nval);