Skip to content

Commit

Permalink
Merge commit '61701c2290' into merge/2.12.x-to-2.13.x-20190909
Browse files Browse the repository at this point in the history
  • Loading branch information
retronym committed Sep 9, 2019
2 parents cd6c858 + 61701c2 commit 6cc54ee
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 3 deletions.
5 changes: 2 additions & 3 deletions src/reflect/scala/reflect/internal/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -110,12 +110,11 @@ trait Types
private object substTypeMapCache {
private[this] var cached: SubstTypeMap = new SubstTypeMap(Nil, Nil)

def apply(from: List[Symbol], to: List[Type]): SubstTypeMap = {
def apply(from: List[Symbol], to: List[Type]): SubstTypeMap = if (isCompilerUniverse) {
if ((cached.from ne from) || (cached.to ne to))
cached = new SubstTypeMap(from, to)

cached
}
} else new SubstTypeMap(from, to)
}

/** The current skolemization level, needed for the algorithms
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ private[reflect] trait SynchronizedSymbols extends internal.Symbols { self: Symb
override def exists: Boolean = gilSynchronizedIfNotThreadsafe(super.exists)
override def typeSignature: Type = gilSynchronizedIfNotThreadsafe { super.typeSignature }
override def typeSignatureIn(site: Type): Type = gilSynchronizedIfNotThreadsafe { super.typeSignatureIn(site) }
override def typeConstructor: Type = gilSynchronizedIfNotThreadsafe { super.typeConstructor }

override def typeParams: List[Symbol] = gilSynchronizedIfNotThreadsafe {
if (isCompilerUniverse) super.typeParams
Expand Down
103 changes: 103 additions & 0 deletions test/junit/scala/reflect/runtime/ThreadSafetyTest.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
* Scala (https://www.scala-lang.org)
*
* Copyright EPFL and Lightbend, Inc.
*
* Licensed under Apache License 2.0
* (http://www.apache.org/licenses/LICENSE-2.0).
*
* See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*/

package scala.reflect.runtime

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

import org.junit.Test

class ThreadSafetyTest {
import scala.reflect.runtime.universe._
sealed abstract class Instance(tp: Type)
final case class ListInstance(tp: Type, elemInstance: Instance) extends Instance(appliedType(symbolOf[List[_]], tp :: Nil))

final case class DoubleInstance() extends Instance(typeOf[Double])

final case class StringInstance() extends Instance(typeOf[String])

final case class LowPriorityInstance(tpe: Type) extends Instance(tpe)

def classKeyOf[T: TypeTag]: String = {
classKeyOf(typeOf[T])
}
def classKeyOf(tpe: Type): String = {
tpe.typeSymbol.fullName
}

class Lazy[T](thunk: () => T) {
lazy val force: T = thunk()
}

class Registry {
private val cache = new java.util.concurrent.ConcurrentHashMap[Type, Lazy[Instance]]()
def instance[T: TypeTag]: Lazy[Instance] = {
instance(typeOf[T])
}
def instance(tpe: Type): Lazy[Instance] = {
val value = keyOf(tpe)
assert(value =:= tpe, (value, tpe))
cache.computeIfAbsent(value, create(_))
}

private def create(tpe: Type): Lazy[Instance] = {
val key = classKeyOf(tpe)
if (key == "scala.collection.immutable.List") {
new Lazy(() => {val elemTpe = tpe.dealias.typeArgs.head; ListInstance(tpe.dealias, instance(elemTpe).force)})
} else if (key == "scala.Double") {
new Lazy(() => DoubleInstance())
} else if (key == "java.lang.String") {
new Lazy(() => StringInstance())
} else {
new Lazy(() => LowPriorityInstance(tpe))
}
}
private def keyOf(tp: Type): universe.Type = {
tp.map(_.dealias)
}
}

@Test
def test(): Unit = {
val executor = Executors.newFixedThreadPool(16)
for (i <- (0 to 128)) {
val registry = new Registry
val is = List(
(() => typeOf[List[Double]], "ListInstance(List[Double],DoubleInstance())"),
(() => typeOf[List[String]], "ListInstance(List[String],StringInstance())"),
(() => typeOf[List[List[Double]]], "ListInstance(List[List[Double]],ListInstance(List[Double],DoubleInstance()))"),
(() => typeOf[List[List[List[Double]]]], "ListInstance(List[List[List[Double]]],ListInstance(List[List[Double]],ListInstance(List[Double],DoubleInstance())))"),
(() => typeOf[List[List[List[Object]]]], "ListInstance(List[List[List[Object]]],ListInstance(List[List[Object]],ListInstance(List[Object],LowPriorityInstance(Object))))")
)
sealed abstract class Result
case object Okay extends Result
case class Failed(i: Int, tp: Type, expected: String, instance: String) extends Result
def check(i: Int): Result = {
val (f, expected) = is(i % is.size)
val tp = f()
val instance = registry.instance(tp).force
if (instance.toString == expected) Okay else Failed(i, tp, expected, "[" + instance + "]")
}
val par = true
val fs: List[Result] = if (par) Array.tabulate(32)(i =>
executor.submit(new Callable[Result] {
override def call(): Result = {
check(i % is.size)
}
})).map(_.get).toList
else is.indices.map(check).toList
val fails = fs.filter(_ != Okay)
assert(fails.isEmpty, "iteration " + i + ": " + fails.mkString("\n"))
}
executor.shutdownNow()
}
}

0 comments on commit 6cc54ee

Please sign in to comment.