From fe9a3e9c5ef2d2de5fa62a14fa4a613a573e5e5f Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Thu, 22 Aug 2013 23:51:15 +0200 Subject: SI-7269 Rework MapLike#retains to account for desugaring change `MapLike#retains` contains a for-comprehension that relied on the strict `filter` by its generator. You can't, in general, iterate a mutable map and remove items in the same pass. Here's the history of the desugaring of: def retain[A, B](thiz: mutable.Map[A, B])(p: (A, B) => Boolean): thiz.type = { thiz.foreach { case (k, v) => if (p(k, v)) thiz -= k } Before regression (c82ecabad6~1): thiz.filter(((check$ifrefutable$1) => check$ifrefutable$1: @scala.unchecked match { case scala.Tuple2((k @ _), (v @ _)) => true case _ => false })).withFilter(((x$1) => x$1: @scala.unchecked match { case scala.Tuple2((k @ _), (v @ _)) => p(k, v).unary_$bang })).foreach(((x$2) => x$2: @scala.unchecked match { case scala.Tuple2((k @ _), (v @ _)) => thiz.$minus$eq(k) })); After regression (c82ecabad6, which incorrectly assumed in the parser that no filter is required for isInstanceOf[Tuple2]) thiz.withFilter(((x$1) => x$1: @scala.unchecked match { case scala.Tuple2((k @ _), (v @ _)) => p(k, v).unary_$bang })).foreach(((x$2) => x$2: @scala.unchecked match { case scala.Tuple2((k @ _), (v @ _)) => thiz.$minus$eq(k) })); After the reversion of c82ecabad6, v2.10.2 This is also after 365bb2b4e, which uses `withFilter` rather than `filter`. thiz.withFilter(((check$q$1) => check$ifrefutable$1: @scala.unchecked match { case scala.Tuple2((k @ _), (v @ _)) => true case _ => false })).withFilter(((x$1) => x$1: @scala.unchecked match { case scala.Tuple2((k @ _), (v @ _)) => p(k, v).unary_$bang })).foreach(((x$2) => x$2: @scala.unchecked match { case scala.Tuple2((k @ _), (v @ _)) => thiz.$minus$eq(k) })); This commit does the same as `SetLike#retains`, and converts the map to an immutable list before the rest of the operation. --- test/files/run/t7269.scala | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 test/files/run/t7269.scala (limited to 'test/files/run') diff --git a/test/files/run/t7269.scala b/test/files/run/t7269.scala new file mode 100644 index 0000000000..d22e57dfee --- /dev/null +++ b/test/files/run/t7269.scala @@ -0,0 +1,32 @@ +import scala.collection.JavaConversions._ +import scala.collection.mutable + +object Test extends App { + + def testMap(): Unit = { + val mapJ = new java.util.HashMap[Int, String] + val mapS: mutable.Map[Int, String] = mapJ + + (10 to 20).foreach(i => mapS += ((i, i.toString))) + assert(11 == mapS.size) + + // ConcurrentModificationException thrown in the following line + mapS.retain((i, str) => i % 2 == 0) + assert(6 == mapS.size) + } + + def testSet(): Unit = { + val mapJ = new java.util.HashSet[Int] + val mapS: mutable.Set[Int] = mapJ + + (10 to 20).foreach(i => mapS += i) + assert(11 == mapS.size) + + // ConcurrentModificationException thrown in the following line + mapS.retain((i) => i % 2 == 0) + assert(6 == mapS.size) + } + + testSet() + testMap() +} -- cgit v1.2.3 From fb43ec8dc28625c929beaf28767a955388700d0d Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Thu, 5 Sep 2013 15:27:15 +0200 Subject: SI-7814 Avoid init cycle between Predef, `package`, ScalaRuntime Not every application will force these in a single thread; we have to do our best to avoid cycles between them. The enclosed test was failing every time before the change. This commit breaks the cycle by avoiding computing `tupleNames` in the constructor of `ScalaRuntime`. The new version has the added benefit of including specialized tuple subclasses, which is verified with a unit test for `isTuple`. Are there more of these lurking? It seems likely. I'm more than a little concerned about the way the `ControlThrowable` fires up `scala.SystemProperties` to check whether or not to suppress stack traces; there is already an ugly hack in place: object NoStackTrace { final def noSuppression = _noSuppression // two-stage init to make checkinit happy, // since sys.SystemProperties.noTraceSupression.value // calls back into NoStackTrace.noSuppression final private var _noSuppression = false _noSuppression = sys.SystemProperties.noTraceSupression.value } --- src/library/scala/runtime/ScalaRunTime.scala | 14 ++--- test/files/run/predef-cycle.scala | 71 +++++++++++++++++++++++++ test/junit/scala/runtime/ScalaRunTimeTest.scala | 70 ++++++++++++++++++++++++ 3 files changed, 144 insertions(+), 11 deletions(-) create mode 100644 test/files/run/predef-cycle.scala create mode 100644 test/junit/scala/runtime/ScalaRunTimeTest.scala (limited to 'test/files/run') diff --git a/src/library/scala/runtime/ScalaRunTime.scala b/src/library/scala/runtime/ScalaRunTime.scala index 1d8fe5e9ad..4fee6d75a3 100644 --- a/src/library/scala/runtime/ScalaRunTime.scala +++ b/src/library/scala/runtime/ScalaRunTime.scala @@ -34,21 +34,13 @@ object ScalaRunTime { clazz.isArray && (atLevel == 1 || isArrayClass(clazz.getComponentType, atLevel - 1)) def isValueClass(clazz: jClass[_]) = clazz.isPrimitive() - def isTuple(x: Any) = x != null && tupleNames(x.getClass.getName) + + // includes specialized subclasses and future proofed against hypothetical TupleN (for N > 22) + def isTuple(x: Any) = x != null && x.getClass.getName.startsWith("scala.Tuple") def isAnyVal(x: Any) = x match { case _: Byte | _: Short | _: Char | _: Int | _: Long | _: Float | _: Double | _: Boolean | _: Unit => true case _ => false } - // Avoiding boxing which messes up the specialized tests. Don't ask. - private val tupleNames = { - var i = 22 - var names: List[String] = Nil - while (i >= 1) { - names ::= ("scala.Tuple" + String.valueOf(i)) - i -= 1 - } - names.toSet - } /** Return the class object representing an array with element class `clazz`. */ diff --git a/test/files/run/predef-cycle.scala b/test/files/run/predef-cycle.scala new file mode 100644 index 0000000000..ab147688bc --- /dev/null +++ b/test/files/run/predef-cycle.scala @@ -0,0 +1,71 @@ +class Force { + val t1 = new Thread { + override def run() { + scala.`package` + } + } + val t2 = new Thread { + override def run() { + scala.Predef + } + } + t1.start() + t2.start() + t1.join() + t2.join() +} + +object Test { + def main(args: Array[String]) { + new Force() + } +} + +/* Was deadlocking: +"Thread-2" prio=5 tid=7f9637268000 nid=0x119601000 in Object.wait() [119600000] + java.lang.Thread.State: RUNNABLE + at scala.Predef$.(Predef.scala:90) + at scala.Predef$.(Predef.scala) + at Force$$anon$2.run(predef-cycle.scala:10) + +"Thread-1" prio=5 tid=7f9637267800 nid=0x1194fe000 in Object.wait() [1194fb000] + java.lang.Thread.State: RUNNABLE + at scala.collection.immutable.Set$Set4.$plus(Set.scala:127) + at scala.collection.immutable.Set$Set4.$plus(Set.scala:121) + at scala.collection.mutable.SetBuilder.$plus$eq(SetBuilder.scala:24) + at scala.collection.mutable.SetBuilder.$plus$eq(SetBuilder.scala:22) + at scala.collection.generic.Growable$$anonfun$$plus$plus$eq$1.apply(Growable.scala:48) + at scala.collection.generic.Growable$$anonfun$$plus$plus$eq$1.apply(Growable.scala:48) + at scala.collection.immutable.List.foreach(List.scala:318) + at scala.collection.generic.Growable$class.$plus$plus$eq(Growable.scala:48) + at scala.collection.mutable.SetBuilder.$plus$plus$eq(SetBuilder.scala:22) + at scala.collection.TraversableLike$class.to(TraversableLike.scala:629) + at scala.collection.AbstractTraversable.to(Traversable.scala:105) + at scala.collection.TraversableOnce$class.toSet(TraversableOnce.scala:267) + at scala.collection.AbstractTraversable.toSet(Traversable.scala:105) + at scala.runtime.ScalaRunTime$.(ScalaRunTime.scala:50) + at scala.runtime.ScalaRunTime$.(ScalaRunTime.scala) + at scala.collection.mutable.HashTable$HashUtils$class.elemHashCode(HashTable.scala) + at scala.collection.mutable.HashMap.elemHashCode(HashMap.scala:39) + at scala.collection.mutable.HashTable$class.findOrAddEntry(HashTable.scala:161) + at scala.collection.mutable.HashMap.findOrAddEntry(HashMap.scala:39) + at scala.collection.mutable.HashMap.put(HashMap.scala:75) + at scala.collection.mutable.HashMap.update(HashMap.scala:80) + at scala.sys.SystemProperties$.addHelp(SystemProperties.scala:64) + at scala.sys.SystemProperties$.bool(SystemProperties.scala:68) + at scala.sys.SystemProperties$.noTraceSupression$lzycompute(SystemProperties.scala:80) + - locked <7b8b0e228> (a scala.sys.SystemProperties$) + at scala.sys.SystemProperties$.noTraceSupression(SystemProperties.scala:80) + at scala.util.control.NoStackTrace$.(NoStackTrace.scala:31) + at scala.util.control.NoStackTrace$.(NoStackTrace.scala) + at scala.util.control.NoStackTrace$class.fillInStackTrace(NoStackTrace.scala:22) + at scala.util.control.BreakControl.fillInStackTrace(Breaks.scala:93) + at java.lang.Throwable.(Throwable.java:181) + at scala.util.control.BreakControl.(Breaks.scala:93) + at scala.util.control.Breaks.(Breaks.scala:28) + at scala.collection.Traversable$.(Traversable.scala:96) + at scala.collection.Traversable$.(Traversable.scala) + at scala.package$.(package.scala:46) + at scala.package$.(package.scala) + at Force$$anon$1.run(predef-cycle.scala:4) + */ \ No newline at end of file diff --git a/test/junit/scala/runtime/ScalaRunTimeTest.scala b/test/junit/scala/runtime/ScalaRunTimeTest.scala new file mode 100644 index 0000000000..9da197c71a --- /dev/null +++ b/test/junit/scala/runtime/ScalaRunTimeTest.scala @@ -0,0 +1,70 @@ +package scala.runtime + +import org.junit.Assert._ +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +/** Tests for the private class DefaultPromise */ +@RunWith(classOf[JUnit4]) +class ScalaRunTimeTest { + @Test + def testIsTuple() { + import ScalaRunTime.isTuple + def check(v: Any) = { + assertTrue(v.toString, isTuple(v)) + } + + val s = "" + check(Tuple1(s)) + check((s, s)) + check((s, s, s)) + check((s, s, s, s)) + check((s, s, s, s, s)) + check((s, s, s, s, s, s)) + check((s, s, s, s, s, s, s)) + check((s, s, s, s, s, s, s, s)) + check((s, s, s, s, s, s, s, s, s)) + check((s, s, s, s, s, s, s, s, s, s)) + check((s, s, s, s, s, s, s, s, s, s, s)) + check((s, s, s, s, s, s, s, s, s, s, s, s)) + check((s, s, s, s, s, s, s, s, s, s, s, s, s)) + check((s, s, s, s, s, s, s, s, s, s, s, s, s, s)) + check((s, s, s, s, s, s, s, s, s, s, s, s, s, s, s)) + check((s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s)) + check((s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s)) + check((s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s)) + check((s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s)) + check((s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s)) + check((s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s)) + check((s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s)) + + // some specialized variants will have mangled classnames + check(Tuple1(0)) + check((0, 0)) + check((0, 0, 0)) + check((0, 0, 0, 0)) + check((0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) + + case class C() + val c = new C() + assertFalse(c.toString, isTuple(c)) + } +} -- cgit v1.2.3