Unfortunately, in its current state released in Scala 2.10.0, reflection is not thread safe. There’s a JIRA issue SI-6240, which can be used to track our progress and to look up technical details, and here’s a concise summary of the state of the art.
Currently we know about two kinds of races associated with reflection. First of all, reflection initialization (the code that is called when
scala.reflect.runtime.universe is accessed for the first time) cannot be safely called from multiple threads. Secondly, symbol initialization (the code that is called when symbol’s flags or type signature are accessed for the first time) isn’t safe as well. Here’s a typical manifestation:
java.lang.NullPointerException: at s.r.i.Types$TypeRef.computeHashCode(Types.scala:2332) at s.r.i.Types$UniqueType.<init>(Types.scala:1274) at s.r.i.Types$TypeRef.<init>(Types.scala:2315) at s.r.i.Types$NoArgsTypeRef.<init>(Types.scala:2107) at s.r.i.Types$ModuleTypeRef.<init>(Types.scala:2078) at s.r.i.Types$PackageTypeRef.<init>(Types.scala:2095) at s.r.i.Types$TypeRef$.apply(Types.scala:2516) at s.r.i.Types$class.typeRef(Types.scala:3577) at s.r.i.SymbolTable.typeRef(SymbolTable.scala:13) at s.r.i.Symbols$TypeSymbol.newTypeRef(Symbols.scala:2754)
Good news is that compile-time reflection (the one exposed to macros via
scala.reflect.macros.Context) is much less susceptible to threading problems than runtime reflection (the one exposed via
scala.reflect.runtime.universe). The first reason is that by the time macros get chance to run, compile-time reflective universe are already initialized, which rules our the race condition #1. The second reason is that the compiler has never been thread-safe, so there are no tools, which expect is to run in parallel. Nevertheless, if your macro spawns multiple threads you should still be careful.
It’s much worse for runtime reflection though. Reflection init is called the first time when
scala.reflect.runtime.universe is initialized, and this initialization can happen in an indirect fashion. The most prominent example here is that calling methods with
TypeTag context bounds is potentially problematic, because to call such a method Scala typically needs to construct an autogenerated type tag, which needs to create a type, which needs to initialize the reflective universe. A corollary is that if you don’t take special measures, you can’t call reliably use
TypeTag-based methods in tests, because a lot of tools, e.g. SBT, run tests in parallel.
TypeTagcontext bounds might lead to non-deterministic results.