# HG changeset patch # User alanb # Date 1382617472 -3600 # Node ID ad2735d414969f8698e97c69d82d593a6101d548 # Parent c53a16ac1969911725f8e29120085187207376f2 8026344: j.u.c.a *Adder and *Accumulator extend a package private class that is Serializable Reviewed-by: rriggs, psandoz, chegar Contributed-by: dl@cs.oswego.edu, alan.bateman@oracle.com diff -r c53a16ac1969 -r ad2735d41496 jdk/src/share/classes/java/util/concurrent/atomic/DoubleAccumulator.java --- a/jdk/src/share/classes/java/util/concurrent/atomic/DoubleAccumulator.java Tue Oct 15 12:53:54 2013 +0200 +++ b/jdk/src/share/classes/java/util/concurrent/atomic/DoubleAccumulator.java Thu Oct 24 13:24:32 2013 +0100 @@ -224,18 +224,71 @@ return (float)get(); } - private void writeObject(java.io.ObjectOutputStream s) - throws java.io.IOException { - s.defaultWriteObject(); - s.writeDouble(get()); + /** + * Serialization proxy, used to avoid reference to the non-public + * Striped64 superclass in serialized forms. + * @serial include + */ + private static class SerializationProxy implements Serializable { + private static final long serialVersionUID = 7249069246863182397L; + + /** + * The current value returned by get(). + * @serial + */ + private final double value; + /** + * The function used for updates. + * @serial + */ + private final DoubleBinaryOperator function; + /** + * The identity value + * @serial + */ + private final long identity; + + SerializationProxy(DoubleAccumulator a) { + function = a.function; + identity = a.identity; + value = a.get(); + } + + /** + * Returns a {@code DoubleAccumulator} object with initial state + * held by this proxy. + * + * @return a {@code DoubleAccumulator} object with initial state + * held by this proxy. + */ + private Object readResolve() { + double d = Double.longBitsToDouble(identity); + DoubleAccumulator a = new DoubleAccumulator(function, d); + a.base = Double.doubleToRawLongBits(value); + return a; + } } + /** + * Returns a + * + * SerializationProxy + * representing the state of this instance. + * + * @return a {@link SerializationProxy} + * representing the state of this instance + */ + private Object writeReplace() { + return new SerializationProxy(this); + } + + /** + * @param s the stream + * @throws java.io.InvalidObjectException always + */ private void readObject(java.io.ObjectInputStream s) - throws java.io.IOException, ClassNotFoundException { - s.defaultReadObject(); - cellsBusy = 0; - cells = null; - base = Double.doubleToRawLongBits(s.readDouble()); + throws java.io.InvalidObjectException { + throw new java.io.InvalidObjectException("Proxy required"); } } diff -r c53a16ac1969 -r ad2735d41496 jdk/src/share/classes/java/util/concurrent/atomic/DoubleAdder.java --- a/jdk/src/share/classes/java/util/concurrent/atomic/DoubleAdder.java Tue Oct 15 12:53:54 2013 +0200 +++ b/jdk/src/share/classes/java/util/concurrent/atomic/DoubleAdder.java Thu Oct 24 13:24:32 2013 +0100 @@ -210,18 +210,58 @@ return (float)sum(); } - private void writeObject(java.io.ObjectOutputStream s) - throws java.io.IOException { - s.defaultWriteObject(); - s.writeDouble(sum()); + /** + * Serialization proxy, used to avoid reference to the non-public + * Striped64 superclass in serialized forms. + * @serial include + */ + private static class SerializationProxy implements Serializable { + private static final long serialVersionUID = 7249069246863182397L; + + /** + * The current value returned by sum(). + * @serial + */ + private final double value; + + SerializationProxy(DoubleAdder a) { + value = a.sum(); + } + + /** + * Returns a {@code DoubleAdder} object with initial state + * held by this proxy. + * + * @return a {@code DoubleAdder} object with initial state + * held by this proxy. + */ + private Object readResolve() { + DoubleAdder a = new DoubleAdder(); + a.base = Double.doubleToRawLongBits(value); + return a; + } } + /** + * Returns a + * + * SerializationProxy + * representing the state of this instance. + * + * @return a {@link SerializationProxy} + * representing the state of this instance + */ + private Object writeReplace() { + return new SerializationProxy(this); + } + + /** + * @param s the stream + * @throws java.io.InvalidObjectException always + */ private void readObject(java.io.ObjectInputStream s) - throws java.io.IOException, ClassNotFoundException { - s.defaultReadObject(); - cellsBusy = 0; - cells = null; - base = Double.doubleToRawLongBits(s.readDouble()); + throws java.io.InvalidObjectException { + throw new java.io.InvalidObjectException("Proxy required"); } } diff -r c53a16ac1969 -r ad2735d41496 jdk/src/share/classes/java/util/concurrent/atomic/LongAccumulator.java --- a/jdk/src/share/classes/java/util/concurrent/atomic/LongAccumulator.java Tue Oct 15 12:53:54 2013 +0200 +++ b/jdk/src/share/classes/java/util/concurrent/atomic/LongAccumulator.java Thu Oct 24 13:24:32 2013 +0100 @@ -221,18 +221,70 @@ return (double)get(); } - private void writeObject(java.io.ObjectOutputStream s) - throws java.io.IOException { - s.defaultWriteObject(); - s.writeLong(get()); + /** + * Serialization proxy, used to avoid reference to the non-public + * Striped64 superclass in serialized forms. + * @serial include + */ + private static class SerializationProxy implements Serializable { + private static final long serialVersionUID = 7249069246863182397L; + + /** + * The current value returned by get(). + * @serial + */ + private final long value; + /** + * The function used for updates. + * @serial + */ + private final LongBinaryOperator function; + /** + * The identity value + * @serial + */ + private final long identity; + + SerializationProxy(LongAccumulator a) { + function = a.function; + identity = a.identity; + value = a.get(); + } + + /** + * Returns a {@code LongAccumulator} object with initial state + * held by this proxy. + * + * @return a {@code LongAccumulator} object with initial state + * held by this proxy. + */ + private Object readResolve() { + LongAccumulator a = new LongAccumulator(function, identity); + a.base = value; + return a; + } } + /** + * Returns a + * + * SerializationProxy + * representing the state of this instance. + * + * @return a {@link SerializationProxy} + * representing the state of this instance + */ + private Object writeReplace() { + return new SerializationProxy(this); + } + + /** + * @param s the stream + * @throws java.io.InvalidObjectException always + */ private void readObject(java.io.ObjectInputStream s) - throws java.io.IOException, ClassNotFoundException { - s.defaultReadObject(); - cellsBusy = 0; - cells = null; - base = s.readLong(); + throws java.io.InvalidObjectException { + throw new java.io.InvalidObjectException("Proxy required"); } } diff -r c53a16ac1969 -r ad2735d41496 jdk/src/share/classes/java/util/concurrent/atomic/LongAdder.java --- a/jdk/src/share/classes/java/util/concurrent/atomic/LongAdder.java Tue Oct 15 12:53:54 2013 +0200 +++ b/jdk/src/share/classes/java/util/concurrent/atomic/LongAdder.java Thu Oct 24 13:24:32 2013 +0100 @@ -211,18 +211,58 @@ return (double)sum(); } - private void writeObject(java.io.ObjectOutputStream s) - throws java.io.IOException { - s.defaultWriteObject(); - s.writeLong(sum()); + /** + * Serialization proxy, used to avoid reference to the non-public + * Striped64 superclass in serialized forms. + * @serial include + */ + private static class SerializationProxy implements Serializable { + private static final long serialVersionUID = 7249069246863182397L; + + /** + * The current value returned by sum(). + * @serial + */ + private final long value; + + SerializationProxy(LongAdder a) { + value = a.sum(); + } + + /** + * Return a {@code LongAdder} object with initial state + * held by this proxy. + * + * @return a {@code LongAdder} object with initial state + * held by this proxy. + */ + private Object readResolve() { + LongAdder a = new LongAdder(); + a.base = value; + return a; + } } + /** + * Returns a + * + * SerializationProxy + * representing the state of this instance. + * + * @return a {@link SerializationProxy} + * representing the state of this instance + */ + private Object writeReplace() { + return new SerializationProxy(this); + } + + /** + * @param s the stream + * @throws java.io.InvalidObjectException always + */ private void readObject(java.io.ObjectInputStream s) - throws java.io.IOException, ClassNotFoundException { - s.defaultReadObject(); - cellsBusy = 0; - cells = null; - base = s.readLong(); + throws java.io.InvalidObjectException { + throw new java.io.InvalidObjectException("Proxy required"); } } diff -r c53a16ac1969 -r ad2735d41496 jdk/test/java/util/concurrent/atomic/Serial.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/util/concurrent/atomic/Serial.java Thu Oct 24 13:24:32 2013 +0100 @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8026344 + * @summary Exercise classes in j.u.c.atomic that use serialization proxies + */ + +import java.util.concurrent.atomic.DoubleAdder; +import java.util.concurrent.atomic.DoubleAccumulator; +import java.util.concurrent.atomic.LongAdder; +import java.util.concurrent.atomic.LongAccumulator; +import java.util.function.DoubleBinaryOperator; +import java.util.function.LongBinaryOperator; +import java.io.ByteArrayOutputStream; +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.Serializable; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.IOException; + +/** + * Basic test to exercise the j.u.c.atomic classes that use serialization + * proxies. + */ + +public class Serial { + + public static void main(String[] args) { + testDoubleAdder(); + testDoubleAccumulator(); + testLongAdder(); + testLongAccumulator(); + } + + static void testDoubleAdder() { + DoubleAdder a = new DoubleAdder(); + a.add(20.1d); + DoubleAdder result = echo(a); + if (result.doubleValue() != a.doubleValue()) + throw new RuntimeException("Unexpected doubleValue"); + + checkSerialClassName(a, "java.util.concurrent.atomic.DoubleAdder$SerializationProxy"); + } + + static void testDoubleAccumulator() { + DoubleBinaryOperator plus = (DoubleBinaryOperator & Serializable) (x, y) -> x + y; + DoubleAccumulator a = new DoubleAccumulator(plus, 13.9d); + a.accumulate(17.5d); + DoubleAccumulator result = echo(a); + if (result.get() != a.get()) + throw new RuntimeException("Unexpected value"); + a.reset(); + result.reset(); + if (result.get() != a.get()) + throw new RuntimeException("Unexpected value after reset"); + + checkSerialClassName(a, "java.util.concurrent.atomic.DoubleAccumulator$SerializationProxy"); + } + + static void testLongAdder() { + LongAdder a = new LongAdder(); + a.add(45); + LongAdder result = echo(a); + if (result.longValue() != a.longValue()) + throw new RuntimeException("Unexpected longValue"); + + checkSerialClassName(a, "java.util.concurrent.atomic.LongAdder$SerializationProxy"); + } + + static void testLongAccumulator() { + LongBinaryOperator plus = (LongBinaryOperator & Serializable) (x, y) -> x + y; + LongAccumulator a = new LongAccumulator(plus, -2); + a.accumulate(34); + LongAccumulator result = echo(a); + if (result.get() != a.get()) + throw new RuntimeException("Unexpected value"); + a.reset(); + result.reset(); + if (result.get() != a.get()) + throw new RuntimeException("Unexpected value after reset"); + + checkSerialClassName(a, "java.util.concurrent.atomic.LongAccumulator$SerializationProxy"); + } + + /** + * Serialize the given object, returning the reconstituted object. + */ + @SuppressWarnings("unchecked") + static T echo(T obj) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try (ObjectOutputStream oos = new ObjectOutputStream(out)) { + oos.writeObject(obj); + } catch (IOException e) { + throw new RuntimeException("Serialization failed: " + e); + } + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + try (ObjectInputStream ois = new ObjectInputStream(in)) { + return (T) ois.readObject(); + } catch (IOException | ClassNotFoundException e) { + throw new RuntimeException("Deserialization failed: " + e); + } + } + + /** + * Checks that the given object serializes to the expected class. + */ + static void checkSerialClassName(Serializable obj, String expected) { + String cn = serialClassName(obj); + if (!cn.equals(expected)) + throw new RuntimeException(obj.getClass() + " serialized as " + cn + + ", expected " + expected); + } + + /** + * Returns the class name that the given object serializes as. + */ + static String serialClassName(Serializable obj) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try (ObjectOutputStream oos = new ObjectOutputStream(out)) { + oos.writeObject(obj); + } catch (IOException e) { + throw new RuntimeException("Serialization failed: " + e); + } + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + try (DataInputStream dis = new DataInputStream(in)) { + dis.readShort(); // STREAM_MAGIC + dis.readShort(); // STREAM_VERSION + dis.readByte(); // TC_OBJECT + dis.readByte(); // TC_CLASSDESC + return dis.readUTF(); // className + } catch (IOException e) { + throw new RuntimeException(e); + } + } +}