river-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Peter Firmstone <j...@zeus.net.au>
Subject Final fields - discussion
Date Sat, 25 Jan 2014 05:21:32 GMT
Rather than discuss specific instances where I've made changes to ensure 
an object reference doesn't escape during construction, I figure it 
would be more constructive to discuss final fields themselves.

Some of the arguments against using Startable were based on timing when 
references to partially constructed objects would be accessed, this is 
incorrect.  REF: JLS 17.5.1 Semantics of final fields, final paragraph 
"For reads of |final| fields, the only writes that are deemed to come 
before the read of the |final| field are the ones derived through the 
|final| field semantics."  The problem was that the execution path from 
our service instances to the location where other threads read its 
reference, did not pass through a final field freeze at the end of a 
constructor; the compiler is under no obligation to reload these final 
fields.

The argument that ensued over Startable could have been avoided if 
someone simply proposed they preferred to use a public constructor that 
publishes and exports, after calling a private constructor that sets 
final fields, that would have also been correct.  REF: JLS 17.5.1 
Semantics of final fields, paragraph 2.

Does anyone here on the list still think it's acceptable to allow the 
reference of an object that has final fields to escape from the 
constructor that sets those final fields as our services in River 2.2 
and all earlier releases of Jini do?

Welcome to theoretical development.

The relevant section of the Java 7 Language Specification:


    17.5. |final| Field Semantics

Fields declared final are initialized once, but never changed under 
normal circumstances. The detailed semantics of |final| fields are 
somewhat different from those of normal fields. In particular, compilers 
have a great deal of freedom to move reads of |final| fields across 
synchronization barriers and calls to arbitrary or unknown methods. 
Correspondingly, compilers are allowed to keep the value of a |final| 
field cached in a register and not reload it from memory in situations 
where a non-|final| field would have to be reloaded.

|final| fields also allow programmers to implement thread-safe immutable 
objects without synchronization. A thread-safe immutable object is seen 
as immutable by all threads, even if a data race is used to pass 
references to the immutable object between threads. This can provide 
safety guarantees against misuse of an immutable class by incorrect or 
malicious code. |final| fields must be used correctly to provide a 
guarantee of immutability.

An object is considered to be /completely initialized/ when its 
constructor finishes. A thread that can only see a reference to an 
object after that object has been completely initialized is guaranteed 
to see the correctly initialized values for that object's |final| fields.

The usage model for |final| fields is a simple one: Set the |final| 
fields for an object in that object's constructor; and do not write a 
reference to the object being constructed in a place where another 
thread can see it before the object's constructor is finished. If this 
is followed, then when the object is seen by another thread, that thread 
will always see the correctly constructed version of that object's 
|final| fields. It will also see versions of any object or array 
referenced by those |final| fields that are at least as up-to-date as 
the |final| fields are.

*Example 17.5-1. |final| Fields In The Java Memory Model*

The program below illustrates how |final| fields compare to normal fields.

class FinalFieldExample {
     final int x;
     int y;
     static FinalFieldExample f;

     public FinalFieldExample() {
         x = 3;
         y = 4;
     }

     static void writer() {
         f = new FinalFieldExample();
     }

     static void reader() {
         if (f != null) {
             int i = f.x;  // guaranteed to see 3
             int j = f.y;  // could see 0
         }
     }
}

The class |FinalFieldExample| has a |final| |int| field |x| and a 
non-|final| |int| field |y|. One thread might execute the method 
|writer| and another might execute the method |reader|.

Because the |writer| method writes |f| /after/ the object's constructor 
finishes, the |reader| method will be guaranteed to see the properly 
initialized value for |f.x|: it will read the value |3|. However, |f.y| 
is not |final|; the |reader| method is therefore not guaranteed to see 
the value |4| for it.


*Example 17.5-2. |final| Fields For Security*

|final| fields are designed to allow for necessary security guarantees. 
Consider the following program. One thread (which we shall refer to as 
thread 1) executes:

Global.s = "/tmp/usr".substring(4);

while another thread (thread 2) executes

String myS = Global.s;
if (myS.equals("/tmp"))System.out.println(myS);

|String| objects are intended to be immutable and string operations do 
not perform synchronization. While the |String| implementation does not 
have any data races, other code could have data races involving the use 
of |String| objects, and the memory model makes weak guarantees for 
programs that have data races. In particular, if the fields of the 
|String| class were not |final|, then it would be possible (although 
unlikely) that thread 2 could initially see the default value of |0| for 
the offset of the string object, allowing it to compare as equal to 
"|/tmp|". A later operation on the |String| object might see the correct 
offset of |4|, so that the |String| object is perceived as being 
"|/usr|". Many security features of the Java programming language depend 
upon |String| objects being perceived as truly immutable, even if 
malicious code is using data races to pass |String| references between 
threads.


      17.5.1. Semantics of |final| Fields

Let /o/ be an object, and /c/ be a constructor for /o/ in which a 
|final| field /f/ is written. A /freeze/ action on |final| field /f/ of 
/o/ takes place when /c/ exits, either normally or abruptly.

Note that if one constructor invokes another constructor, and the 
invoked constructor sets a |final| field, the freeze for the |final| 
field takes place at the end of the invoked constructor.

For each execution, the behavior of reads is influenced by two 
additional partial orders, the dereference chain /dereferences()/ and 
the memory chain /mc()/, which are considered to be part of the 
execution (and thus, fixed for any particular execution). These partial 
orders must satisfy the following constraints (which need not have a 
unique solution):

    *

      Dereference Chain: If an action /a/ is a read or write of a field
      or element of an object /o/ by a thread /t/ that did not
      initialize /o/, then there must exist some read /r/ by thread /t/
      that sees the address of /o/ such that /r/ /dereferences(r, a)/.

    *

      Memory Chain: There are several constraints on the memory chain
      ordering:

          o

            If /r/ is a read that sees a write /w/, then it must be the
            case that /mc(w, r)/.

          o

            If /r/ and /a/ are actions such that /dereferences(r, a)/,
            then it must be the case that /mc(r, a)/.

          o

            If /w/ is a write of the address of an object /o/ by a
            thread /t/ that did not initialize /o/, then there must
            exist some read /r/ by thread /t/ that sees the address of
            /o/ such that /mc(r, w)/.

Given a write /w/, a freeze /f/, an action /a/ (that is not a read of a 
|final| field), a read /r_1 / of the |final| field frozen by /f/, and a 
read /r_2 / such that /hb(w, f)/, /hb(f, a)/, /mc(a, r_1 )/, and 
/dereferences(r_1 , r_2 )/, then when determining which values can be 
seen by /r_2 /, we consider /hb(w, r_2 )/. (This /happens-before/ 
ordering does not transitively close with other /happens-before/ 
orderings.)

Note that the /dereferences/ order is reflexive, and /r_1 / can be the 
same as /r_2 /.

For reads of |final| fields, the only writes that are deemed to come 
before the read of the |final| field are the ones derived through the 
|final| field semantics.


      17.5.2. Reading |final| Fields During Construction

A read of a |final| field of an object within the thread that constructs 
that object is ordered with respect to the initialization of that field 
within the constructor by the usual /happens-before/ rules. If the read 
occurs after the field is set in the constructor, it sees the value the 
|final| field is assigned, otherwise it sees the default value.


      17.5.3. Subsequent Modification of |final| Fields

In some cases, such as deserialization, the system will need to change 
the |final| fields of an object after construction. |final| fields can 
be changed via reflection and other implementation-dependent means. The 
only pattern in which this has reasonable semantics is one in which an 
object is constructed and then the |final| fields of the object are 
updated. The object should not be made visible to other threads, nor 
should the |final| fields be read, until all updates to the |final| 
fields of the object are complete. Freezes of a |final| field occur both 
at the end of the constructor in which the |final| field is set, and 
immediately after each modification of a |final| field via reflection or 
other special mechanism.

Even then, there are a number of complications. If a |final| field is 
initialized to a compile-time constant expression (ยง15.28 
<http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.28>) in 
the field declaration, changes to the |final| field may not be observed, 
since uses of that |final| field are replaced at compile time with the 
value of the constant expression.

Another problem is that the specification allows aggressive optimization 
of |final| fields. Within a thread, it is permissible to reorder reads 
of a |final| field with those modifications of a |final| field that do 
not take place in the constructor.

*Example 17.5.3-1. Aggressive Optimization of |final| Fields*

class A {
     final int x;
     A() {
         x = 1;
     }

     int f() {
         return d(this,this);
     }

     int d(A a1, A a2) {
         int i = a1.x;
         g(a1);
         int j = a2.x;
         return j - i;
     }

     static void g(A a) {
         // uses reflection to change a.x to 2
     }
}

In the |d| method, the compiler is allowed to reorder the reads of |x| 
and the call to |g| freely. Thus, |new A().f()| could return |-1|, |0|, 
or |1|.


An implementation may provide a way to execute a block of code in a 
/|final|-field-safe context/. If an object is constructed within a 
|final|-field-safe context, the reads of a |final| field of that object 
will not be reordered with modifications of that |final| field that 
occur within that |final|-field-safe context.

A |final|-field-safe context has additional protections. If a thread has 
seen an incorrectly published reference to an object that allows the 
thread to see the default value of a |final| field, and then, within a 
|final|-field-safe context, reads a properly published reference to the 
object, it will be guaranteed to see the correct value of the |final| 
field. In the formalism, code executed within a |final|-field-safe 
context is treated as a separate thread (for the purposes of |final| 
field semantics only).

In an implementation, a compiler should not move an access to a |final| 
field into or out of a |final|-field-safe context (although it can be 
moved around the execution of such a context, so long as the object is 
not constructed within that context).

One place where use of a |final|-field-safe context would be appropriate 
is in an executor or thread pool. By executing each |Runnable| in a 
separate |final|-field-safe context, the executor could guarantee that 
incorrect access by one |Runnable| to a object /o/ will not remove 
|final| field guarantees for other |Runnable|s handled by the same 
executor.


      17.5.4. Write-protected Fields

Normally, a field that is |final| and |static| may not be modified. 
However, |System.in|, |System.out|, and |System.err| are |static| 
|final| fields that, for legacy reasons, must be allowed to be changed 
by the methods |System.setIn|, |System.setOut|, and |System.setErr|. We 
refer to these fields as being /write-protected/ to distinguish them 
from ordinary |final| fields.

The compiler needs to treat these fields differently from other |final| 
fields. For example, a read of an ordinary |final| field is "immune" to 
synchronization: the barrier involved in a lock or volatile read does 
not have to affect what value is read from a |final| field. Since the 
value of write-protected fields may be seen to change, synchronization 
events should have an effect on them. Therefore, the semantics dictate 
that these fields be treated as normal fields that cannot be changed by 
user code, unless that user code is in the |System| class.


    17.6. Word Tearing

One consideration for implementations of the Java Virtual Machine is 
that every field and array element is considered distinct; updates to 
one field or element must not interact with reads or updates of any 
other field or element. In particular, two threads that update adjacent 
elements of a byte array separately must not interfere or interact and 
do not need synchronization to ensure sequential consistency.

Some processors do not provide the ability to write to a single byte. It 
would be illegal to implement byte array updates on such a processor by 
simply reading an entire word, updating the appropriate byte, and then 
writing the entire word back to memory. This problem is sometimes known 
as /word tearing/, and on processors that cannot easily update a single 
byte in isolation some other approach will be required.

*Example 17.6-1. Detection of Word Tearing*

The following program is a test case to detect word tearing:

public class WordTearing extends Thread {
     static final int LENGTH = 8;
     static final int ITERS  = 1000000;
     static byte[] counts    = new byte[LENGTH];
     static Thread[] threads = new Thread[LENGTH];

     final int id;
     WordTearing(int i) {
         id = i;
     }

     public void run() {
         byte v = 0;
         for (int i = 0; i<  ITERS; i++) {
             byte v2 = counts[id];
             if (v != v2) {
                 System.err.println("Word-Tearing found: " +
                               "counts[" + id + "] = "+ v2 +
                               ", should be " + v);
                 return;
             }
             v++;
             counts[id] = v;
         }
     }

     public static void main(String[] args) {
         for (int i = 0; i<  LENGTH; ++i)
             (threads[i] = new WordTearing(i)).start();
     }
}

This makes the point that bytes must not be overwritten by writes to 
adjacent bytes.



Mime
View raw message