summaryrefslogtreecommitdiff
path: root/src/library/scala/collection
diff options
context:
space:
mode:
authorJason Zaugg <jzaugg@gmail.com>2017-02-10 08:22:14 +1000
committerJason Zaugg <jzaugg@gmail.com>2017-02-10 10:14:36 +1000
commitd1dbf1864c4853854701c1b0c51c0e5e21eda9d8 (patch)
tree2b1416b3717ef5f1ccd46b4bbcd93d4aac2c16cb /src/library/scala/collection
parentb9d4089d19ead36d07c2d6cdda283c9b678d515e (diff)
downloadscala-d1dbf1864c4853854701c1b0c51c0e5e21eda9d8.tar.gz
scala-d1dbf1864c4853854701c1b0c51c0e5e21eda9d8.tar.bz2
scala-d1dbf1864c4853854701c1b0c51c0e5e21eda9d8.zip
SI-10177 Override lazy operations to preserve TrieMap's semantics
Calling .iterator on a TrieMap gives a read-only snapshot. This then extends to most inherited implementations written in terms of .iterator. However, some inherited methods, such as .values, either defer the call to .iterator or call it more than once. This results in subsequent mutations to the original map being visible I reviewed the inherited implementations from MapLike and found we needed overrides of `values`, `keySet`, `filterKeys`, and `mapValues`. Like `iterator`, these now create a read-only snapshot.
Diffstat (limited to 'src/library/scala/collection')
-rw-r--r--src/library/scala/collection/concurrent/TrieMap.scala27
1 files changed, 27 insertions, 0 deletions
diff --git a/src/library/scala/collection/concurrent/TrieMap.scala b/src/library/scala/collection/concurrent/TrieMap.scala
index 769d7b0dac..fe0b5c4f0e 100644
--- a/src/library/scala/collection/concurrent/TrieMap.scala
+++ b/src/library/scala/collection/concurrent/TrieMap.scala
@@ -932,6 +932,33 @@ extends scala.collection.concurrent.Map[K, V]
if (nonReadOnly) readOnlySnapshot().iterator
else new TrieMapIterator(0, this)
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // SI-10177 These methods need overrides as the inherited implementations
+ // call `.iterator` more than once, which doesn't guarantee a coherent
+ // view of the data if there is a concurrent writer
+ // Note that the we don't need overrides for keysIterator or valuesIterator
+ // TrieMapTest validates the behaviour.
+ override def values: Iterable[V] = {
+ if (nonReadOnly) readOnlySnapshot().values
+ else super.values
+ }
+ override def keySet: Set[K] = {
+ if (nonReadOnly) readOnlySnapshot().keySet
+ else super.keySet
+ }
+ override def filterKeys(p: K => Boolean): collection.Map[K, V] = {
+ if (nonReadOnly) readOnlySnapshot().filterKeys(p)
+ else super.filterKeys(p)
+ }
+ override def mapValues[W](f: V => W): collection.Map[K, W] = {
+ if (nonReadOnly) readOnlySnapshot().mapValues(f)
+ else super.mapValues(f)
+ }
+ // END extra overrides
+ ///////////////////////////////////////////////////////////////////
+
+
private def cachedSize() = {
val r = RDCSS_READ_ROOT()
r.cachedSize(this)