This doc page is specific to features shipped in Scala 2, which have either been removed in Scala 3 or replaced by an alternative. Unless otherwise stated, all the code examples in this page assume you are using Scala 2.
EXPERIMENTAL
Eugene Burmako
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.
NEW Thread safety issues have been fixed in Scala 2.11.0-RC1, but we are going to keep this document available for now, since the problem still remains in the Scala 2.10.x series, and we currently don't have concrete plans on when the fix is going to be backported.
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.
Bottom line:
- If you’re writing a macro, which doesn’t explicitly create threads, you’re perfectly fine.
- Runtime reflection mixed with threads or actors might be dangerous.
- Multiple threads calling methods with
TypeTag
context bounds might lead to non-deterministic results. - Check out SI-6240 to see our progress with this issue.