Skip to content

Commit 73c6406

Browse files
committed
Do not compute size in TrieMap#isEmpty
Do not compute size in `TrieMap#isEmpty`. Override `TrieMap#knownSize`.
1 parent 22560e9 commit 73c6406

File tree

2 files changed

+27
-17
lines changed

2 files changed

+27
-17
lines changed

library/src/scala/collection/concurrent/MainNode.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ abstract class MainNode<K, V> extends BasicNode {
2424

2525
public abstract int cachedSize(Object ct);
2626

27+
// standard contract
28+
public abstract int knownSize();
29+
2730
public boolean CAS_PREV(MainNode<K, V> oldval, MainNode<K, V> nval) {
2831
return updater.compareAndSet(this, oldval, nval);
2932
}

library/src/scala/collection/concurrent/TrieMap.scala

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -398,10 +398,11 @@ private[collection] final class INode[K, V](bn: MainNode[K, V], g: Gen, equiv: E
398398

399399
def isNullInode(ct: TrieMap[K, V]) = GCAS_READ(ct) eq null
400400

401-
def cachedSize(ct: TrieMap[K, V]): Int = {
402-
val m = GCAS_READ(ct)
403-
m.cachedSize(ct)
404-
}
401+
def cachedSize(ct: TrieMap[K, V]): Int =
402+
GCAS_READ(ct).cachedSize(ct)
403+
404+
def knownSize(ct: TrieMap[K, V]): Int =
405+
GCAS_READ(ct).knownSize()
405406

406407
/* this is a quiescent method! */
407408
def string(lev: Int) = "%sINode -> %s".format(" " * lev, mainnode match {
@@ -438,6 +439,8 @@ private[concurrent] final class FailedNode[K, V](p: MainNode[K, V]) extends Main
438439

439440
def cachedSize(ct: AnyRef): Int = throw new UnsupportedOperationException
440441

442+
def knownSize: Int = throw new UnsupportedOperationException
443+
441444
override def toString = "FailedNode(%s)".format(p)
442445
}
443446

@@ -456,18 +459,19 @@ private[collection] final class SNode[K, V](final val k: K, final val v: V, fina
456459
def string(lev: Int) = (" " * lev) + "SNode(%s, %s, %x)".format(k, v, hc)
457460
}
458461

459-
462+
// Tomb Node, used to ensure proper ordering during removals
460463
private[collection] final class TNode[K, V](final val k: K, final val v: V, final val hc: Int)
461464
extends MainNode[K, V] with KVNode[K, V] {
462465
def copy = new TNode(k, v, hc)
463466
def copyTombed = new TNode(k, v, hc)
464467
def copyUntombed = new SNode(k, v, hc)
465468
def kvPair = (k, v)
466469
def cachedSize(ct: AnyRef): Int = 1
470+
def knownSize: Int = 1
467471
def string(lev: Int) = (" " * lev) + "TNode(%s, %s, %x, !)".format(k, v, hc)
468472
}
469473

470-
474+
// List Node, leaf node that handles hash collisions
471475
private[collection] final class LNode[K, V](val entries: List[(K, V)], equiv: Equiv[K])
472476
extends MainNode[K, V] {
473477

@@ -492,7 +496,7 @@ private[collection] final class LNode[K, V](val entries: List[(K, V)], equiv: Eq
492496

493497
def removed(k: K, ct: TrieMap[K, V]): MainNode[K, V] = {
494498
val updmap = entries.filterNot(entry => equiv.equiv(entry._1, k))
495-
if (updmap.size > 1) new LNode(updmap, equiv)
499+
if (updmap.sizeIs > 1) new LNode(updmap, equiv)
496500
else {
497501
val (k, v) = updmap.iterator.next()
498502
new TNode(k, v, ct.computeHash(k)) // create it tombed so that it gets compressed on subsequent accesses
@@ -503,14 +507,16 @@ private[collection] final class LNode[K, V](val entries: List[(K, V)], equiv: Eq
503507

504508
def cachedSize(ct: AnyRef): Int = entries.size
505509

510+
def knownSize: Int = -1 // shouldn't ever be empty, and the size of a list is not known
511+
506512
def string(lev: Int) = (" " * lev) + "LNode(%s)".format(entries.mkString(", "))
507513

508514
}
509515

510-
516+
// Ctrie Node, contains bitmap and array of references to branch nodes
511517
private[collection] final class CNode[K, V](val bitmap: Int, val array: Array[BasicNode], val gen: Gen) extends CNodeBase[K, V] {
512518
// this should only be called from within read-only snapshots
513-
def cachedSize(ct: AnyRef) = {
519+
def cachedSize(ct: AnyRef): Int = {
514520
val currsz = READ_SIZE()
515521
if (currsz != -1) currsz
516522
else {
@@ -520,6 +526,8 @@ private[collection] final class CNode[K, V](val bitmap: Int, val array: Array[Ba
520526
}
521527
}
522528

529+
def knownSize: Int = READ_SIZE() // this should only ever return -1 if unknown
530+
523531
// lends itself towards being parallelizable by choosing
524532
// a random starting offset in the array
525533
// => if there are concurrent size computations, they start
@@ -676,6 +684,7 @@ private[concurrent] case class RDCSS_Descriptor[K, V](old: INode[K, V], expected
676684
*
677685
* For details, see: [[http://lampwww.epfl.ch/~prokopec/ctries-snapshot.pdf]]
678686
*/
687+
@SerialVersionUID(-5212455458703321708L)
679688
final class TrieMap[K, V] private (r: AnyRef, rtupd: AtomicReferenceFieldUpdater[TrieMap[K, V], AnyRef], hashf: Hashing[K], ef: Equiv[K])
680689
extends scala.collection.mutable.AbstractMap[K, V]
681690
with scala.collection.concurrent.Map[K, V]
@@ -1002,16 +1011,14 @@ final class TrieMap[K, V] private (r: AnyRef, rtupd: AtomicReferenceFieldUpdater
10021011
// END extra overrides
10031012
///////////////////////////////////////////////////////////////////
10041013

1005-
1006-
private def cachedSize() = {
1007-
val r = RDCSS_READ_ROOT()
1008-
r.cachedSize(this)
1009-
}
1010-
10111014
override def size: Int =
10121015
if (nonReadOnly) readOnlySnapshot().size
1013-
else cachedSize()
1014-
override def isEmpty: Boolean = size == 0
1016+
else RDCSS_READ_ROOT().cachedSize(this)
1017+
override def knownSize: Int =
1018+
if (nonReadOnly) -1
1019+
else RDCSS_READ_ROOT().knownSize(this)
1020+
override def isEmpty: Boolean =
1021+
(if (nonReadOnly) readOnlySnapshot() else this).sizeIs == 0 // sizeIs checks knownSize
10151022
override protected[this] def className = "TrieMap"
10161023

10171024
}

0 commit comments

Comments
 (0)