diff --git a/src/library/scala/collection/convert/JavaCollectionWrappers.scala b/src/library/scala/collection/convert/JavaCollectionWrappers.scala index b1045e3d1f96..1efe0d826ba9 100644 --- a/src/library/scala/collection/convert/JavaCollectionWrappers.scala +++ b/src/library/scala/collection/convert/JavaCollectionWrappers.scala @@ -452,6 +452,10 @@ private[collection] object JavaCollectionWrappers extends Serializable { case _ if isEmpty => None case _ => Try(last).toOption } + + override def updateWith(key: K)(remappingFunction: Option[V] => Option[V]): Option[V] = Option { + underlying.compute(key, (_: K, v: V) => remappingFunction(Option(v)).getOrElse(null.asInstanceOf[V])) + } } @SerialVersionUID(3L) diff --git a/test/junit/scala/collection/convert/MapWrapperTest.scala b/test/junit/scala/collection/convert/MapWrapperTest.scala index 089b974625b6..7861dbe66a96 100644 --- a/test/junit/scala/collection/convert/MapWrapperTest.scala +++ b/test/junit/scala/collection/convert/MapWrapperTest.scala @@ -1,6 +1,6 @@ package scala.collection.convert -import java.util +import java.{util => jutil} import org.junit.Assert._ import org.junit.Test @@ -8,6 +8,7 @@ import org.junit.runner.RunWith import org.junit.runners.JUnit4 import scala.jdk.CollectionConverters._ +import scala.util.chaining._ @RunWith(classOf[JUnit4]) class MapWrapperTest { @@ -63,7 +64,7 @@ class MapWrapperTest { // regression test for https://github.com/scala/bug/issues/10663 @Test def testHashCodeEqualsMatchesJavaMap(): Unit = { - val jmap = new util.HashMap[String, String]() + val jmap = new jutil.HashMap[String, String]() jmap.put("scala", "rocks") jmap.put("java interop is fun!", "ya!") jmap.put("Ĺởồҝ ïŧ\\'ş ūŋǐčōđẹ", "whyyyy") @@ -79,4 +80,31 @@ class MapWrapperTest { assertTrue(jmap == mapWrapper) assertTrue(mapWrapper == jmap) } + + // was: induce intermittent failure due to contention, where updater is called more than once + @Test def `t12586 updateWith should delegate to compute`: Unit = { + final val limit = 100 // retries until trigger + @volatile var count = 0 + val jmap = new jutil.concurrent.ConcurrentHashMap[String, String]() + class Loki extends Runnable { + @volatile var done = false + def run(): Unit = { + while (!done) { + jmap.put("KEY", "VALUE") + //Thread.`yield`() + } + } + } + val loki = new Loki + val runner = new Thread(loki).tap(_.start) + val wrapped = jmap.asScala + def updater(old: Option[String]) = { count += 1 ; old.map(_ * 2) } + for (i <- 1 to limit) { + count = 0 + wrapped.updateWith("KEY")(updater) + assertEquals(s"index $i", 1, count) + } + loki.done = true + runner.join() + } }