--- old/src/java.base/share/classes/java/io/ObjectInputStream.java 2015-02-24 10:47:57.000000000 +0000 +++ new/src/java.base/share/classes/java/io/ObjectInputStream.java 2015-02-24 10:47:57.000000000 +0000 @@ -40,6 +40,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import static java.io.ObjectStreamClass.processQueue; +import sun.misc.Unsafe; import sun.reflect.misc.ReflectUtil; /** @@ -268,6 +269,16 @@ private SerialCallbackContext curContext; /** + * Tracks whether deserialization of an object in the graph may have set + * a final field, and therefore requires a freeze action before returning + * the graph of objects from the topmost readObject call. + * Nested calls to readObject do not issue freeze actions because the + * sub-graph returned from a nested call is not guaranteed to be fully + * initialized yet (possible cycles). + */ + private boolean requiresFreeze; + + /** * Creates an ObjectInputStream that reads from the specified InputStream. * A serialization stream header is read from the stream and verified. * This constructor will block until the corresponding ObjectOutputStream @@ -375,6 +386,9 @@ } if (depth == 0) { vlist.doCallbacks(); + if (requiresFreeze) { + freeze(); + } } return obj; } finally { @@ -465,6 +479,9 @@ } if (depth == 0) { vlist.doCallbacks(); + if (requiresFreeze) { + freeze(); + } } return obj; } finally { @@ -1878,8 +1895,12 @@ throws IOException { ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout(); + boolean freeze = false; for (int i = 0; i < slots.length; i++) { ObjectStreamClass slotDesc = slots[i].desc; + if (!freeze && slotDesc.hasFinalField()) { + freeze = true; + } if (slots[i].hasData) { if (obj != null && @@ -1930,6 +1951,7 @@ } } } + requiresFreeze = freeze; } /** @@ -2357,6 +2379,15 @@ } } + private static final Unsafe UNSAFE = Unsafe.getUnsafe(); + + private void freeze() { + // Issue a storestore|storeload fence, which is at least sufficient + // to provide final-freeze semantics. + UNSAFE.storeFence(); + requiresFreeze = false; + } + /** * Input stream with two modes: in default mode, inputs data written in the * same format as DataOutputStream; in "block data" mode, inputs data