diff options
author | Jason Zaugg <jzaugg@gmail.com> | 2017-02-10 08:22:14 +1000 |
---|---|---|
committer | Jason Zaugg <jzaugg@gmail.com> | 2017-02-10 10:14:36 +1000 |
commit | d1dbf1864c4853854701c1b0c51c0e5e21eda9d8 (patch) | |
tree | 2b1416b3717ef5f1ccd46b4bbcd93d4aac2c16cb /src/library/scala/collection | |
parent | b9d4089d19ead36d07c2d6cdda283c9b678d515e (diff) | |
download | scala-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.scala | 27 |
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) |