reflection

Thread Safety

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.

blog comments powered by Disqus