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
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
TypeTag-based methods in tests, because a lot of tools, e.g. sbt, run tests in parallel.
- 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
TypeTagcontext bounds might lead to non-deterministic results.
- Check out SI-6240 to see our progress with this issue.