8

As I read, Scala immutable val doesn't get translated to Java final for various reasons. Does this mean that accessing a val from an other Thread must be guarded with synchronization in order to guarantee visibility?

ron
  • 9,262
  • 4
  • 40
  • 73

2 Answers2

12

the assignment to val itself is fine from a multi-threading point of view, because you have to assign val a value when you declare it and that value can't be changed in the future (so if you do a val s="hello", s is "hello" from its birth on: no thread will ever read another value). There are a couple of caveats, however:

1 - if you assign an instance of a mutable class to val, val by itself will not "protect" the internal state of the class from changing.

class Foo(s:String) { var thisIsMutable=s }
// you can then do this
val x = new Foo("hello")
x.thisIsMutable="goodbye"
// note that val guarantees that x is still the same instance of Foo
// reassigning x = new Foo("goodbye") would be illegal

2 - you (or one of your libraries...) can change a val via reflection. If this happens two threads could indeed read a different value for your val

import java.lang.reflect.Field
class Foo { val foo=true } // foo is immutable

object test {
  def main(args: Array[String]) {  
        val f = new Foo
        println("foo is " + f.foo) // "foo is true"

        val fld = f.getClass.getDeclaredField("foo")
        fld.setAccessible(true) 
        fld.setBoolean(f, false) 
        println("foo is " + f.foo) // "foo is false"
  }
}
Paolo Falabella
  • 24,914
  • 3
  • 72
  • 86
  • In the JVM, the fact that something *cannot be changed in the future* does not guarantee that every thread will see the same value. Consider `class x{private int A;x(int i){A=i;}int a(){return A;}}`: if you publish `new x(3);` without synchronization, a thread might see `.a() == 0`, and this class adheres to your condition (the value can't be changed in the future, only by using reflection). To protected from this, you **must** make the field *final*. And Scala doesn't give you this guarantee. – Bruno Reis Aug 20 '11 at 14:08
  • @Bruno Reis I'm not sure I understand. Your class in scala would be just `class x(val a: Int)`, where the a gets compiled to the equivalent of java's `private final int a; public final int a() { return this.a; }`. – Paolo Falabella Aug 21 '11 at 09:53
  • @paolo: the problem is that the Scala specification *does not guarantee* that the compiler will add the `final` modifier to the field. – Bruno Reis Aug 21 '11 at 14:42
  • @Bruno Reis I'll take your word for it (until I have the time to research some more :) ). Thanks for bringing this up, I was taking it for granted. – Paolo Falabella Aug 21 '11 at 19:00
9

As object members, once initialized, vals never change their values during the lifetime of the object. As such, their values are guaranteed to be visible to all threads provided that the reference to the object didn't escape in the constructor. And, in fact, they get Java final modifiers as illustrated below:

object Obj {
  val r = 1

  def foo {
    val a = 1
    def bar = a
    bar
  }
}

Using javap:

...
private final int r;
...

public void foo();
...
   0:   iconst_1
   1:   istore_1
   2:   aload_0
   3:   iload_1
   4:   invokespecial   #31; //Method bar$1:(I)I
   7:   pop
...
private final int bar$1(int);
...
   0:   iload_1
   1:   ireturn
...

As method locals, they are used only within the method, or they're being passed to a nested method or a closure as arguments (see lifted bar$1 above). A closure might be passed on to another thread, but it will only have a final field with the value of the local val. Therefore, they are visible from the point where they are created to all other threads and synchronization is not necessary.

Note that this says nothing about the object the val points to - it itself may be mutable and warrant synchronization.

In most cases the above cannot be violated via reflection - the Scala val member declaration actually generates a getter with the same name and a private field which the getter accesses. Trying to use reflection to modify the field will result in the NoSuchFieldException. The only way you could modify it is to add a specialized annotation to your class which will make the specialized fields protected, hence accessible to reflection. I cannot currently think of any other situation that could change something declared as val...

axel22
  • 32,045
  • 9
  • 125
  • 137
  • Same comment as to the other answer: in the JVM, the fact that *something never changes its value during its lifetime* does not guarantee that every thread will see the same value. Consider class x{private int A;x(int i){A=i;}int a(){return A;}}: if you publish new x(3); without synchronization, a thread might see .a() == 0, and this class adheres to your condition (the value can't be changed in the future, only by using reflection). To protected from this, you **must** make the field *final*. And Scala doesn't give you this guarantee, although the compiler usually adds the final modifier. – Bruno Reis Aug 20 '11 at 14:13
  • 1
    I thought that the JVM memory model guarantees that every final field modified in the ctor is guaranteed to be visible to everybody after the reference to the object is published, as long as the reference doesn't get published in the ctor itself. Reference: http://www.ibm.com/developerworks/library/j-jtp03304/, search for "initialization safety". The `val` is initialized in the ctor, so it should be visible. To my knowledge, `val`s should always be translated to final fields. – axel22 Aug 21 '11 at 02:12
  • @axel22: the reasoning about the memory model is correct (and that is my point: vals should translated do final fields to be thread-safe). The problem is that *to your knowledge `val`s should always be translated to final fields* is not guarantee enough. If that is really the case, it should be stated on the language specification, which it is not. – Bruno Reis Aug 21 '11 at 14:45
  • So, there are some possibilities: 1) You simply write `case class x(a: Int)` and hope for it to be compiled with a `final` field, and if it is not, damn it, let concurrency bugs go wild; 2) You decompile and analyse the bytecode generated for the class `x` just after every single compilation, to be sure that it has still been compiled with a final field; 3) You add lots of synchronization, as if the field is not final, just in case; 4) You hope that the specification will be more precise about this theme, and give you enough guarantees so that it will be easy and safe (as does Java's final kw) – Bruno Reis Aug 21 '11 at 14:58
  • You have a valid point. Do you have an example where it is not compiled to a `final`? If it is just the matter of adding it to the spec, you should file a bug report, so that the specification is updated. – axel22 Aug 21 '11 at 22:08