Thread Safety in Scala reflection with type matching

无人久伴 提交于 2021-02-10 14:16:32

问题


Working in scala 2.11.12, JDK 1.8.0_131, I have been able to replicate a thread safety bug observed in Apache Spark with the following code, in which I repeatedly check with multiple threads whether Option[Int] can be matched via <:< to Option[_]:

package stuff

import java.util.concurrent.{Executors, Future}

import scala.collection.mutable.ListBuffer

object Main {
  val universe: scala.reflect.runtime.universe.type = scala.reflect.runtime.universe
  import universe._

  def mirror: universe.Mirror = {
    universe.runtimeMirror(Thread.currentThread().getContextClassLoader)
  }

  def localTypeOf[T: TypeTag]: `Type` = {
    val tag = implicitly[TypeTag[T]]
    tag.in(mirror).tpe.dealias
  }

  def matcher[T: TypeTag]: Boolean = {
    val typ = localTypeOf[T]
    typ.dealias match {
      case t if t <:< localTypeOf[Option[_]] =>
        true
      case _ =>
        false
    }
  }

  def main(args: Array[String]): Unit =  {
    val executor = Executors.newFixedThreadPool(5)

    try {
      val futures = new ListBuffer[Future[_]]()

      for (i <- 1 to 10) {
        futures += executor.submit(new Runnable {
          override def run(): Unit = {
            if (Main.matcher[Option[Int]]) {
              println("ALL OK")
            } else {
              throw new Exception("THIS SHOULD BE IMPOSSIBLE!!!!!!")
            }
          }
        })
      }

      futures.foreach(_.get())
    } finally {
      executor.shutdown()
    }
  }
}

This code should always print "ALL OK", but sometimes (~5% chance) it actually throws the "THIS SHOULD BE IMPOSSIBLE" error, with the following stacktrace:

Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.Exception: THIS SHOULD BE IMPOSSIBLE!!!!!!
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:192)
at stuff.Main$$anonfun$main$2.apply(Main.scala:81)
at stuff.Main$$anonfun$main$2.apply(Main.scala:81)
at scala.collection.immutable.List.foreach(List.scala:392)
at scala.collection.generic.TraversableForwarder$class.foreach(TraversableForwarder.scala:35)
at scala.collection.mutable.ListBuffer.foreach(ListBuffer.scala:45)
at stuff.Main$.main(Main.scala:81)
at stuff.Main.main(Main.scala)
Caused by: java.lang.Exception: THIS SHOULD BE IMPOSSIBLE!!!!!!
at stuff.Main$$anonfun$main$1$$anon$1.run(Main.scala:75)

at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)ALL OK

at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:748)
  • Why?
  • Is there something I can change in my program to fix this behavior?
  • If this is an issue with scala, is it fixed in any future releases?

回答1:


  • Why? Turns out to be a known thread-safety bug in scala reflection itself https://github.com/scala/bug/issues/10766
  • Is there something I can change in my program to fix this behavior? After trying out a couple things, it works to wrap each <:< call in a synchronized block. Still interested in knowing if anyone has a more elegant way to do this.
  • If this is an issue with scala, is it fixed in any future releases? No, it is present in all current versions of scala (through 2.13.0-M5) (see above scala issue).


来源:https://stackoverflow.com/questions/55150590/thread-safety-in-scala-reflection-with-type-matching

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!