# HG changeset patch # User duke # Date 1499280888 -7200 # Node ID 470068717ae29a1a4a1dd52d49b10136b424c4e9 # Parent 9d153359b8ff271e3db5e648cd3aef9f521c91db# Parent 7974c792d22f736d0f51d6189e64b5ccc30494fe Merge diff -r 9d153359b8ff -r 470068717ae2 .hgtags-top-repo --- a/.hgtags-top-repo Wed Oct 21 15:15:26 2015 -0700 +++ b/.hgtags-top-repo Wed Jul 05 20:54:48 2017 +0200 @@ -329,3 +329,4 @@ 1c8134475511ffe6726677e1418a89a7a45e92d6 jdk9-b84 1f345217c9bab05f192d00cf1665b3286c49ccdb jdk9-b85 2aa1daf98d3e2ee37f20f6858c53cc37020f6937 jdk9-b86 +fd4f4f7561074dc0dbc1772c8489c7b902b6b8a9 jdk9-b87 diff -r 9d153359b8ff -r 470068717ae2 common/autoconf/generated-configure.sh --- a/common/autoconf/generated-configure.sh Wed Oct 21 15:15:26 2015 -0700 +++ b/common/autoconf/generated-configure.sh Wed Jul 05 20:54:48 2017 +0200 @@ -4587,7 +4587,7 @@ #CUSTOM_AUTOCONF_INCLUDE # Do not change or remove the following line, it is needed for consistency checks: -DATE_WHEN_GENERATED=1444224363 +DATE_WHEN_GENERATED=1444643341 ############################################################################### # @@ -46678,10 +46678,24 @@ X_CFLAGS= X_LIBS= else - # Check if the user has specified sysroot, but not --x-includes or --x-libraries. - # Make a simple check for the libraries at the sysroot, and setup --x-includes and - # --x-libraries for the sysroot, if that seems to be correct. - if test "x$OPENJDK_TARGET_OS" = "xlinux"; then + + if test "x${with_x}" = xno; then + as_fn_error $? "It is not possible to disable the use of X11. Remove the --without-x option." "$LINENO" 5 + fi + + if test "x${with_x}" != x && test "x${with_x}" != xyes; then + # The user has specified a X11 base directory. Use it for includes and + # libraries, unless explicitely overridden. + if test "x$x_includes" = xNONE; then + x_includes="${with_x}/include" + fi + if test "x$x_libraries" = xNONE; then + x_libraries="${with_x}/lib" + fi + else + # Check if the user has specified sysroot, but not --with-x, --x-includes or --x-libraries. + # Make a simple check for the libraries at the sysroot, and setup --x-includes and + # --x-libraries for the sysroot, if that seems to be correct. if test "x$SYSROOT" != "x"; then if test "x$x_includes" = xNONE; then if test -f "$SYSROOT/usr/X11R6/include/X11/Xlib.h"; then diff -r 9d153359b8ff -r 470068717ae2 common/autoconf/lib-x11.m4 --- a/common/autoconf/lib-x11.m4 Wed Oct 21 15:15:26 2015 -0700 +++ b/common/autoconf/lib-x11.m4 Wed Jul 05 20:54:48 2017 +0200 @@ -35,10 +35,24 @@ X_CFLAGS= X_LIBS= else - # Check if the user has specified sysroot, but not --x-includes or --x-libraries. - # Make a simple check for the libraries at the sysroot, and setup --x-includes and - # --x-libraries for the sysroot, if that seems to be correct. - if test "x$OPENJDK_TARGET_OS" = "xlinux"; then + + if test "x${with_x}" = xno; then + AC_MSG_ERROR([It is not possible to disable the use of X11. Remove the --without-x option.]) + fi + + if test "x${with_x}" != x && test "x${with_x}" != xyes; then + # The user has specified a X11 base directory. Use it for includes and + # libraries, unless explicitely overridden. + if test "x$x_includes" = xNONE; then + x_includes="${with_x}/include" + fi + if test "x$x_libraries" = xNONE; then + x_libraries="${with_x}/lib" + fi + else + # Check if the user has specified sysroot, but not --with-x, --x-includes or --x-libraries. + # Make a simple check for the libraries at the sysroot, and setup --x-includes and + # --x-libraries for the sysroot, if that seems to be correct. if test "x$SYSROOT" != "x"; then if test "x$x_includes" = xNONE; then if test -f "$SYSROOT/usr/X11R6/include/X11/Xlib.h"; then diff -r 9d153359b8ff -r 470068717ae2 corba/.hgtags --- a/corba/.hgtags Wed Oct 21 15:15:26 2015 -0700 +++ b/corba/.hgtags Wed Jul 05 20:54:48 2017 +0200 @@ -329,3 +329,4 @@ df70bb200356fec686681f0295c50cc3ed43c3b3 jdk9-b84 3ec06af1368924469f7ce60a00324bac55eaeecc jdk9-b85 0a3f0d25c201b40575a7c3920fce4d6f4d3ae310 jdk9-b86 +a5c40ac9b916ff44d512ee764fa919ed2097e149 jdk9-b87 diff -r 9d153359b8ff -r 470068717ae2 hotspot/.hgtags --- a/hotspot/.hgtags Wed Oct 21 15:15:26 2015 -0700 +++ b/hotspot/.hgtags Wed Jul 05 20:54:48 2017 +0200 @@ -489,3 +489,4 @@ 184c4328444974edd6b3b490b9d0177ace7e331c jdk9-b84 03845376ea9dbf9690b6a9cfb4ed63f8cc0541c0 jdk9-b85 1ae4191359d811a51512f17dca80ffe79837a5ff jdk9-b86 +d7ffd16382fe7071181b967932b47cff6d1312e1 jdk9-b87 diff -r 9d153359b8ff -r 470068717ae2 jaxp/.hgtags --- a/jaxp/.hgtags Wed Oct 21 15:15:26 2015 -0700 +++ b/jaxp/.hgtags Wed Jul 05 20:54:48 2017 +0200 @@ -329,3 +329,4 @@ 91795d86744f3074d1e59b1e75d9c851c098688f jdk9-b84 1d9850c1b35c74e8b5c17970ed5d46dc0fc33f06 jdk9-b85 88d9b1f6b73e0c46fcb5ccabe1231a30ce758a22 jdk9-b86 +eb435c878c2cbbfb043d0b205f4d5bd6faffd44a jdk9-b87 diff -r 9d153359b8ff -r 470068717ae2 jdk/.hgtags --- a/jdk/.hgtags Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/.hgtags Wed Jul 05 20:54:48 2017 +0200 @@ -329,3 +329,4 @@ 757ef7f6d0042934edea3e0bf616fad2c1a22789 jdk9-b84 fe40b31c0e526d357cf5b62044fea006e43b53a5 jdk9-b85 e8a66c0b05d786a282a7ff1d7eb4989afa30c891 jdk9-b86 +110fc90bdfa0fe59606c047c2301ed75d2bad6cf jdk9-b87 diff -r 9d153359b8ff -r 470068717ae2 jdk/make/mapfiles/libjava/mapfile-vers --- a/jdk/make/mapfiles/libjava/mapfile-vers Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/make/mapfiles/libjava/mapfile-vers Wed Jul 05 20:54:48 2017 +0200 @@ -152,7 +152,6 @@ Java_java_lang_StrictMath_log10; Java_java_lang_StrictMath_sin; Java_java_lang_StrictMath_sqrt; - Java_java_lang_StrictMath_cbrt; Java_java_lang_StrictMath_tan; Java_java_lang_StrictMath_cosh; Java_java_lang_StrictMath_sinh; diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/lang/FdLibm.java --- a/jdk/src/java.base/share/classes/java/lang/FdLibm.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/FdLibm.java Wed Jul 05 20:54:48 2017 +0200 @@ -100,6 +100,64 @@ } /** + * cbrt(x) + * Return cube root of x + */ + public static class Cbrt { + // unsigned + private static final int B1 = 715094163; /* B1 = (682-0.03306235651)*2**20 */ + private static final int B2 = 696219795; /* B2 = (664-0.03306235651)*2**20 */ + + private static final double C = 0x1.15f15f15f15f1p-1; // 19/35 ~= 5.42857142857142815906e-01 + private static final double D = -0x1.691de2532c834p-1; // -864/1225 ~= 7.05306122448979611050e-01 + private static final double E = 0x1.6a0ea0ea0ea0fp0; // 99/70 ~= 1.41428571428571436819e+00 + private static final double F = 0x1.9b6db6db6db6ep0; // 45/28 ~= 1.60714285714285720630e+00 + private static final double G = 0x1.6db6db6db6db7p-2; // 5/14 ~= 3.57142857142857150787e-01 + + public static strictfp double compute(double x) { + double t = 0.0; + double sign; + + if (x == 0.0 || !Double.isFinite(x)) + return x; // Handles signed zeros properly + + sign = (x < 0.0) ? -1.0: 1.0; + + x = Math.abs(x); // x <- |x| + + // Rough cbrt to 5 bits + if (x < 0x1.0p-1022) { // subnormal number + t = 0x1.0p54; // set t= 2**54 + t *= x; + t = __HI(t, __HI(t)/3 + B2); + } else { + int hx = __HI(x); // high word of x + t = __HI(t, hx/3 + B1); + } + + // New cbrt to 23 bits, may be implemented in single precision + double r, s, w; + r = t * t/x; + s = C + r*t; + t *= G + F/(s + E + D/s); + + // Chopped to 20 bits and make it larger than cbrt(x) + t = __LO(t, 0); + t = __HI(t, __HI(t) + 0x00000001); + + // One step newton iteration to 53 bits with error less than 0.667 ulps + s = t * t; // t*t is exact + r = x / s; + w = t + t; + r = (r - t)/(w + r); // r-s is exact + t = t + t*r; + + // Restore the original sign bit + return sign * t; + } + } + + /** * hypot(x,y) * * Method : diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/lang/StrictMath.java --- a/jdk/src/java.base/share/classes/java/lang/StrictMath.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/StrictMath.java Wed Jul 05 20:54:48 2017 +0200 @@ -307,7 +307,9 @@ * @return the cube root of {@code a}. * @since 1.5 */ - public static native double cbrt(double a); + public static double cbrt(double a) { + return FdLibm.Cbrt.compute(a); + } /** * Computes the remainder operation on two arguments as prescribed diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java --- a/jdk/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java Wed Jul 05 20:54:48 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2015, 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 @@ -834,7 +834,7 @@ static MethodHandle makeCbmhCtor(Class cbmh, String types) { try { - return LOOKUP.findStatic(cbmh, "make", MethodType.fromMethodDescriptorString(makeSignature(types, false), null)); + return LOOKUP.findStatic(cbmh, "make", MethodType.fromDescriptor(makeSignature(types, false), null)); } catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | TypeNotPresentException e) { throw newInternalError(e); } diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/lang/invoke/MemberName.java --- a/jdk/src/java.base/share/classes/java/lang/invoke/MemberName.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MemberName.java Wed Jul 05 20:54:48 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2015, 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 @@ -141,7 +141,7 @@ synchronized (this) { if (type instanceof String) { String sig = (String) type; - MethodType res = MethodType.fromMethodDescriptorString(sig, getClassLoader()); + MethodType res = MethodType.fromDescriptor(sig, getClassLoader()); type = res; } else if (type instanceof Object[]) { Object[] typeInfo = (Object[]) type; @@ -206,7 +206,7 @@ synchronized (this) { if (type instanceof String) { String sig = (String) type; - MethodType mtype = MethodType.fromMethodDescriptorString("()"+sig, getClassLoader()); + MethodType mtype = MethodType.fromDescriptor("()"+sig, getClassLoader()); Class res = mtype.returnType(); type = res; } diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java --- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java Wed Jul 05 20:54:48 2017 +0200 @@ -383,7 +383,7 @@ if (type instanceof MethodType) return (MethodType) type; else - return MethodType.fromMethodDescriptorString((String)type, callerClass.getClassLoader()); + return MethodType.fromDescriptor((String)type, callerClass.getClassLoader()); } // Tracing logic: static MemberName linkMethodTracing(Class callerClass, int refKind, diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/lang/invoke/MethodType.java --- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodType.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodType.java Wed Jul 05 20:54:48 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2015, 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 @@ -1058,6 +1058,23 @@ public static MethodType fromMethodDescriptorString(String descriptor, ClassLoader loader) throws IllegalArgumentException, TypeNotPresentException { + return fromDescriptor(descriptor, + (loader == null) ? ClassLoader.getSystemClassLoader() : loader); + } + + /** + * Same as {@link #fromMethodDescriptorString(String, ClassLoader)}, but + * {@code null} ClassLoader means the bootstrap loader is used here. + *

+ * IMPORTANT: This method is preferable for JDK internal use as it more + * correctly interprets {@code null} ClassLoader than + * {@link #fromMethodDescriptorString(String, ClassLoader)}. + * Use of this method also avoids early initialization issues when system + * ClassLoader is not initialized yet. + */ + static MethodType fromDescriptor(String descriptor, ClassLoader loader) + throws IllegalArgumentException, TypeNotPresentException + { if (!descriptor.startsWith("(") || // also generates NPE if needed descriptor.indexOf(')') < 0 || descriptor.indexOf('.') >= 0) diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/lang/invoke/TypeConvertingMethodAdapter.java --- a/jdk/src/java.base/share/classes/java/lang/invoke/TypeConvertingMethodAdapter.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/TypeConvertingMethodAdapter.java Wed Jul 05 20:54:48 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2015, 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 @@ -131,7 +131,7 @@ } private static String boxingDescriptor(Wrapper w) { - return String.format("(%s)L%s;", w.basicTypeChar(), wrapperName(w)); + return "(" + w.basicTypeChar() + ")L" + wrapperName(w) + ";"; } private static String unboxingDescriptor(Wrapper w) { diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/net/URLStreamHandlerFactory.java --- a/jdk/src/java.base/share/classes/java/net/URLStreamHandlerFactory.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/net/URLStreamHandlerFactory.java Wed Jul 05 20:54:48 2017 +0200 @@ -28,9 +28,9 @@ /** * This interface defines a factory for {@code URL} stream * protocol handlers. - *

- * It is used by the {@code URL} class to create a - * {@code URLStreamHandler} for a specific protocol. + * + *

A URL stream handler factory is used as specified in the + * {@linkplain java.net.URL#URL(String,String,int,String) URL constructor}. * * @author Arthur van Hoff * @see java.net.URL diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/net/spi/URLStreamHandlerProvider.java --- a/jdk/src/java.base/share/classes/java/net/spi/URLStreamHandlerProvider.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/net/spi/URLStreamHandlerProvider.java Wed Jul 05 20:54:48 2017 +0200 @@ -41,6 +41,9 @@ * fully-qualified concrete URL stream handler provider class names, one per * line. * + *

URL stream handler providers are located at runtime, as specified in the + * {@linkplain java.net.URL#URL(String,String,int,String) URL constructor}. + * * @since 1.9 */ public abstract class URLStreamHandlerProvider diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/AbstractQueue.java --- a/jdk/src/java.base/share/classes/java/util/AbstractQueue.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/AbstractQueue.java Wed Jul 05 20:54:48 2017 +0200 @@ -38,16 +38,16 @@ /** * This class provides skeletal implementations of some {@link Queue} * operations. The implementations in this class are appropriate when - * the base implementation does not allow null + * the base implementation does not allow {@code null} * elements. Methods {@link #add add}, {@link #remove remove}, and * {@link #element element} are based on {@link #offer offer}, {@link * #poll poll}, and {@link #peek peek}, respectively, but throw - * exceptions instead of indicating failure via false or - * null returns. + * exceptions instead of indicating failure via {@code false} or + * {@code null} returns. * - *

A Queue implementation that extends this class must + *

A {@code Queue} implementation that extends this class must * minimally define a method {@link Queue#offer} which does not permit - * insertion of null elements, along with methods {@link + * insertion of {@code null} elements, along with methods {@link * Queue#peek}, {@link Queue#poll}, {@link Collection#size}, and * {@link Collection#iterator}. Typically, additional methods will be * overridden as well. If these requirements cannot be met, consider @@ -59,7 +59,7 @@ * * @since 1.5 * @author Doug Lea - * @param the type of elements held in this collection + * @param the type of elements held in this queue */ public abstract class AbstractQueue extends AbstractCollection @@ -74,14 +74,14 @@ /** * Inserts the specified element into this queue if it is possible to do so * immediately without violating capacity restrictions, returning - * true upon success and throwing an IllegalStateException + * {@code true} upon success and throwing an {@code IllegalStateException} * if no space is currently available. * - *

This implementation returns true if offer succeeds, - * else throws an IllegalStateException. + *

This implementation returns {@code true} if {@code offer} succeeds, + * else throws an {@code IllegalStateException}. * * @param e the element to add - * @return true (as specified by {@link Collection#add}) + * @return {@code true} (as specified by {@link Collection#add}) * @throws IllegalStateException if the element cannot be added at this * time due to capacity restrictions * @throws ClassCastException if the class of the specified element @@ -103,7 +103,7 @@ * from {@link #poll poll} only in that it throws an exception if this * queue is empty. * - *

This implementation returns the result of poll + *

This implementation returns the result of {@code poll} * unless the queue is empty. * * @return the head of this queue @@ -122,7 +122,7 @@ * differs from {@link #peek peek} only in that it throws an exception if * this queue is empty. * - *

This implementation returns the result of peek + *

This implementation returns the result of {@code peek} * unless the queue is empty. * * @return the head of this queue @@ -141,7 +141,7 @@ * The queue will be empty after this call returns. * *

This implementation repeatedly invokes {@link #poll poll} until it - * returns null. + * returns {@code null}. */ public void clear() { while (poll() != null) @@ -151,7 +151,7 @@ /** * Adds all of the elements in the specified collection to this * queue. Attempts to addAll of a queue to itself result in - * IllegalArgumentException. Further, the behavior of + * {@code IllegalArgumentException}. Further, the behavior of * this operation is undefined if the specified collection is * modified while the operation is in progress. * @@ -159,12 +159,12 @@ * and adds each element returned by the iterator to this * queue, in turn. A runtime exception encountered while * trying to add an element (including, in particular, a - * null element) may result in only some of the elements + * {@code null} element) may result in only some of the elements * having been successfully added when the associated exception is * thrown. * * @param c collection containing elements to be added to this queue - * @return true if this queue changed as a result of the call + * @return {@code true} if this queue changed as a result of the call * @throws ClassCastException if the class of an element of the specified * collection prevents it from being added to this queue * @throws NullPointerException if the specified collection contains a diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/ArrayDeque.java --- a/jdk/src/java.base/share/classes/java/util/ArrayDeque.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/ArrayDeque.java Wed Jul 05 20:54:48 2017 +0200 @@ -47,16 +47,18 @@ * when used as a queue. * *

Most {@code ArrayDeque} operations run in amortized constant time. - * Exceptions include {@link #remove(Object) remove}, {@link - * #removeFirstOccurrence removeFirstOccurrence}, {@link #removeLastOccurrence - * removeLastOccurrence}, {@link #contains contains}, {@link #iterator - * iterator.remove()}, and the bulk operations, all of which run in linear - * time. + * Exceptions include + * {@link #remove(Object) remove}, + * {@link #removeFirstOccurrence removeFirstOccurrence}, + * {@link #removeLastOccurrence removeLastOccurrence}, + * {@link #contains contains}, + * {@link #iterator iterator.remove()}, + * and the bulk operations, all of which run in linear time. * - *

The iterators returned by this class's {@code iterator} method are - * fail-fast: If the deque is modified at any time after the iterator - * is created, in any way except through the iterator's own {@code remove} - * method, the iterator will generally throw a {@link + *

The iterators returned by this class's {@link #iterator() iterator} + * method are fail-fast: If the deque is modified at any time after + * the iterator is created, in any way except through the iterator's own + * {@code remove} method, the iterator will generally throw a {@link * ConcurrentModificationException}. Thus, in the face of concurrent * modification, the iterator fails quickly and cleanly, rather than risking * arbitrary, non-deterministic behavior at an undetermined time in the @@ -80,7 +82,7 @@ * * @author Josh Bloch and Doug Lea * @since 1.6 - * @param the type of elements held in this collection + * @param the type of elements held in this deque */ public class ArrayDeque extends AbstractCollection implements Deque, Cloneable, Serializable @@ -136,8 +138,8 @@ initialCapacity |= (initialCapacity >>> 16); initialCapacity++; - if (initialCapacity < 0) // Too many elements, must back off - initialCapacity >>>= 1;// Good luck allocating 2 ^ 30 elements + if (initialCapacity < 0) // Too many elements, must back off + initialCapacity >>>= 1; // Good luck allocating 2^30 elements } elements = new Object[initialCapacity]; } @@ -163,24 +165,6 @@ } /** - * Copies the elements from our element array into the specified array, - * in order (from first to last element in the deque). It is assumed - * that the array is large enough to hold all elements in the deque. - * - * @return its argument - */ - private T[] copyElements(T[] a) { - if (head < tail) { - System.arraycopy(elements, head, a, 0, size()); - } else if (head > tail) { - int headPortionLen = elements.length - head; - System.arraycopy(elements, head, a, 0, headPortionLen); - System.arraycopy(elements, 0, a, headPortionLen, tail); - } - return a; - } - - /** * Constructs an empty array deque with an initial capacity * sufficient to hold 16 elements. */ @@ -292,25 +276,27 @@ } public E pollFirst() { - int h = head; + final Object[] elements = this.elements; + final int h = head; @SuppressWarnings("unchecked") E result = (E) elements[h]; // Element is null if deque empty - if (result == null) - return null; - elements[h] = null; // Must null out slot - head = (h + 1) & (elements.length - 1); + if (result != null) { + elements[h] = null; // Must null out slot + head = (h + 1) & (elements.length - 1); + } return result; } public E pollLast() { - int t = (tail - 1) & (elements.length - 1); + final Object[] elements = this.elements; + final int t = (tail - 1) & (elements.length - 1); @SuppressWarnings("unchecked") E result = (E) elements[t]; - if (result == null) - return null; - elements[t] = null; - tail = t; + if (result != null) { + elements[t] = null; + tail = t; + } return result; } @@ -360,17 +346,15 @@ * @return {@code true} if the deque contained the specified element */ public boolean removeFirstOccurrence(Object o) { - if (o == null) - return false; - int mask = elements.length - 1; - int i = head; - Object x; - while ( (x = elements[i]) != null) { - if (o.equals(x)) { - delete(i); - return true; + if (o != null) { + int mask = elements.length - 1; + int i = head; + for (Object x; (x = elements[i]) != null; i = (i + 1) & mask) { + if (o.equals(x)) { + delete(i); + return true; + } } - i = (i + 1) & mask; } return false; } @@ -388,17 +372,15 @@ * @return {@code true} if the deque contained the specified element */ public boolean removeLastOccurrence(Object o) { - if (o == null) - return false; - int mask = elements.length - 1; - int i = (tail - 1) & mask; - Object x; - while ( (x = elements[i]) != null) { - if (o.equals(x)) { - delete(i); - return true; + if (o != null) { + int mask = elements.length - 1; + int i = (tail - 1) & mask; + for (Object x; (x = elements[i]) != null; i = (i - 1) & mask) { + if (o.equals(x)) { + delete(i); + return true; + } } - i = (i - 1) & mask; } return false; } @@ -535,7 +517,7 @@ * * @return true if elements moved backwards */ - private boolean delete(int i) { + boolean delete(int i) { checkInvariants(); final Object[] elements = this.elements; final int mask = elements.length - 1; @@ -671,12 +653,12 @@ } } + /** + * This class is nearly a mirror-image of DeqIterator, using tail + * instead of head for initial cursor, and head instead of tail + * for fence. + */ private class DescendingIterator implements Iterator { - /* - * This class is nearly a mirror-image of DeqIterator, using - * tail instead of head for initial cursor, and head instead of - * tail for fence. - */ private int cursor = tail; private int fence = head; private int lastRet = -1; @@ -717,15 +699,13 @@ * @return {@code true} if this deque contains the specified element */ public boolean contains(Object o) { - if (o == null) - return false; - int mask = elements.length - 1; - int i = head; - Object x; - while ( (x = elements[i]) != null) { - if (o.equals(x)) - return true; - i = (i + 1) & mask; + if (o != null) { + int mask = elements.length - 1; + int i = head; + for (Object x; (x = elements[i]) != null; i = (i + 1) & mask) { + if (o.equals(x)) + return true; + } } return false; } @@ -779,7 +759,14 @@ * @return an array containing all of the elements in this deque */ public Object[] toArray() { - return copyElements(new Object[size()]); + final int head = this.head; + final int tail = this.tail; + boolean wrap = (tail < head); + int end = wrap ? tail + elements.length : tail; + Object[] a = Arrays.copyOfRange(elements, head, end); + if (wrap) + System.arraycopy(elements, 0, a, elements.length - head, tail); + return a; } /** @@ -804,7 +791,7 @@ * The following code can be used to dump the deque into a newly * allocated array of {@code String}: * - *

 {@code String[] y = x.toArray(new String[0]);}
+ *
 {@code String[] y = x.toArray(new String[0]);}
* * Note that {@code toArray(new Object[0])} is identical in function to * {@code toArray()}. @@ -820,13 +807,22 @@ */ @SuppressWarnings("unchecked") public T[] toArray(T[] a) { - int size = size(); - if (a.length < size) - a = (T[])java.lang.reflect.Array.newInstance( - a.getClass().getComponentType(), size); - copyElements(a); - if (a.length > size) - a[size] = null; + final int head = this.head; + final int tail = this.tail; + boolean wrap = (tail < head); + int size = (tail - head) + (wrap ? elements.length : 0); + int firstLeg = size - (wrap ? tail : 0); + int len = a.length; + if (size > len) { + a = (T[]) Arrays.copyOfRange(elements, head, head + size, + a.getClass()); + } else { + System.arraycopy(elements, head, a, 0, firstLeg); + if (size < len) + a[size] = null; + } + if (wrap) + System.arraycopy(elements, 0, a, firstLeg, tail); return a; } @@ -853,6 +849,8 @@ /** * Saves this deque to a stream (that is, serializes it). * + * @param s the stream + * @throws java.io.IOException if an I/O error occurs * @serialData The current size ({@code int}) of the deque, * followed by all of its elements (each an object reference) in * first-to-last order. @@ -872,6 +870,10 @@ /** * Reconstitutes this deque from a stream (that is, deserializes it). + * @param s the stream + * @throws ClassNotFoundException if the class of a serialized object + * could not be found + * @throws java.io.IOException if an I/O error occurs */ private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { @@ -910,7 +912,7 @@ private int fence; // -1 until first use private int index; // current index, modified on traverse/split - /** Creates new spliterator covering the given array and range */ + /** Creates new spliterator covering the given array and range. */ DeqSpliterator(ArrayDeque deq, int origin, int fence) { this.deq = deq; this.index = origin; @@ -932,7 +934,7 @@ if (h > t) t += n; int m = ((h + t) >>> 1) & (n - 1); - return new DeqSpliterator<>(deq, h, index = m); + return new DeqSpliterator(deq, h, index = m); } return null; } @@ -957,7 +959,7 @@ throw new NullPointerException(); Object[] a = deq.elements; int m = a.length - 1, f = getFence(), i = index; - if (i != fence) { + if (i != f) { @SuppressWarnings("unchecked") E e = (E)a[i]; index = (i + 1) & m; if (e == null) diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/ArrayPrefixHelpers.java --- a/jdk/src/java.base/share/classes/java/util/ArrayPrefixHelpers.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/ArrayPrefixHelpers.java Wed Jul 05 20:54:48 2017 +0200 @@ -1,5 +1,4 @@ /* - * Copyright (c) 2012, 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 @@ -22,20 +21,26 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package java.util; /* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at * http://creativecommons.org/publicdomain/zero/1.0/ */ -import java.util.concurrent.ForkJoinPool; +package java.util; + import java.util.concurrent.CountedCompleter; +import java.util.concurrent.ForkJoinPool; import java.util.function.BinaryOperator; +import java.util.function.DoubleBinaryOperator; import java.util.function.IntBinaryOperator; import java.util.function.LongBinaryOperator; -import java.util.function.DoubleBinaryOperator; /** * ForkJoin tasks to perform Arrays.parallelPrefix operations. @@ -44,7 +49,7 @@ * @since 1.8 */ class ArrayPrefixHelpers { - private ArrayPrefixHelpers() {}; // non-instantiable + private ArrayPrefixHelpers() {} // non-instantiable /* * Parallel prefix (aka cumulate, scan) task classes @@ -113,8 +118,8 @@ this.lo = this.origin = lo; this.hi = this.fence = hi; int p; this.threshold = - (p = (hi - lo) / (ForkJoinPool.getCommonPoolParallelism() << 3)) - <= MIN_PARTITION ? MIN_PARTITION : p; + (p = (hi - lo) / (ForkJoinPool.getCommonPoolParallelism() << 3)) + <= MIN_PARTITION ? MIN_PARTITION : p; } /** Subtask constructor */ @@ -141,9 +146,9 @@ if (lt == null) { // first pass int mid = (l + h) >>> 1; f = rt = t.right = - new CumulateTask(t, fn, a, org, fnc, th, mid, h); - t = lt = t.left = - new CumulateTask(t, fn, a, org, fnc, th, l, mid); + new CumulateTask(t, fn, a, org, fnc, th, mid, h); + t = lt = t.left = + new CumulateTask(t, fn, a, org, fnc, th, l, mid); } else { // possibly refork T pin = t.in; @@ -183,7 +188,7 @@ for (int b;;) { if (((b = t.getPendingCount()) & FINISHED) != 0) break outer; // already done - state = ((b & CUMULATE) != 0? FINISHED : + state = ((b & CUMULATE) != 0 ? FINISHED : (l > org) ? SUMMED : (SUMMED|FINISHED)); if (t.compareAndSetPendingCount(b, b|state)) break; @@ -265,8 +270,8 @@ this.lo = this.origin = lo; this.hi = this.fence = hi; int p; this.threshold = - (p = (hi - lo) / (ForkJoinPool.getCommonPoolParallelism() << 3)) - <= MIN_PARTITION ? MIN_PARTITION : p; + (p = (hi - lo) / (ForkJoinPool.getCommonPoolParallelism() << 3)) + <= MIN_PARTITION ? MIN_PARTITION : p; } /** Subtask constructor */ @@ -293,9 +298,9 @@ if (lt == null) { // first pass int mid = (l + h) >>> 1; f = rt = t.right = - new LongCumulateTask(t, fn, a, org, fnc, th, mid, h); - t = lt = t.left = - new LongCumulateTask(t, fn, a, org, fnc, th, l, mid); + new LongCumulateTask(t, fn, a, org, fnc, th, mid, h); + t = lt = t.left = + new LongCumulateTask(t, fn, a, org, fnc, th, l, mid); } else { // possibly refork long pin = t.in; @@ -335,7 +340,7 @@ for (int b;;) { if (((b = t.getPendingCount()) & FINISHED) != 0) break outer; // already done - state = ((b & CUMULATE) != 0? FINISHED : + state = ((b & CUMULATE) != 0 ? FINISHED : (l > org) ? SUMMED : (SUMMED|FINISHED)); if (t.compareAndSetPendingCount(b, b|state)) break; @@ -415,8 +420,8 @@ this.lo = this.origin = lo; this.hi = this.fence = hi; int p; this.threshold = - (p = (hi - lo) / (ForkJoinPool.getCommonPoolParallelism() << 3)) - <= MIN_PARTITION ? MIN_PARTITION : p; + (p = (hi - lo) / (ForkJoinPool.getCommonPoolParallelism() << 3)) + <= MIN_PARTITION ? MIN_PARTITION : p; } /** Subtask constructor */ @@ -443,9 +448,9 @@ if (lt == null) { // first pass int mid = (l + h) >>> 1; f = rt = t.right = - new DoubleCumulateTask(t, fn, a, org, fnc, th, mid, h); - t = lt = t.left = - new DoubleCumulateTask(t, fn, a, org, fnc, th, l, mid); + new DoubleCumulateTask(t, fn, a, org, fnc, th, mid, h); + t = lt = t.left = + new DoubleCumulateTask(t, fn, a, org, fnc, th, l, mid); } else { // possibly refork double pin = t.in; @@ -485,7 +490,7 @@ for (int b;;) { if (((b = t.getPendingCount()) & FINISHED) != 0) break outer; // already done - state = ((b & CUMULATE) != 0? FINISHED : + state = ((b & CUMULATE) != 0 ? FINISHED : (l > org) ? SUMMED : (SUMMED|FINISHED)); if (t.compareAndSetPendingCount(b, b|state)) break; @@ -565,8 +570,8 @@ this.lo = this.origin = lo; this.hi = this.fence = hi; int p; this.threshold = - (p = (hi - lo) / (ForkJoinPool.getCommonPoolParallelism() << 3)) - <= MIN_PARTITION ? MIN_PARTITION : p; + (p = (hi - lo) / (ForkJoinPool.getCommonPoolParallelism() << 3)) + <= MIN_PARTITION ? MIN_PARTITION : p; } /** Subtask constructor */ @@ -593,9 +598,9 @@ if (lt == null) { // first pass int mid = (l + h) >>> 1; f = rt = t.right = - new IntCumulateTask(t, fn, a, org, fnc, th, mid, h); - t = lt = t.left = - new IntCumulateTask(t, fn, a, org, fnc, th, l, mid); + new IntCumulateTask(t, fn, a, org, fnc, th, mid, h); + t = lt = t.left = + new IntCumulateTask(t, fn, a, org, fnc, th, l, mid); } else { // possibly refork int pin = t.in; @@ -635,7 +640,7 @@ for (int b;;) { if (((b = t.getPendingCount()) & FINISHED) != 0) break outer; // already done - state = ((b & CUMULATE) != 0? FINISHED : + state = ((b & CUMULATE) != 0 ? FINISHED : (l > org) ? SUMMED : (SUMMED|FINISHED)); if (t.compareAndSetPendingCount(b, b|state)) break; diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/Collections.java --- a/jdk/src/java.base/share/classes/java/util/Collections.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/Collections.java Wed Jul 05 20:54:48 2017 +0200 @@ -537,8 +537,9 @@ * Copies all of the elements from one list into another. After the * operation, the index of each copied element in the destination list * will be identical to its index in the source list. The destination - * list must be at least as long as the source list. If it is longer, the - * remaining elements in the destination list are unaffected.

+ * list's size must be greater than or equal to the source list's size. + * If it is greater, the remaining elements in the destination list are + * unaffected.

* * This method runs in linear time. * diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/Deque.java --- a/jdk/src/java.base/share/classes/java/util/Deque.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/Deque.java Wed Jul 05 20:54:48 2017 +0200 @@ -188,7 +188,7 @@ * @author Doug Lea * @author Josh Bloch * @since 1.6 - * @param the type of elements held in this collection + * @param the type of elements held in this deque */ public interface Deque extends Queue { /** @@ -344,8 +344,7 @@ * Removes the first occurrence of the specified element from this deque. * If the deque does not contain the element, it is unchanged. * More formally, removes the first element {@code e} such that - * (o==null ? e==null : o.equals(e)) - * (if such an element exists). + * {@code Objects.equals(o, e)} (if such an element exists). * Returns {@code true} if this deque contained the specified element * (or equivalently, if this deque changed as a result of the call). * @@ -353,10 +352,10 @@ * @return {@code true} if an element was removed as a result of this call * @throws ClassCastException if the class of the specified element * is incompatible with this deque - * (optional) + * (optional) * @throws NullPointerException if the specified element is null and this * deque does not permit null elements - * (optional) + * (optional) */ boolean removeFirstOccurrence(Object o); @@ -364,8 +363,7 @@ * Removes the last occurrence of the specified element from this deque. * If the deque does not contain the element, it is unchanged. * More formally, removes the last element {@code e} such that - * (o==null ? e==null : o.equals(e)) - * (if such an element exists). + * {@code Objects.equals(o, e)} (if such an element exists). * Returns {@code true} if this deque contained the specified element * (or equivalently, if this deque changed as a result of the call). * @@ -373,10 +371,10 @@ * @return {@code true} if an element was removed as a result of this call * @throws ClassCastException if the class of the specified element * is incompatible with this deque - * (optional) + * (optional) * @throws NullPointerException if the specified element is null and this * deque does not permit null elements - * (optional) + * (optional) */ boolean removeLastOccurrence(Object o); @@ -521,8 +519,7 @@ * Removes the first occurrence of the specified element from this deque. * If the deque does not contain the element, it is unchanged. * More formally, removes the first element {@code e} such that - * (o==null ? e==null : o.equals(e)) - * (if such an element exists). + * {@code Objects.equals(o, e)} (if such an element exists). * Returns {@code true} if this deque contained the specified element * (or equivalently, if this deque changed as a result of the call). * @@ -532,27 +529,26 @@ * @return {@code true} if an element was removed as a result of this call * @throws ClassCastException if the class of the specified element * is incompatible with this deque - * (optional) + * (optional) * @throws NullPointerException if the specified element is null and this * deque does not permit null elements - * (optional) + * (optional) */ boolean remove(Object o); /** * Returns {@code true} if this deque contains the specified element. * More formally, returns {@code true} if and only if this deque contains - * at least one element {@code e} such that - * (o==null ? e==null : o.equals(e)). + * at least one element {@code e} such that {@code Objects.equals(o, e)}. * * @param o element whose presence in this deque is to be tested * @return {@code true} if this deque contains the specified element - * @throws ClassCastException if the type of the specified element + * @throws ClassCastException if the class of the specified element * is incompatible with this deque - * (optional) + * (optional) * @throws NullPointerException if the specified element is null and this * deque does not permit null elements - * (optional) + * (optional) */ boolean contains(Object o); @@ -561,7 +557,7 @@ * * @return the number of elements in this deque */ - public int size(); + int size(); /** * Returns an iterator over the elements in this deque in proper sequence. diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/NavigableMap.java --- a/jdk/src/java.base/share/classes/java/util/NavigableMap.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/NavigableMap.java Wed Jul 05 20:54:48 2017 +0200 @@ -38,30 +38,32 @@ /** * A {@link SortedMap} extended with navigation methods returning the * closest matches for given search targets. Methods - * {@code lowerEntry}, {@code floorEntry}, {@code ceilingEntry}, - * and {@code higherEntry} return {@code Map.Entry} objects + * {@link #lowerEntry}, {@link #floorEntry}, {@link #ceilingEntry}, + * and {@link #higherEntry} return {@code Map.Entry} objects * associated with keys respectively less than, less than or equal, * greater than or equal, and greater than a given key, returning * {@code null} if there is no such key. Similarly, methods - * {@code lowerKey}, {@code floorKey}, {@code ceilingKey}, and - * {@code higherKey} return only the associated keys. All of these + * {@link #lowerKey}, {@link #floorKey}, {@link #ceilingKey}, and + * {@link #higherKey} return only the associated keys. All of these * methods are designed for locating, not traversing entries. * *

A {@code NavigableMap} may be accessed and traversed in either - * ascending or descending key order. The {@code descendingMap} + * ascending or descending key order. The {@link #descendingMap} * method returns a view of the map with the senses of all relational * and directional methods inverted. The performance of ascending * operations and views is likely to be faster than that of descending - * ones. Methods {@code subMap}, {@code headMap}, - * and {@code tailMap} differ from the like-named {@code - * SortedMap} methods in accepting additional arguments describing - * whether lower and upper bounds are inclusive versus exclusive. - * Submaps of any {@code NavigableMap} must implement the {@code - * NavigableMap} interface. + * ones. Methods + * {@link #subMap(Object, boolean, Object, boolean) subMap(K, boolean, K, boolean)}, + * {@link #headMap(Object, boolean) headMap(K, boolean)}, and + * {@link #tailMap(Object, boolean) tailMap(K, boolean)} + * differ from the like-named {@code SortedMap} methods in accepting + * additional arguments describing whether lower and upper bounds are + * inclusive versus exclusive. Submaps of any {@code NavigableMap} + * must implement the {@code NavigableMap} interface. * - *

This interface additionally defines methods {@code firstEntry}, - * {@code pollFirstEntry}, {@code lastEntry}, and - * {@code pollLastEntry} that return and/or remove the least and + *

This interface additionally defines methods {@link #firstEntry}, + * {@link #pollFirstEntry}, {@link #lastEntry}, and + * {@link #pollLastEntry} that return and/or remove the least and * greatest mappings, if any exist, else returning {@code null}. * *

Implementations of entry-returning methods are expected to @@ -80,7 +82,7 @@ * implement {@code NavigableMap}, but extensions and implementations * of this interface are encouraged to override these methods to return * {@code NavigableMap}. Similarly, - * {@link #keySet()} can be overriden to return {@code NavigableSet}. + * {@link #keySet()} can be overridden to return {@link NavigableSet}. * *

This interface is a member of the * @@ -254,7 +256,7 @@ * operation), the results of the iteration are undefined. * *

The returned map has an ordering equivalent to - * {@link Collections#reverseOrder(Comparator) Collections.reverseOrder}(comparator()). + * {@link Collections#reverseOrder(Comparator) Collections.reverseOrder}{@code (comparator())}. * The expression {@code m.descendingMap().descendingMap()} returns a * view of {@code m} essentially equivalent to {@code m}. * diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/NavigableSet.java --- a/jdk/src/java.base/share/classes/java/util/NavigableSet.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/NavigableSet.java Wed Jul 05 20:54:48 2017 +0200 @@ -37,26 +37,30 @@ /** * A {@link SortedSet} extended with navigation methods reporting - * closest matches for given search targets. Methods {@code lower}, - * {@code floor}, {@code ceiling}, and {@code higher} return elements + * closest matches for given search targets. Methods {@link #lower}, + * {@link #floor}, {@link #ceiling}, and {@link #higher} return elements * respectively less than, less than or equal, greater than or equal, * and greater than a given element, returning {@code null} if there - * is no such element. A {@code NavigableSet} may be accessed and - * traversed in either ascending or descending order. The {@code - * descendingSet} method returns a view of the set with the senses of - * all relational and directional methods inverted. The performance of - * ascending operations and views is likely to be faster than that of - * descending ones. This interface additionally defines methods - * {@code pollFirst} and {@code pollLast} that return and remove the - * lowest and highest element, if one exists, else returning {@code - * null}. Methods {@code subSet}, {@code headSet}, - * and {@code tailSet} differ from the like-named {@code - * SortedSet} methods in accepting additional arguments describing - * whether lower and upper bounds are inclusive versus exclusive. - * Subsets of any {@code NavigableSet} must implement the {@code - * NavigableSet} interface. + * is no such element. * - *

The return values of navigation methods may be ambiguous in + *

A {@code NavigableSet} may be accessed and traversed in either + * ascending or descending order. The {@link #descendingSet} method + * returns a view of the set with the senses of all relational and + * directional methods inverted. The performance of ascending + * operations and views is likely to be faster than that of descending + * ones. This interface additionally defines methods {@link + * #pollFirst} and {@link #pollLast} that return and remove the lowest + * and highest element, if one exists, else returning {@code null}. + * Methods + * {@link #subSet(Object, boolean, Object, boolean) subSet(E, boolean, E, boolean)}, + * {@link #headSet(Object, boolean) headSet(E, boolean)}, and + * {@link #tailSet(Object, boolean) tailSet(E, boolean)} + * differ from the like-named {@code SortedSet} methods in accepting + * additional arguments describing whether lower and upper bounds are + * inclusive versus exclusive. Subsets of any {@code NavigableSet} + * must implement the {@code NavigableSet} interface. + * + *

The return values of navigation methods may be ambiguous in * implementations that permit {@code null} elements. However, even * in this case the result can be disambiguated by checking * {@code contains(null)}. To avoid such issues, implementations of @@ -172,7 +176,7 @@ * the iteration are undefined. * *

The returned set has an ordering equivalent to - * {@link Collections#reverseOrder(Comparator) Collections.reverseOrder}(comparator()). + * {@link Collections#reverseOrder(Comparator) Collections.reverseOrder}{@code (comparator())}. * The expression {@code s.descendingSet().descendingSet()} returns a * view of {@code s} essentially equivalent to {@code s}. * diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/PriorityQueue.java --- a/jdk/src/java.base/share/classes/java/util/PriorityQueue.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/PriorityQueue.java Wed Jul 05 20:54:48 2017 +0200 @@ -77,7 +77,7 @@ * * @since 1.5 * @author Josh Bloch, Doug Lea - * @param the type of elements held in this collection + * @param the type of elements held in this queue */ public class PriorityQueue extends AbstractQueue implements java.io.Serializable { @@ -99,7 +99,7 @@ /** * The number of elements in the priority queue. */ - private int size = 0; + int size; /** * The comparator, or null if priority queue uses elements' @@ -111,7 +111,7 @@ * The number of times this priority queue has been * structurally modified. See AbstractList for gory details. */ - transient int modCount = 0; // non-private to simplify nested class access + transient int modCount; // non-private to simplify nested class access /** * Creates a {@code PriorityQueue} with the default initial @@ -448,7 +448,7 @@ * The following code can be used to dump the queue into a newly * allocated array of {@code String}: * - *

 {@code String[] y = x.toArray(new String[0]);}
+ *
 {@code String[] y = x.toArray(new String[0]);}
* * Note that {@code toArray(new Object[0])} is identical in function to * {@code toArray()}. @@ -489,7 +489,7 @@ * Index (into queue array) of element to be returned by * subsequent call to next. */ - private int cursor = 0; + private int cursor; /** * Index of element returned by most recent call to next, @@ -509,13 +509,13 @@ * We expect that most iterations, even those involving removals, * will not need to store elements in this field. */ - private ArrayDeque forgetMeNot = null; + private ArrayDeque forgetMeNot; /** * Element returned by the most recent call to next iff that * element was drawn from the forgetMeNot list. */ - private E lastRetElt = null; + private E lastRetElt; /** * The modCount value that the iterator believes that the backing @@ -609,7 +609,7 @@ * avoid missing traversing elements. */ @SuppressWarnings("unchecked") - private E removeAt(int i) { + E removeAt(int i) { // assert i >= 0 && i < size; modCount++; int s = --size; @@ -756,6 +756,7 @@ * emitted (int), followed by all of its elements * (each an {@code Object}) in the proper order. * @param s the stream + * @throws java.io.IOException if an I/O error occurs */ private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { @@ -775,6 +776,9 @@ * (that is, deserializes it). * * @param s the stream + * @throws ClassNotFoundException if the class of a serialized object + * could not be found + * @throws java.io.IOException if an I/O error occurs */ private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { @@ -822,9 +826,9 @@ private int fence; // -1 until first use private int expectedModCount; // initialized when fence set - /** Creates new spliterator covering the given range */ + /** Creates new spliterator covering the given range. */ PriorityQueueSpliterator(PriorityQueue pq, int origin, int fence, - int expectedModCount) { + int expectedModCount) { this.pq = pq; this.index = origin; this.fence = fence; diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/Queue.java --- a/jdk/src/java.base/share/classes/java/util/Queue.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/Queue.java Wed Jul 05 20:54:48 2017 +0200 @@ -139,7 +139,7 @@ * @see java.util.concurrent.PriorityBlockingQueue * @since 1.5 * @author Doug Lea - * @param the type of elements held in this collection + * @param the type of elements held in this queue */ public interface Queue extends Collection { /** diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/SplittableRandom.java --- a/jdk/src/java.base/share/classes/java/util/SplittableRandom.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/SplittableRandom.java Wed Jul 05 20:54:48 2017 +0200 @@ -26,13 +26,13 @@ package java.util; import java.util.concurrent.atomic.AtomicLong; +import java.util.function.DoubleConsumer; import java.util.function.IntConsumer; import java.util.function.LongConsumer; -import java.util.function.DoubleConsumer; -import java.util.stream.StreamSupport; +import java.util.stream.DoubleStream; import java.util.stream.IntStream; import java.util.stream.LongStream; -import java.util.stream.DoubleStream; +import java.util.stream.StreamSupport; /** * A generator of uniform pseudorandom values applicable for use in @@ -52,15 +52,15 @@ * types and ranges, but similar properties are expected to hold, at * least approximately, for others as well. The period * (length of any series of generated values before it repeats) is at - * least 264. + * least 264. * - *
  • Method {@link #split} constructs and returns a new + *
  • Method {@link #split} constructs and returns a new * SplittableRandom instance that shares no mutable state with the * current instance. However, with very high probability, the * values collectively generated by the two objects have the same * statistical properties as if the same quantity of values were * generated by a single thread using a single {@code - * SplittableRandom} object.
  • + * SplittableRandom} object. * *
  • Instances of SplittableRandom are not thread-safe. * They are designed to be split, not shared, across threads. For @@ -71,7 +71,7 @@ * *
  • This class provides additional methods for generating random * streams, that employ the above techniques when used in {@code - * stream.parallel()} mode.
  • + * stream.parallel()} mode. * * * @@ -240,9 +240,9 @@ } // IllegalArgumentException messages - static final String BadBound = "bound must be positive"; - static final String BadRange = "bound must be greater than origin"; - static final String BadSize = "size must be non-negative"; + static final String BAD_BOUND = "bound must be positive"; + static final String BAD_RANGE = "bound must be greater than origin"; + static final String BAD_SIZE = "size must be non-negative"; /* * Internal versions of nextX methods used by streams, as well as @@ -416,7 +416,7 @@ */ public int nextInt(int bound) { if (bound <= 0) - throw new IllegalArgumentException(BadBound); + throw new IllegalArgumentException(BAD_BOUND); // Specialize internalNextInt for origin 0 int r = mix32(nextSeed()); int m = bound - 1; @@ -444,7 +444,7 @@ */ public int nextInt(int origin, int bound) { if (origin >= bound) - throw new IllegalArgumentException(BadRange); + throw new IllegalArgumentException(BAD_RANGE); return internalNextInt(origin, bound); } @@ -468,7 +468,7 @@ */ public long nextLong(long bound) { if (bound <= 0) - throw new IllegalArgumentException(BadBound); + throw new IllegalArgumentException(BAD_BOUND); // Specialize internalNextLong for origin 0 long r = mix64(nextSeed()); long m = bound - 1; @@ -496,7 +496,7 @@ */ public long nextLong(long origin, long bound) { if (origin >= bound) - throw new IllegalArgumentException(BadRange); + throw new IllegalArgumentException(BAD_RANGE); return internalNextLong(origin, bound); } @@ -522,7 +522,7 @@ */ public double nextDouble(double bound) { if (!(bound > 0.0)) - throw new IllegalArgumentException(BadBound); + throw new IllegalArgumentException(BAD_BOUND); double result = (mix64(nextSeed()) >>> 11) * DOUBLE_UNIT * bound; return (result < bound) ? result : // correct for rounding Double.longBitsToDouble(Double.doubleToLongBits(bound) - 1); @@ -541,7 +541,7 @@ */ public double nextDouble(double origin, double bound) { if (!(origin < bound)) - throw new IllegalArgumentException(BadRange); + throw new IllegalArgumentException(BAD_RANGE); return internalNextDouble(origin, bound); } @@ -569,7 +569,7 @@ */ public IntStream ints(long streamSize) { if (streamSize < 0L) - throw new IllegalArgumentException(BadSize); + throw new IllegalArgumentException(BAD_SIZE); return StreamSupport.intStream (new RandomIntsSpliterator (this, 0L, streamSize, Integer.MAX_VALUE, 0), @@ -610,9 +610,9 @@ public IntStream ints(long streamSize, int randomNumberOrigin, int randomNumberBound) { if (streamSize < 0L) - throw new IllegalArgumentException(BadSize); + throw new IllegalArgumentException(BAD_SIZE); if (randomNumberOrigin >= randomNumberBound) - throw new IllegalArgumentException(BadRange); + throw new IllegalArgumentException(BAD_RANGE); return StreamSupport.intStream (new RandomIntsSpliterator (this, 0L, streamSize, randomNumberOrigin, randomNumberBound), @@ -636,7 +636,7 @@ */ public IntStream ints(int randomNumberOrigin, int randomNumberBound) { if (randomNumberOrigin >= randomNumberBound) - throw new IllegalArgumentException(BadRange); + throw new IllegalArgumentException(BAD_RANGE); return StreamSupport.intStream (new RandomIntsSpliterator (this, 0L, Long.MAX_VALUE, randomNumberOrigin, randomNumberBound), @@ -655,7 +655,7 @@ */ public LongStream longs(long streamSize) { if (streamSize < 0L) - throw new IllegalArgumentException(BadSize); + throw new IllegalArgumentException(BAD_SIZE); return StreamSupport.longStream (new RandomLongsSpliterator (this, 0L, streamSize, Long.MAX_VALUE, 0L), @@ -696,9 +696,9 @@ public LongStream longs(long streamSize, long randomNumberOrigin, long randomNumberBound) { if (streamSize < 0L) - throw new IllegalArgumentException(BadSize); + throw new IllegalArgumentException(BAD_SIZE); if (randomNumberOrigin >= randomNumberBound) - throw new IllegalArgumentException(BadRange); + throw new IllegalArgumentException(BAD_RANGE); return StreamSupport.longStream (new RandomLongsSpliterator (this, 0L, streamSize, randomNumberOrigin, randomNumberBound), @@ -722,7 +722,7 @@ */ public LongStream longs(long randomNumberOrigin, long randomNumberBound) { if (randomNumberOrigin >= randomNumberBound) - throw new IllegalArgumentException(BadRange); + throw new IllegalArgumentException(BAD_RANGE); return StreamSupport.longStream (new RandomLongsSpliterator (this, 0L, Long.MAX_VALUE, randomNumberOrigin, randomNumberBound), @@ -741,7 +741,7 @@ */ public DoubleStream doubles(long streamSize) { if (streamSize < 0L) - throw new IllegalArgumentException(BadSize); + throw new IllegalArgumentException(BAD_SIZE); return StreamSupport.doubleStream (new RandomDoublesSpliterator (this, 0L, streamSize, Double.MAX_VALUE, 0.0), @@ -784,9 +784,9 @@ public DoubleStream doubles(long streamSize, double randomNumberOrigin, double randomNumberBound) { if (streamSize < 0L) - throw new IllegalArgumentException(BadSize); + throw new IllegalArgumentException(BAD_SIZE); if (!(randomNumberOrigin < randomNumberBound)) - throw new IllegalArgumentException(BadRange); + throw new IllegalArgumentException(BAD_RANGE); return StreamSupport.doubleStream (new RandomDoublesSpliterator (this, 0L, streamSize, randomNumberOrigin, randomNumberBound), @@ -810,7 +810,7 @@ */ public DoubleStream doubles(double randomNumberOrigin, double randomNumberBound) { if (!(randomNumberOrigin < randomNumberBound)) - throw new IllegalArgumentException(BadRange); + throw new IllegalArgumentException(BAD_RANGE); return StreamSupport.doubleStream (new RandomDoublesSpliterator (this, 0L, Long.MAX_VALUE, randomNumberOrigin, randomNumberBound), @@ -825,7 +825,8 @@ * approach. The long and double versions of this class are * identical except for types. */ - static final class RandomIntsSpliterator implements Spliterator.OfInt { + private static final class RandomIntsSpliterator + implements Spliterator.OfInt { final SplittableRandom rng; long index; final long fence; @@ -880,7 +881,8 @@ /** * Spliterator for long streams. */ - static final class RandomLongsSpliterator implements Spliterator.OfLong { + private static final class RandomLongsSpliterator + implements Spliterator.OfLong { final SplittableRandom rng; long index; final long fence; @@ -936,7 +938,8 @@ /** * Spliterator for double streams. */ - static final class RandomDoublesSpliterator implements Spliterator.OfDouble { + private static final class RandomDoublesSpliterator + implements Spliterator.OfDouble { final SplittableRandom rng; long index; final long fence; diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/AbstractExecutorService.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/AbstractExecutorService.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/AbstractExecutorService.java Wed Jul 05 20:54:48 2017 +0200 @@ -34,7 +34,13 @@ */ package java.util.concurrent; -import java.util.*; + +import static java.util.concurrent.TimeUnit.NANOSECONDS; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; /** * Provides default implementations of {@link ExecutorService} @@ -51,7 +57,7 @@ *

    Extension example. Here is a sketch of a class * that customizes {@link ThreadPoolExecutor} to use * a {@code CustomTask} class instead of the default {@code FutureTask}: - *

     {@code
    + * 
     {@code
      * public class CustomThreadPoolExecutor extends ThreadPoolExecutor {
      *
      *   static class CustomTask implements RunnableFuture {...}
    @@ -146,7 +152,7 @@
             int ntasks = tasks.size();
             if (ntasks == 0)
                 throw new IllegalArgumentException();
    -        ArrayList> futures = new ArrayList>(ntasks);
    +        ArrayList> futures = new ArrayList<>(ntasks);
             ExecutorCompletionService ecs =
                 new ExecutorCompletionService(this);
     
    @@ -179,7 +185,7 @@
                         else if (active == 0)
                             break;
                         else if (timed) {
    -                        f = ecs.poll(nanos, TimeUnit.NANOSECONDS);
    +                        f = ecs.poll(nanos, NANOSECONDS);
                             if (f == null)
                                 throw new TimeoutException();
                             nanos = deadline - System.nanoTime();
    @@ -204,8 +210,7 @@
                 throw ee;
     
             } finally {
    -            for (int i = 0, size = futures.size(); i < size; i++)
    -                futures.get(i).cancel(true);
    +            cancelAll(futures);
             }
         }
     
    @@ -229,8 +234,7 @@
             throws InterruptedException {
             if (tasks == null)
                 throw new NullPointerException();
    -        ArrayList> futures = new ArrayList>(tasks.size());
    -        boolean done = false;
    +        ArrayList> futures = new ArrayList<>(tasks.size());
             try {
                 for (Callable t : tasks) {
                     RunnableFuture f = newTaskFor(t);
    @@ -240,19 +244,15 @@
                 for (int i = 0, size = futures.size(); i < size; i++) {
                     Future f = futures.get(i);
                     if (!f.isDone()) {
    -                    try {
    -                        f.get();
    -                    } catch (CancellationException ignore) {
    -                    } catch (ExecutionException ignore) {
    -                    }
    +                    try { f.get(); }
    +                    catch (CancellationException ignore) {}
    +                    catch (ExecutionException ignore) {}
                     }
                 }
    -            done = true;
                 return futures;
    -        } finally {
    -            if (!done)
    -                for (int i = 0, size = futures.size(); i < size; i++)
    -                    futures.get(i).cancel(true);
    +        } catch (Throwable t) {
    +            cancelAll(futures);
    +            throw t;
             }
         }
     
    @@ -261,47 +261,52 @@
             throws InterruptedException {
             if (tasks == null)
                 throw new NullPointerException();
    -        long nanos = unit.toNanos(timeout);
    -        ArrayList> futures = new ArrayList>(tasks.size());
    -        boolean done = false;
    -        try {
    +        final long nanos = unit.toNanos(timeout);
    +        final long deadline = System.nanoTime() + nanos;
    +        ArrayList> futures = new ArrayList<>(tasks.size());
    +        int j = 0;
    +        timedOut: try {
                 for (Callable t : tasks)
                     futures.add(newTaskFor(t));
     
    -            final long deadline = System.nanoTime() + nanos;
                 final int size = futures.size();
     
                 // Interleave time checks and calls to execute in case
                 // executor doesn't have any/much parallelism.
                 for (int i = 0; i < size; i++) {
    +                if (((i == 0) ? nanos : deadline - System.nanoTime()) <= 0L)
    +                    break timedOut;
                     execute((Runnable)futures.get(i));
    -                nanos = deadline - System.nanoTime();
    -                if (nanos <= 0L)
    -                    return futures;
                 }
     
    -            for (int i = 0; i < size; i++) {
    -                Future f = futures.get(i);
    +            for (; j < size; j++) {
    +                Future f = futures.get(j);
                     if (!f.isDone()) {
    -                    if (nanos <= 0L)
    -                        return futures;
    -                    try {
    -                        f.get(nanos, TimeUnit.NANOSECONDS);
    -                    } catch (CancellationException ignore) {
    -                    } catch (ExecutionException ignore) {
    -                    } catch (TimeoutException toe) {
    -                        return futures;
    +                    try { f.get(deadline - System.nanoTime(), NANOSECONDS); }
    +                    catch (CancellationException ignore) {}
    +                    catch (ExecutionException ignore) {}
    +                    catch (TimeoutException timedOut) {
    +                        break timedOut;
                         }
    -                    nanos = deadline - System.nanoTime();
                     }
                 }
    -            done = true;
                 return futures;
    -        } finally {
    -            if (!done)
    -                for (int i = 0, size = futures.size(); i < size; i++)
    -                    futures.get(i).cancel(true);
    +        } catch (Throwable t) {
    +            cancelAll(futures);
    +            throw t;
             }
    +        // Timed out before all the tasks could be completed; cancel remaining
    +        cancelAll(futures, j);
    +        return futures;
         }
     
    +    private static  void cancelAll(ArrayList> futures) {
    +        cancelAll(futures, 0);
    +    }
    +
    +    /** Cancels all futures with index at least j. */
    +    private static  void cancelAll(ArrayList> futures, int j) {
    +        for (int size = futures.size(); j < size; j++)
    +            futures.get(j).cancel(true);
    +    }
     }
    diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/ArrayBlockingQueue.java
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/ArrayBlockingQueue.java	Wed Oct 21 15:15:26 2015 -0700
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ArrayBlockingQueue.java	Wed Jul 05 20:54:48 2017 +0200
    @@ -34,15 +34,18 @@
      */
     
     package java.util.concurrent;
    -import java.util.concurrent.locks.Condition;
    -import java.util.concurrent.locks.ReentrantLock;
    +
    +import java.lang.ref.WeakReference;
     import java.util.AbstractQueue;
    +import java.util.Arrays;
     import java.util.Collection;
     import java.util.Iterator;
     import java.util.NoSuchElementException;
    -import java.lang.ref.WeakReference;
    +import java.util.Objects;
    +import java.util.Spliterator;
     import java.util.Spliterators;
    -import java.util.Spliterator;
    +import java.util.concurrent.locks.Condition;
    +import java.util.concurrent.locks.ReentrantLock;
     
     /**
      * A bounded {@linkplain BlockingQueue blocking queue} backed by an
    @@ -77,7 +80,7 @@
      *
      * @since 1.5
      * @author Doug Lea
    - * @param  the type of elements held in this collection
    + * @param  the type of elements held in this queue
      */
     public class ArrayBlockingQueue extends AbstractQueue
             implements BlockingQueue, java.io.Serializable {
    @@ -121,12 +124,12 @@
          * are known not to be any.  Allows queue operations to update
          * iterator state.
          */
    -    transient Itrs itrs = null;
    +    transient Itrs itrs;
     
         // Internal helper methods
     
         /**
    -     * Circularly decrement i.
    +     * Circularly decrements array index i.
          */
         final int dec(int i) {
             return ((i == 0) ? items.length : i) - 1;
    @@ -141,16 +144,6 @@
         }
     
         /**
    -     * Throws NullPointerException if argument is null.
    -     *
    -     * @param v the element
    -     */
    -    private static void checkNotNull(Object v) {
    -        if (v == null)
    -            throw new NullPointerException();
    -    }
    -
    -    /**
          * Inserts element at current put position, advances, and signals.
          * Call only when holding lock.
          */
    @@ -159,8 +152,7 @@
             // assert items[putIndex] == null;
             final Object[] items = this.items;
             items[putIndex] = x;
    -        if (++putIndex == items.length)
    -            putIndex = 0;
    +        if (++putIndex == items.length) putIndex = 0;
             count++;
             notEmpty.signal();
         }
    @@ -176,8 +168,7 @@
             @SuppressWarnings("unchecked")
             E x = (E) items[takeIndex];
             items[takeIndex] = null;
    -        if (++takeIndex == items.length)
    -            takeIndex = 0;
    +        if (++takeIndex == items.length) takeIndex = 0;
             count--;
             if (itrs != null)
                 itrs.elementDequeued();
    @@ -198,8 +189,7 @@
             if (removeIndex == takeIndex) {
                 // removing front item; just advance
                 items[takeIndex] = null;
    -            if (++takeIndex == items.length)
    -                takeIndex = 0;
    +            if (++takeIndex == items.length) takeIndex = 0;
                 count--;
                 if (itrs != null)
                     itrs.elementDequeued();
    @@ -207,19 +197,15 @@
                 // an "interior" remove
     
                 // slide over all others up through putIndex.
    -            final int putIndex = this.putIndex;
    -            for (int i = removeIndex;;) {
    -                int next = i + 1;
    -                if (next == items.length)
    -                    next = 0;
    -                if (next != putIndex) {
    -                    items[i] = items[next];
    -                    i = next;
    -                } else {
    -                    items[i] = null;
    -                    this.putIndex = i;
    +            for (int i = removeIndex, putIndex = this.putIndex;;) {
    +                int pred = i;
    +                if (++i == items.length) i = 0;
    +                if (i == putIndex) {
    +                    items[pred] = null;
    +                    this.putIndex = pred;
                         break;
                     }
    +                items[pred] = items[i];
                 }
                 count--;
                 if (itrs != null)
    @@ -283,10 +269,8 @@
             try {
                 int i = 0;
                 try {
    -                for (E e : c) {
    -                    checkNotNull(e);
    -                    items[i++] = e;
    -                }
    +                for (E e : c)
    +                    items[i++] = Objects.requireNonNull(e);
                 } catch (ArrayIndexOutOfBoundsException ex) {
                     throw new IllegalArgumentException();
                 }
    @@ -322,7 +306,7 @@
          * @throws NullPointerException if the specified element is null
          */
         public boolean offer(E e) {
    -        checkNotNull(e);
    +        Objects.requireNonNull(e);
             final ReentrantLock lock = this.lock;
             lock.lock();
             try {
    @@ -345,7 +329,7 @@
          * @throws NullPointerException {@inheritDoc}
          */
         public void put(E e) throws InterruptedException {
    -        checkNotNull(e);
    +        Objects.requireNonNull(e);
             final ReentrantLock lock = this.lock;
             lock.lockInterruptibly();
             try {
    @@ -368,13 +352,13 @@
         public boolean offer(E e, long timeout, TimeUnit unit)
             throws InterruptedException {
     
    -        checkNotNull(e);
    +        Objects.requireNonNull(e);
             long nanos = unit.toNanos(timeout);
             final ReentrantLock lock = this.lock;
             lock.lockInterruptibly();
             try {
                 while (count == items.length) {
    -                if (nanos <= 0)
    +                if (nanos <= 0L)
                         return false;
                     nanos = notFull.awaitNanos(nanos);
                 }
    @@ -413,7 +397,7 @@
             lock.lockInterruptibly();
             try {
                 while (count == 0) {
    -                if (nanos <= 0)
    +                if (nanos <= 0L)
                         return null;
                     nanos = notEmpty.awaitNanos(nanos);
                 }
    @@ -492,11 +476,11 @@
          */
         public boolean remove(Object o) {
             if (o == null) return false;
    -        final Object[] items = this.items;
             final ReentrantLock lock = this.lock;
             lock.lock();
             try {
                 if (count > 0) {
    +                final Object[] items = this.items;
                     final int putIndex = this.putIndex;
                     int i = takeIndex;
                     do {
    @@ -504,8 +488,7 @@
                             removeAt(i);
                             return true;
                         }
    -                    if (++i == items.length)
    -                        i = 0;
    +                    if (++i == items.length) i = 0;
                     } while (i != putIndex);
                 }
                 return false;
    @@ -524,18 +507,17 @@
          */
         public boolean contains(Object o) {
             if (o == null) return false;
    -        final Object[] items = this.items;
             final ReentrantLock lock = this.lock;
             lock.lock();
             try {
                 if (count > 0) {
    +                final Object[] items = this.items;
                     final int putIndex = this.putIndex;
                     int i = takeIndex;
                     do {
                         if (o.equals(items[i]))
                             return true;
    -                    if (++i == items.length)
    -                        i = 0;
    +                    if (++i == items.length) i = 0;
                     } while (i != putIndex);
                 }
                 return false;
    @@ -558,23 +540,18 @@
          * @return an array containing all of the elements in this queue
          */
         public Object[] toArray() {
    -        Object[] a;
             final ReentrantLock lock = this.lock;
             lock.lock();
             try {
    -            final int count = this.count;
    -            a = new Object[count];
    -            int n = items.length - takeIndex;
    -            if (count <= n)
    -                System.arraycopy(items, takeIndex, a, 0, count);
    -            else {
    -                System.arraycopy(items, takeIndex, a, 0, n);
    -                System.arraycopy(items, 0, a, n, count - n);
    -            }
    +            final Object[] items = this.items;
    +            final int end = takeIndex + count;
    +            final Object[] a = Arrays.copyOfRange(items, takeIndex, end);
    +            if (end != putIndex)
    +                System.arraycopy(items, 0, a, items.length - takeIndex, putIndex);
    +            return a;
             } finally {
                 lock.unlock();
             }
    -        return a;
         }
     
         /**
    @@ -598,7 +575,7 @@
          * The following code can be used to dump the queue into a newly
          * allocated array of {@code String}:
          *
    -     *  
     {@code String[] y = x.toArray(new String[0]);}
    + *
     {@code String[] y = x.toArray(new String[0]);}
    * * Note that {@code toArray(new Object[0])} is identical in function to * {@code toArray()}. @@ -614,53 +591,30 @@ */ @SuppressWarnings("unchecked") public T[] toArray(T[] a) { - final Object[] items = this.items; final ReentrantLock lock = this.lock; lock.lock(); try { + final Object[] items = this.items; final int count = this.count; - final int len = a.length; - if (len < count) - a = (T[])java.lang.reflect.Array.newInstance( - a.getClass().getComponentType(), count); - int n = items.length - takeIndex; - if (count <= n) - System.arraycopy(items, takeIndex, a, 0, count); - else { - System.arraycopy(items, takeIndex, a, 0, n); - System.arraycopy(items, 0, a, n, count - n); + final int firstLeg = Math.min(items.length - takeIndex, count); + if (a.length < count) { + a = (T[]) Arrays.copyOfRange(items, takeIndex, takeIndex + count, + a.getClass()); + } else { + System.arraycopy(items, takeIndex, a, 0, firstLeg); + if (a.length > count) + a[count] = null; } - if (len > count) - a[count] = null; + if (firstLeg < count) + System.arraycopy(items, 0, a, firstLeg, putIndex); + return a; } finally { lock.unlock(); } - return a; } public String toString() { - final ReentrantLock lock = this.lock; - lock.lock(); - try { - int k = count; - if (k == 0) - return "[]"; - - final Object[] items = this.items; - StringBuilder sb = new StringBuilder(); - sb.append('['); - for (int i = takeIndex; ; ) { - Object e = items[i]; - sb.append(e == this ? "(this Collection)" : e); - if (--k == 0) - return sb.append(']').toString(); - sb.append(',').append(' '); - if (++i == items.length) - i = 0; - } - } finally { - lock.unlock(); - } + return Helpers.collectionToString(this); } /** @@ -668,18 +622,17 @@ * The queue will be empty after this call returns. */ public void clear() { - final Object[] items = this.items; final ReentrantLock lock = this.lock; lock.lock(); try { int k = count; if (k > 0) { + final Object[] items = this.items; final int putIndex = this.putIndex; int i = takeIndex; do { items[i] = null; - if (++i == items.length) - i = 0; + if (++i == items.length) i = 0; } while (i != putIndex); takeIndex = putIndex; count = 0; @@ -710,7 +663,7 @@ * @throws IllegalArgumentException {@inheritDoc} */ public int drainTo(Collection c, int maxElements) { - checkNotNull(c); + Objects.requireNonNull(c); if (c == this) throw new IllegalArgumentException(); if (maxElements <= 0) @@ -728,8 +681,7 @@ E x = (E) items[take]; c.add(x); items[take] = null; - if (++take == items.length) - take = 0; + if (++take == items.length) take = 0; i++; } return n; @@ -832,13 +784,13 @@ } /** Incremented whenever takeIndex wraps around to 0 */ - int cycles = 0; + int cycles; /** Linked list of weak iterator references */ private Node head; /** Used to expunge stale iterators */ - private Node sweeper = null; + private Node sweeper; private static final int SHORT_SWEEP_PROBES = 4; private static final int LONG_SWEEP_PROBES = 16; @@ -1095,10 +1047,8 @@ private int incCursor(int index) { // assert lock.getHoldCount() == 1; - if (++index == items.length) - index = 0; - if (index == putIndex) - index = NONE; + if (++index == items.length) index = 0; + if (index == putIndex) index = NONE; return index; } @@ -1314,17 +1264,18 @@ if (isDetached()) return true; - final int cycles = itrs.cycles; final int takeIndex = ArrayBlockingQueue.this.takeIndex; - final int prevCycles = this.prevCycles; final int prevTakeIndex = this.prevTakeIndex; final int len = items.length; - int cycleDiff = cycles - prevCycles; - if (removedIndex < takeIndex) - cycleDiff++; + // distance from prevTakeIndex to removedIndex final int removedDistance = - (cycleDiff * len) + (removedIndex - prevTakeIndex); - // assert removedDistance >= 0; + len * (itrs.cycles - this.prevCycles + + ((removedIndex < takeIndex) ? 1 : 0)) + + (removedIndex - prevTakeIndex); + // assert itrs.cycles - this.prevCycles >= 0; + // assert itrs.cycles - this.prevCycles <= 1; + // assert removedDistance > 0; + // assert removedIndex != takeIndex; int cursor = this.cursor; if (cursor >= 0) { int x = distance(cursor, prevTakeIndex, len); @@ -1353,7 +1304,7 @@ else if (x > removedDistance) this.nextIndex = nextIndex = dec(nextIndex); } - else if (cursor < 0 && nextIndex < 0 && lastRet < 0) { + if (cursor < 0 && nextIndex < 0 && lastRet < 0) { this.prevTakeIndex = DETACHED; return true; } @@ -1410,8 +1361,9 @@ */ public Spliterator spliterator() { return Spliterators.spliterator - (this, Spliterator.ORDERED | Spliterator.NONNULL | - Spliterator.CONCURRENT); + (this, (Spliterator.ORDERED | + Spliterator.NONNULL | + Spliterator.CONCURRENT)); } } diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/BlockingDeque.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/BlockingDeque.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/BlockingDeque.java Wed Jul 05 20:54:48 2017 +0200 @@ -34,7 +34,10 @@ */ package java.util.concurrent; -import java.util.*; + +import java.util.Deque; +import java.util.Iterator; +import java.util.NoSuchElementException; /** * A {@link Deque} that additionally supports blocking operations that wait @@ -195,7 +198,7 @@ * * @since 1.6 * @author Doug Lea - * @param the type of elements held in this collection + * @param the type of elements held in this deque */ public interface BlockingDeque extends BlockingQueue, Deque { /* @@ -401,9 +404,9 @@ * @return {@code true} if an element was removed as a result of this call * @throws ClassCastException if the class of the specified element * is incompatible with this deque - * (
    optional) + * (optional) * @throws NullPointerException if the specified element is null - * (optional) + * (optional) */ boolean removeFirstOccurrence(Object o); @@ -419,9 +422,9 @@ * @return {@code true} if an element was removed as a result of this call * @throws ClassCastException if the class of the specified element * is incompatible with this deque - * (optional) + * (optional) * @throws NullPointerException if the specified element is null - * (optional) + * (optional) */ boolean removeLastOccurrence(Object o); @@ -596,9 +599,9 @@ * @return {@code true} if this deque changed as a result of the call * @throws ClassCastException if the class of the specified element * is incompatible with this deque - * (optional) + * (optional) * @throws NullPointerException if the specified element is null - * (optional) + * (optional) */ boolean remove(Object o); @@ -611,18 +614,18 @@ * @return {@code true} if this deque contains the specified element * @throws ClassCastException if the class of the specified element * is incompatible with this deque - * (optional) + * (optional) * @throws NullPointerException if the specified element is null - * (optional) + * (optional) */ - public boolean contains(Object o); + boolean contains(Object o); /** * Returns the number of elements in this deque. * * @return the number of elements in this deque */ - public int size(); + int size(); /** * Returns an iterator over the elements in this deque in proper sequence. diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/BlockingQueue.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/BlockingQueue.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/BlockingQueue.java Wed Jul 05 20:54:48 2017 +0200 @@ -127,7 +127,7 @@ * Usage example, based on a typical producer-consumer scenario. * Note that a {@code BlockingQueue} can safely be used with multiple * producers and multiple consumers. - *
     {@code
    + * 
     {@code
      * class Producer implements Runnable {
      *   private final BlockingQueue queue;
      *   Producer(BlockingQueue q) { queue = q; }
    @@ -175,7 +175,7 @@
      *
      * @since 1.5
      * @author Doug Lea
    - * @param  the type of elements held in this collection
    + * @param  the type of elements held in this queue
      */
     public interface BlockingQueue extends Queue {
         /**
    @@ -303,9 +303,9 @@
          * @return {@code true} if this queue changed as a result of the call
          * @throws ClassCastException if the class of the specified element
          *         is incompatible with this queue
    -     *         (optional)
    +     * (optional)
          * @throws NullPointerException if the specified element is null
    -     *         (optional)
    +     * (optional)
          */
         boolean remove(Object o);
     
    @@ -318,11 +318,11 @@
          * @return {@code true} if this queue contains the specified element
          * @throws ClassCastException if the class of the specified element
          *         is incompatible with this queue
    -     *         (optional)
    +     * (optional)
          * @throws NullPointerException if the specified element is null
    -     *         (optional)
    +     * (optional)
          */
    -    public boolean contains(Object o);
    +    boolean contains(Object o);
     
         /**
          * Removes all available elements from this queue and adds them
    diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java	Wed Oct 21 15:15:26 2015 -0700
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java	Wed Jul 05 20:54:48 2017 +0200
    @@ -34,23 +34,13 @@
      */
     
     package java.util.concurrent;
    -import java.util.function.Supplier;
    -import java.util.function.Consumer;
    +
    +import java.util.concurrent.locks.LockSupport;
     import java.util.function.BiConsumer;
    -import java.util.function.Function;
     import java.util.function.BiFunction;
    -import java.util.concurrent.Future;
    -import java.util.concurrent.TimeUnit;
    -import java.util.concurrent.ForkJoinPool;
    -import java.util.concurrent.ForkJoinTask;
    -import java.util.concurrent.Executor;
    -import java.util.concurrent.ThreadLocalRandom;
    -import java.util.concurrent.ExecutionException;
    -import java.util.concurrent.TimeoutException;
    -import java.util.concurrent.CancellationException;
    -import java.util.concurrent.CompletionException;
    -import java.util.concurrent.CompletionStage;
    -import java.util.concurrent.locks.LockSupport;
    +import java.util.function.Consumer;
    +import java.util.function.Function;
    +import java.util.function.Supplier;
     
     /**
      * A {@link Future} that may be explicitly completed (setting its
    @@ -71,19 +61,32 @@
      * 
  • Actions supplied for dependent completions of * non-async methods may be performed by the thread that * completes the current CompletableFuture, or by any other caller of - * a completion method.
  • + * a completion method. * *
  • All async methods without an explicit Executor * argument are performed using the {@link ForkJoinPool#commonPool()} * (unless it does not support a parallelism level of at least two, in - * which case, a new Thread is created to run each task). To simplify - * monitoring, debugging, and tracking, all generated asynchronous - * tasks are instances of the marker interface {@link - * AsynchronousCompletionTask}.
  • + * which case, a new Thread is created to run each task). This may be + * overridden for non-static methods in subclasses by defining method + * {@link #defaultExecutor()}. To simplify monitoring, debugging, + * and tracking, all generated asynchronous tasks are instances of the + * marker interface {@link AsynchronousCompletionTask}. Operations + * with time-delays can use adapter methods defined in this class, for + * example: {@code supplyAsync(supplier, delayedExecutor(timeout, + * timeUnit))}. To support methods with delays and timeouts, this + * class maintains at most one daemon thread for triggering and + * cancelling actions, not for running them. * *
  • All CompletionStage methods are implemented independently of * other public methods, so the behavior of one method is not impacted - * by overrides of others in subclasses.
  • + * by overrides of others in subclasses. + * + *
  • All CompletionStage methods return CompletableFutures. To + * restrict usages to only those methods defined in interface + * CompletionStage, use method {@link #minimalCompletionStage}. Or to + * ensure only that clients do not themselves modify a future, use + * method {@link #copy}. + * * *

    CompletableFuture also implements {@link Future} with the following * policies:

      @@ -94,7 +97,7 @@ * completion. Method {@link #cancel cancel} has the same effect as * {@code completeExceptionally(new CancellationException())}. Method * {@link #isCompletedExceptionally} can be used to determine if a - * CompletableFuture completed in any exceptional fashion. + * CompletableFuture completed in any exceptional fashion. * *
    • In case of exceptional completion with a CompletionException, * methods {@link #get()} and {@link #get(long, TimeUnit)} throw an @@ -102,10 +105,38 @@ * corresponding CompletionException. To simplify usage in most * contexts, this class also defines methods {@link #join()} and * {@link #getNow} that instead throw the CompletionException directly - * in these cases.
    + * in these cases. + * + * + *

    Arguments used to pass a completion result (that is, for + * parameters of type {@code T}) for methods accepting them may be + * null, but passing a null value for any other parameter will result + * in a {@link NullPointerException} being thrown. + * + *

    Subclasses of this class should normally override the "virtual + * constructor" method {@link #newIncompleteFuture}, which establishes + * the concrete type returned by CompletionStage methods. For example, + * here is a class that substitutes a different default Executor and + * disables the {@code obtrude} methods: + * + *

     {@code
    + * class MyCompletableFuture extends CompletableFuture {
    + *   static final Executor myExecutor = ...;
    + *   public MyCompletableFuture() { }
    + *   public  CompletableFuture newIncompleteFuture() {
    + *     return new MyCompletableFuture(); }
    + *   public Executor defaultExecutor() {
    + *     return myExecutor; }
    + *   public void obtrudeValue(T value) {
    + *     throw new UnsupportedOperationException(); }
    + *   public void obtrudeException(Throwable ex) {
    + *     throw new UnsupportedOperationException(); }
    + * }}
    * * @author Doug Lea * @since 1.8 + * @param The result type returned by this future's {@code join} + * and {@code get} methods */ public class CompletableFuture implements Future, CompletionStage { @@ -150,9 +181,7 @@ * fields for source(s), actions, and dependent. They are * boringly similar, differing from others only with respect to * underlying functional forms. We do this so that users don't - * encounter layers of adaptors in common usages. We also - * include "Relay" classes/methods that don't correspond to user - * methods; they copy results from one stage to another. + * encounter layers of adapters in common usages. * * * Boolean CompletableFuture method x(...) (for example * uniApply) takes all of the arguments needed to check that an @@ -219,18 +248,18 @@ volatile Completion stack; // Top of Treiber stack of dependent actions final boolean internalComplete(Object r) { // CAS from null to r - return UNSAFE.compareAndSwapObject(this, RESULT, null, r); + return U.compareAndSwapObject(this, RESULT, null, r); } final boolean casStack(Completion cmp, Completion val) { - return UNSAFE.compareAndSwapObject(this, STACK, cmp, val); + return U.compareAndSwapObject(this, STACK, cmp, val); } /** Returns true if successfully pushed c onto stack. */ final boolean tryPushStack(Completion c) { Completion h = stack; lazySetNext(c, h); - return UNSAFE.compareAndSwapObject(this, STACK, h, c); + return U.compareAndSwapObject(this, STACK, h, c); } /** Unconditionally pushes c onto stack, retrying if necessary. */ @@ -250,8 +279,8 @@ /** Completes with the null value, unless already completed. */ final boolean completeNull() { - return UNSAFE.compareAndSwapObject(this, RESULT, null, - NIL); + return U.compareAndSwapObject(this, RESULT, null, + NIL); } /** Returns the encoding of the given non-exceptional value. */ @@ -261,8 +290,8 @@ /** Completes with a non-exceptional result, unless already completed. */ final boolean completeValue(T t) { - return UNSAFE.compareAndSwapObject(this, RESULT, null, - (t == null) ? NIL : t); + return U.compareAndSwapObject(this, RESULT, null, + (t == null) ? NIL : t); } /** @@ -276,8 +305,8 @@ /** Completes with an exceptional result, unless already completed. */ final boolean completeThrowable(Throwable x) { - return UNSAFE.compareAndSwapObject(this, RESULT, null, - encodeThrowable(x)); + return U.compareAndSwapObject(this, RESULT, null, + encodeThrowable(x)); } /** @@ -304,8 +333,8 @@ * existing CompletionException. */ final boolean completeThrowable(Throwable x, Object r) { - return UNSAFE.compareAndSwapObject(this, RESULT, null, - encodeThrowable(x, r)); + return U.compareAndSwapObject(this, RESULT, null, + encodeThrowable(x, r)); } /** @@ -334,8 +363,8 @@ * If exceptional, r is first coerced to a CompletionException. */ final boolean completeRelay(Object r) { - return UNSAFE.compareAndSwapObject(this, RESULT, null, - encodeRelay(r)); + return U.compareAndSwapObject(this, RESULT, null, + encodeRelay(r)); } /** @@ -390,14 +419,14 @@ public static interface AsynchronousCompletionTask { } - private static final boolean useCommonPool = + private static final boolean USE_COMMON_POOL = (ForkJoinPool.getCommonPoolParallelism() > 1); /** * Default executor -- ForkJoinPool.commonPool() unless it cannot * support parallelism. */ - private static final Executor asyncPool = useCommonPool ? + private static final Executor ASYNC_POOL = USE_COMMON_POOL ? ForkJoinPool.commonPool() : new ThreadPerTaskExecutor(); /** Fallback if ForkJoinPool.commonPool() cannot support parallelism */ @@ -407,11 +436,11 @@ /** * Null-checks user executor argument, and translates uses of - * commonPool to asyncPool in case parallelism disabled. + * commonPool to ASYNC_POOL in case parallelism disabled. */ static Executor screenExecutor(Executor e) { - if (!useCommonPool && e == ForkJoinPool.commonPool()) - return asyncPool; + if (!USE_COMMON_POOL && e == ForkJoinPool.commonPool()) + return ASYNC_POOL; if (e == null) throw new NullPointerException(); return e; } @@ -421,6 +450,12 @@ static final int ASYNC = 1; static final int NESTED = -1; + /** + * Spins before blocking in waitingGet + */ + static final int SPINS = (Runtime.getRuntime().availableProcessors() > 1 ? + 1 << 8 : 0); + /* ------------- Base Completion classes and operations -------------- */ @SuppressWarnings("serial") @@ -440,13 +475,13 @@ abstract boolean isLive(); public final void run() { tryFire(ASYNC); } - public final boolean exec() { tryFire(ASYNC); return true; } + public final boolean exec() { tryFire(ASYNC); return false; } public final Void getRawResult() { return null; } public final void setRawResult(Void v) {} } static void lazySetNext(Completion c, Completion next) { - UNSAFE.putOrderedObject(c, NEXT, next); + U.putOrderedObject(c, NEXT, next); } /** @@ -610,7 +645,7 @@ private CompletableFuture uniApplyStage( Executor e, Function f) { if (f == null) throw new NullPointerException(); - CompletableFuture d = new CompletableFuture(); + CompletableFuture d = newIncompleteFuture(); if (e != null || !d.uniApply(this, f, null)) { UniApply c = new UniApply(e, d, this, f); push(c); @@ -665,7 +700,7 @@ private CompletableFuture uniAcceptStage(Executor e, Consumer f) { if (f == null) throw new NullPointerException(); - CompletableFuture d = new CompletableFuture(); + CompletableFuture d = newIncompleteFuture(); if (e != null || !d.uniAccept(this, f, null)) { UniAccept c = new UniAccept(e, d, this, f); push(c); @@ -713,7 +748,7 @@ private CompletableFuture uniRunStage(Executor e, Runnable f) { if (f == null) throw new NullPointerException(); - CompletableFuture d = new CompletableFuture(); + CompletableFuture d = newIncompleteFuture(); if (e != null || !d.uniRun(this, f, null)) { UniRun c = new UniRun(e, d, this, f); push(c); @@ -774,7 +809,7 @@ private CompletableFuture uniWhenCompleteStage( Executor e, BiConsumer f) { if (f == null) throw new NullPointerException(); - CompletableFuture d = new CompletableFuture(); + CompletableFuture d = newIncompleteFuture(); if (e != null || !d.uniWhenComplete(this, f, null)) { UniWhenComplete c = new UniWhenComplete(e, d, this, f); push(c); @@ -830,7 +865,7 @@ private CompletableFuture uniHandleStage( Executor e, BiFunction f) { if (f == null) throw new NullPointerException(); - CompletableFuture d = new CompletableFuture(); + CompletableFuture d = newIncompleteFuture(); if (e != null || !d.uniHandle(this, f, null)) { UniHandle c = new UniHandle(e, d, this, f); push(c); @@ -880,7 +915,7 @@ private CompletableFuture uniExceptionallyStage( Function f) { if (f == null) throw new NullPointerException(); - CompletableFuture d = new CompletableFuture(); + CompletableFuture d = newIncompleteFuture(); if (!d.uniExceptionally(this, f, null)) { UniExceptionally c = new UniExceptionally(d, this, f); push(c); @@ -912,6 +947,30 @@ return true; } + private CompletableFuture uniCopyStage() { + Object r; + CompletableFuture d = newIncompleteFuture(); + if ((r = result) != null) + d.completeRelay(r); + else { + UniRelay c = new UniRelay(d, this); + push(c); + c.tryFire(SYNC); + } + return d; + } + + private MinimalStage uniAsMinimalStage() { + Object r; + if ((r = result) != null) + return new MinimalStage(encodeRelay(r)); + MinimalStage d = new MinimalStage(); + UniRelay c = new UniRelay(d, this); + push(c); + c.tryFire(SYNC); + return d; + } + @SuppressWarnings("serial") static final class UniCompose extends UniCompletion { Function> fn; @@ -967,31 +1026,32 @@ private CompletableFuture uniComposeStage( Executor e, Function> f) { if (f == null) throw new NullPointerException(); - Object r; Throwable x; + Object r, s; Throwable x; + CompletableFuture d = newIncompleteFuture(); if (e == null && (r = result) != null) { - // try to return function result directly if (r instanceof AltResult) { if ((x = ((AltResult)r).ex) != null) { - return new CompletableFuture(encodeThrowable(x, r)); + d.result = encodeThrowable(x, r); + return d; } r = null; } try { @SuppressWarnings("unchecked") T t = (T) r; CompletableFuture g = f.apply(t).toCompletableFuture(); - Object s = g.result; - if (s != null) - return new CompletableFuture(encodeRelay(s)); - CompletableFuture d = new CompletableFuture(); - UniRelay copy = new UniRelay(d, g); - g.push(copy); - copy.tryFire(SYNC); + if ((s = g.result) != null) + d.completeRelay(s); + else { + UniRelay c = new UniRelay(d, g); + g.push(c); + c.tryFire(SYNC); + } return d; } catch (Throwable ex) { - return new CompletableFuture(encodeThrowable(ex)); + d.result = encodeThrowable(ex); + return d; } } - CompletableFuture d = new CompletableFuture(); UniCompose c = new UniCompose(e, d, this, f); push(c); c.tryFire(SYNC); @@ -1116,7 +1176,7 @@ CompletableFuture b; if (f == null || (b = o.toCompletableFuture()) == null) throw new NullPointerException(); - CompletableFuture d = new CompletableFuture(); + CompletableFuture d = newIncompleteFuture(); if (e != null || !d.biApply(this, b, f, null)) { BiApply c = new BiApply(e, d, this, b, f); bipush(b, c); @@ -1188,7 +1248,7 @@ CompletableFuture b; if (f == null || (b = o.toCompletableFuture()) == null) throw new NullPointerException(); - CompletableFuture d = new CompletableFuture(); + CompletableFuture d = newIncompleteFuture(); if (e != null || !d.biAccept(this, b, f, null)) { BiAccept c = new BiAccept(e, d, this, b, f); bipush(b, c); @@ -1247,7 +1307,7 @@ CompletableFuture b; if (f == null || (b = o.toCompletableFuture()) == null) throw new NullPointerException(); - CompletableFuture d = new CompletableFuture(); + CompletableFuture d = newIncompleteFuture(); if (e != null || !d.biRun(this, b, f, null)) { BiRun c = new BiRun<>(e, d, this, b, f); bipush(b, c); @@ -1302,7 +1362,7 @@ if ((a = (lo == mid ? cfs[lo] : andTree(cfs, lo, mid))) == null || (b = (lo == hi ? a : (hi == mid+1) ? cfs[hi] : - andTree(cfs, mid+1, hi))) == null) + andTree(cfs, mid+1, hi))) == null) throw new NullPointerException(); if (!d.biRelay(a, b)) { BiRelay c = new BiRelay<>(d, a, b); @@ -1388,7 +1448,7 @@ CompletableFuture b; if (f == null || (b = o.toCompletableFuture()) == null) throw new NullPointerException(); - CompletableFuture d = new CompletableFuture(); + CompletableFuture d = newIncompleteFuture(); if (e != null || !d.orApply(this, b, f, null)) { OrApply c = new OrApply(e, d, this, b, f); orpush(b, c); @@ -1452,7 +1512,7 @@ CompletableFuture b; if (f == null || (b = o.toCompletableFuture()) == null) throw new NullPointerException(); - CompletableFuture d = new CompletableFuture(); + CompletableFuture d = newIncompleteFuture(); if (e != null || !d.orAccept(this, b, f, null)) { OrAccept c = new OrAccept(e, d, this, b, f); orpush(b, c); @@ -1510,7 +1570,7 @@ CompletableFuture b; if (f == null || (b = o.toCompletableFuture()) == null) throw new NullPointerException(); - CompletableFuture d = new CompletableFuture(); + CompletableFuture d = newIncompleteFuture(); if (e != null || !d.orRun(this, b, f, null)) { OrRun c = new OrRun<>(e, d, this, b, f); orpush(b, c); @@ -1556,7 +1616,7 @@ if ((a = (lo == mid ? cfs[lo] : orTree(cfs, lo, mid))) == null || (b = (lo == hi ? a : (hi == mid+1) ? cfs[hi] : - orTree(cfs, mid+1, hi))) == null) + orTree(cfs, mid+1, hi))) == null) throw new NullPointerException(); if (!d.orRelay(a, b)) { OrRelay c = new OrRelay<>(d, a, b); @@ -1571,9 +1631,9 @@ @SuppressWarnings("serial") static final class AsyncSupply extends ForkJoinTask - implements Runnable, AsynchronousCompletionTask { - CompletableFuture dep; Supplier fn; - AsyncSupply(CompletableFuture dep, Supplier fn) { + implements Runnable, AsynchronousCompletionTask { + CompletableFuture dep; Supplier fn; + AsyncSupply(CompletableFuture dep, Supplier fn) { this.dep = dep; this.fn = fn; } @@ -1582,7 +1642,7 @@ public final boolean exec() { run(); return true; } public void run() { - CompletableFuture d; Supplier f; + CompletableFuture d; Supplier f; if ((d = dep) != null && (f = fn) != null) { dep = null; fn = null; if (d.result == null) { @@ -1607,7 +1667,7 @@ @SuppressWarnings("serial") static final class AsyncRun extends ForkJoinTask - implements Runnable, AsynchronousCompletionTask { + implements Runnable, AsynchronousCompletionTask { CompletableFuture dep; Runnable fn; AsyncRun(CompletableFuture dep, Runnable fn) { this.dep = dep; this.fn = fn; @@ -1651,14 +1711,15 @@ @SuppressWarnings("serial") static final class Signaller extends Completion implements ForkJoinPool.ManagedBlocker { - long nanos; // wait time if timed + long nanos; // remaining wait time if timed final long deadline; // non-zero if timed - volatile int interruptControl; // > 0: interruptible, < 0: interrupted + final boolean interruptible; + boolean interrupted; volatile Thread thread; Signaller(boolean interruptible, long nanos, long deadline) { this.thread = Thread.currentThread(); - this.interruptControl = interruptible ? 1 : 0; + this.interruptible = interruptible; this.nanos = nanos; this.deadline = deadline; } @@ -1671,29 +1732,22 @@ return null; } public boolean isReleasable() { - if (thread == null) - return true; - if (Thread.interrupted()) { - int i = interruptControl; - interruptControl = -1; - if (i > 0) - return true; - } - if (deadline != 0L && - (nanos <= 0L || (nanos = deadline - System.nanoTime()) <= 0L)) { - thread = null; - return true; - } - return false; + if (Thread.interrupted()) + interrupted = true; + return ((interrupted && interruptible) || + (deadline != 0L && + (nanos <= 0L || + (nanos = deadline - System.nanoTime()) <= 0L)) || + thread == null); } public boolean block() { - if (isReleasable()) - return true; - else if (deadline == 0L) - LockSupport.park(this); - else if (nanos > 0L) - LockSupport.parkNanos(this, nanos); - return isReleasable(); + while (!isReleasable()) { + if (deadline == 0L) + LockSupport.park(this); + else + LockSupport.parkNanos(this, nanos); + } + return true; } final boolean isLive() { return thread != null; } } @@ -1705,13 +1759,10 @@ private Object waitingGet(boolean interruptible) { Signaller q = null; boolean queued = false; - int spins = -1; + int spins = SPINS; Object r; while ((r = result) == null) { - if (spins < 0) - spins = (Runtime.getRuntime().availableProcessors() > 1) ? - 1 << 8 : 0; // Use brief spin-wait on multiprocessors - else if (spins > 0) { + if (spins > 0) { if (ThreadLocalRandom.nextSecondarySeed() >= 0) --spins; } @@ -1719,29 +1770,27 @@ q = new Signaller(interruptible, 0L, 0L); else if (!queued) queued = tryPushStack(q); - else if (interruptible && q.interruptControl < 0) { - q.thread = null; - cleanStack(); - return null; - } - else if (q.thread != null && result == null) { + else { try { ForkJoinPool.managedBlock(q); - } catch (InterruptedException ie) { - q.interruptControl = -1; + } catch (InterruptedException ie) { // currently cannot happen + q.interrupted = true; } + if (q.interrupted && interruptible) + break; } } if (q != null) { q.thread = null; - if (q.interruptControl < 0) { + if (q.interrupted) { if (interruptible) - r = null; // report interruption + cleanStack(); else Thread.currentThread().interrupt(); } } - postComplete(); + if (r != null) + postComplete(); return r; } @@ -1752,37 +1801,39 @@ private Object timedGet(long nanos) throws TimeoutException { if (Thread.interrupted()) return null; - if (nanos <= 0L) - throw new TimeoutException(); - long d = System.nanoTime() + nanos; - Signaller q = new Signaller(true, nanos, d == 0L ? 1L : d); // avoid 0 - boolean queued = false; - Object r; - // We intentionally don't spin here (as waitingGet does) because - // the call to nanoTime() above acts much like a spin. - while ((r = result) == null) { - if (!queued) - queued = tryPushStack(q); - else if (q.interruptControl < 0 || q.nanos <= 0L) { - q.thread = null; - cleanStack(); - if (q.interruptControl < 0) - return null; - throw new TimeoutException(); - } - else if (q.thread != null && result == null) { - try { - ForkJoinPool.managedBlock(q); - } catch (InterruptedException ie) { - q.interruptControl = -1; + if (nanos > 0L) { + long d = System.nanoTime() + nanos; + long deadline = (d == 0L) ? 1L : d; // avoid 0 + Signaller q = null; + boolean queued = false; + Object r; + while ((r = result) == null) { // similar to untimed, without spins + if (q == null) + q = new Signaller(true, nanos, deadline); + else if (!queued) + queued = tryPushStack(q); + else if (q.nanos <= 0L) + break; + else { + try { + ForkJoinPool.managedBlock(q); + } catch (InterruptedException ie) { + q.interrupted = true; + } + if (q.interrupted) + break; } } + if (q != null) + q.thread = null; + if (r != null) + postComplete(); + else + cleanStack(); + if (r != null || (q != null && q.interrupted)) + return r; } - if (q.interruptControl < 0) - r = null; - q.thread = null; - postComplete(); - return r; + throw new TimeoutException(); } /* ------------- public methods -------------- */ @@ -1796,7 +1847,7 @@ /** * Creates a new complete CompletableFuture with given encoded result. */ - private CompletableFuture(Object r) { + CompletableFuture(Object r) { this.result = r; } @@ -1811,7 +1862,7 @@ * @return the new CompletableFuture */ public static CompletableFuture supplyAsync(Supplier supplier) { - return asyncSupplyStage(asyncPool, supplier); + return asyncSupplyStage(ASYNC_POOL, supplier); } /** @@ -1840,7 +1891,7 @@ * @return the new CompletableFuture */ public static CompletableFuture runAsync(Runnable runnable) { - return asyncRunStage(asyncPool, runnable); + return asyncRunStage(ASYNC_POOL, runnable); } /** @@ -1985,7 +2036,7 @@ public CompletableFuture thenApplyAsync( Function fn) { - return uniApplyStage(asyncPool, fn); + return uniApplyStage(defaultExecutor(), fn); } public CompletableFuture thenApplyAsync( @@ -1998,7 +2049,7 @@ } public CompletableFuture thenAcceptAsync(Consumer action) { - return uniAcceptStage(asyncPool, action); + return uniAcceptStage(defaultExecutor(), action); } public CompletableFuture thenAcceptAsync(Consumer action, @@ -2011,7 +2062,7 @@ } public CompletableFuture thenRunAsync(Runnable action) { - return uniRunStage(asyncPool, action); + return uniRunStage(defaultExecutor(), action); } public CompletableFuture thenRunAsync(Runnable action, @@ -2028,7 +2079,7 @@ public CompletableFuture thenCombineAsync( CompletionStage other, BiFunction fn) { - return biApplyStage(asyncPool, other, fn); + return biApplyStage(defaultExecutor(), other, fn); } public CompletableFuture thenCombineAsync( @@ -2046,7 +2097,7 @@ public CompletableFuture thenAcceptBothAsync( CompletionStage other, BiConsumer action) { - return biAcceptStage(asyncPool, other, action); + return biAcceptStage(defaultExecutor(), other, action); } public CompletableFuture thenAcceptBothAsync( @@ -2062,7 +2113,7 @@ public CompletableFuture runAfterBothAsync(CompletionStage other, Runnable action) { - return biRunStage(asyncPool, other, action); + return biRunStage(defaultExecutor(), other, action); } public CompletableFuture runAfterBothAsync(CompletionStage other, @@ -2078,7 +2129,7 @@ public CompletableFuture applyToEitherAsync( CompletionStage other, Function fn) { - return orApplyStage(asyncPool, other, fn); + return orApplyStage(defaultExecutor(), other, fn); } public CompletableFuture applyToEitherAsync( @@ -2094,7 +2145,7 @@ public CompletableFuture acceptEitherAsync( CompletionStage other, Consumer action) { - return orAcceptStage(asyncPool, other, action); + return orAcceptStage(defaultExecutor(), other, action); } public CompletableFuture acceptEitherAsync( @@ -2110,7 +2161,7 @@ public CompletableFuture runAfterEitherAsync(CompletionStage other, Runnable action) { - return orRunStage(asyncPool, other, action); + return orRunStage(defaultExecutor(), other, action); } public CompletableFuture runAfterEitherAsync(CompletionStage other, @@ -2126,7 +2177,7 @@ public CompletableFuture thenComposeAsync( Function> fn) { - return uniComposeStage(asyncPool, fn); + return uniComposeStage(defaultExecutor(), fn); } public CompletableFuture thenComposeAsync( @@ -2142,7 +2193,7 @@ public CompletableFuture whenCompleteAsync( BiConsumer action) { - return uniWhenCompleteStage(asyncPool, action); + return uniWhenCompleteStage(defaultExecutor(), action); } public CompletableFuture whenCompleteAsync( @@ -2157,7 +2208,7 @@ public CompletableFuture handleAsync( BiFunction fn) { - return uniHandleStage(asyncPool, fn); + return uniHandleStage(defaultExecutor(), fn); } public CompletableFuture handleAsync( @@ -2196,6 +2247,7 @@ return uniExceptionallyStage(fn); } + /* ------------- Arbitrary-arity constructions -------------- */ /** @@ -2353,10 +2405,12 @@ */ public String toString() { Object r = result; - int count; + int count = 0; // avoid call to getNumberOfDependents in case disabled + for (Completion p = stack; p != null; p = p.next) + ++count; return super.toString() + ((r == null) ? - (((count = getNumberOfDependents()) == 0) ? + ((count == 0) ? "[Not completed]" : "[Not completed, " + count + " dependents]") : (((r instanceof AltResult) && ((AltResult)r).ex != null) ? @@ -2364,22 +2418,381 @@ "[Completed normally]")); } + // jdk9 additions + + /** + * Returns a new incomplete CompletableFuture of the type to be + * returned by a CompletionStage method. Subclasses should + * normally override this method to return an instance of the same + * class as this CompletableFuture. The default implementation + * returns an instance of class CompletableFuture. + * + * @param the type of the value + * @return a new CompletableFuture + * @since 1.9 + */ + public CompletableFuture newIncompleteFuture() { + return new CompletableFuture(); + } + + /** + * Returns the default Executor used for async methods that do not + * specify an Executor. This class uses the {@link + * ForkJoinPool#commonPool()} if it supports more than one + * parallel thread, or else an Executor using one thread per async + * task. This method may be overridden in subclasses to return + * an Executor that provides at least one independent thread. + * + * @return the executor + * @since 1.9 + */ + public Executor defaultExecutor() { + return ASYNC_POOL; + } + + /** + * Returns a new CompletableFuture that is completed normally with + * the same value as this CompletableFuture when it completes + * normally. If this CompletableFuture completes exceptionally, + * then the returned CompletableFuture completes exceptionally + * with a CompletionException with this exception as cause. The + * behavior is equivalent to {@code thenApply(x -> x)}. This + * method may be useful as a form of "defensive copying", to + * prevent clients from completing, while still being able to + * arrange dependent actions. + * + * @return the new CompletableFuture + * @since 1.9 + */ + public CompletableFuture copy() { + return uniCopyStage(); + } + + /** + * Returns a new CompletionStage that is completed normally with + * the same value as this CompletableFuture when it completes + * normally, and cannot be independently completed or otherwise + * used in ways not defined by the methods of interface {@link + * CompletionStage}. If this CompletableFuture completes + * exceptionally, then the returned CompletionStage completes + * exceptionally with a CompletionException with this exception as + * cause. + * + * @return the new CompletionStage + * @since 1.9 + */ + public CompletionStage minimalCompletionStage() { + return uniAsMinimalStage(); + } + + /** + * Completes this CompletableFuture with the result of + * the given Supplier function invoked from an asynchronous + * task using the given executor. + * + * @param supplier a function returning the value to be used + * to complete this CompletableFuture + * @param executor the executor to use for asynchronous execution + * @return this CompletableFuture + * @since 1.9 + */ + public CompletableFuture completeAsync(Supplier supplier, + Executor executor) { + if (supplier == null || executor == null) + throw new NullPointerException(); + executor.execute(new AsyncSupply(this, supplier)); + return this; + } + + /** + * Completes this CompletableFuture with the result of the given + * Supplier function invoked from an asynchronous task using the + * default executor. + * + * @param supplier a function returning the value to be used + * to complete this CompletableFuture + * @return this CompletableFuture + * @since 1.9 + */ + public CompletableFuture completeAsync(Supplier supplier) { + return completeAsync(supplier, defaultExecutor()); + } + + /** + * Exceptionally completes this CompletableFuture with + * a {@link TimeoutException} if not otherwise completed + * before the given timeout. + * + * @param timeout how long to wait before completing exceptionally + * with a TimeoutException, in units of {@code unit} + * @param unit a {@code TimeUnit} determining how to interpret the + * {@code timeout} parameter + * @return this CompletableFuture + * @since 1.9 + */ + public CompletableFuture orTimeout(long timeout, TimeUnit unit) { + if (unit == null) + throw new NullPointerException(); + if (result == null) + whenComplete(new Canceller(Delayer.delay(new Timeout(this), + timeout, unit))); + return this; + } + + /** + * Completes this CompletableFuture with the given value if not + * otherwise completed before the given timeout. + * + * @param value the value to use upon timeout + * @param timeout how long to wait before completing normally + * with the given value, in units of {@code unit} + * @param unit a {@code TimeUnit} determining how to interpret the + * {@code timeout} parameter + * @return this CompletableFuture + * @since 1.9 + */ + public CompletableFuture completeOnTimeout(T value, long timeout, + TimeUnit unit) { + if (unit == null) + throw new NullPointerException(); + if (result == null) + whenComplete(new Canceller(Delayer.delay( + new DelayedCompleter(this, value), + timeout, unit))); + return this; + } + + /** + * Returns a new Executor that submits a task to the given base + * executor after the given delay (or no delay if non-positive). + * Each delay commences upon invocation of the returned executor's + * {@code execute} method. + * + * @param delay how long to delay, in units of {@code unit} + * @param unit a {@code TimeUnit} determining how to interpret the + * {@code delay} parameter + * @param executor the base executor + * @return the new delayed executor + * @since 1.9 + */ + public static Executor delayedExecutor(long delay, TimeUnit unit, + Executor executor) { + if (unit == null || executor == null) + throw new NullPointerException(); + return new DelayedExecutor(delay, unit, executor); + } + + /** + * Returns a new Executor that submits a task to the default + * executor after the given delay (or no delay if non-positive). + * Each delay commences upon invocation of the returned executor's + * {@code execute} method. + * + * @param delay how long to delay, in units of {@code unit} + * @param unit a {@code TimeUnit} determining how to interpret the + * {@code delay} parameter + * @return the new delayed executor + * @since 1.9 + */ + public static Executor delayedExecutor(long delay, TimeUnit unit) { + if (unit == null) + throw new NullPointerException(); + return new DelayedExecutor(delay, unit, ASYNC_POOL); + } + + /** + * Returns a new CompletionStage that is already completed with + * the given value and supports only those methods in + * interface {@link CompletionStage}. + * + * @param value the value + * @param the type of the value + * @return the completed CompletionStage + * @since 1.9 + */ + public static CompletionStage completedStage(U value) { + return new MinimalStage((value == null) ? NIL : value); + } + + /** + * Returns a new CompletableFuture that is already completed + * exceptionally with the given exception. + * + * @param ex the exception + * @param the type of the value + * @return the exceptionally completed CompletableFuture + * @since 1.9 + */ + public static CompletableFuture failedFuture(Throwable ex) { + if (ex == null) throw new NullPointerException(); + return new CompletableFuture(new AltResult(ex)); + } + + /** + * Returns a new CompletionStage that is already completed + * exceptionally with the given exception and supports only those + * methods in interface {@link CompletionStage}. + * + * @param ex the exception + * @param the type of the value + * @return the exceptionally completed CompletionStage + * @since 1.9 + */ + public static CompletionStage failedStage(Throwable ex) { + if (ex == null) throw new NullPointerException(); + return new MinimalStage(new AltResult(ex)); + } + + /** + * Singleton delay scheduler, used only for starting and + * cancelling tasks. + */ + static final class Delayer { + static ScheduledFuture delay(Runnable command, long delay, + TimeUnit unit) { + return delayer.schedule(command, delay, unit); + } + + static final class DaemonThreadFactory implements ThreadFactory { + public Thread newThread(Runnable r) { + Thread t = new Thread(r); + t.setDaemon(true); + t.setName("CompletableFutureDelayScheduler"); + return t; + } + } + + static final ScheduledThreadPoolExecutor delayer; + static { + (delayer = new ScheduledThreadPoolExecutor( + 1, new DaemonThreadFactory())). + setRemoveOnCancelPolicy(true); + } + } + + // Little class-ified lambdas to better support monitoring + + static final class DelayedExecutor implements Executor { + final long delay; + final TimeUnit unit; + final Executor executor; + DelayedExecutor(long delay, TimeUnit unit, Executor executor) { + this.delay = delay; this.unit = unit; this.executor = executor; + } + public void execute(Runnable r) { + Delayer.delay(new TaskSubmitter(executor, r), delay, unit); + } + } + + /** Action to submit user task */ + static final class TaskSubmitter implements Runnable { + final Executor executor; + final Runnable action; + TaskSubmitter(Executor executor, Runnable action) { + this.executor = executor; + this.action = action; + } + public void run() { executor.execute(action); } + } + + /** Action to completeExceptionally on timeout */ + static final class Timeout implements Runnable { + final CompletableFuture f; + Timeout(CompletableFuture f) { this.f = f; } + public void run() { + if (f != null && !f.isDone()) + f.completeExceptionally(new TimeoutException()); + } + } + + /** Action to complete on timeout */ + static final class DelayedCompleter implements Runnable { + final CompletableFuture f; + final U u; + DelayedCompleter(CompletableFuture f, U u) { this.f = f; this.u = u; } + public void run() { + if (f != null) + f.complete(u); + } + } + + /** Action to cancel unneeded timeouts */ + static final class Canceller implements BiConsumer { + final Future f; + Canceller(Future f) { this.f = f; } + public void accept(Object ignore, Throwable ex) { + if (ex == null && f != null && !f.isDone()) + f.cancel(false); + } + } + + /** + * A subclass that just throws UOE for most non-CompletionStage methods. + */ + static final class MinimalStage extends CompletableFuture { + MinimalStage() { } + MinimalStage(Object r) { super(r); } + @Override public CompletableFuture newIncompleteFuture() { + return new MinimalStage(); } + @Override public T get() { + throw new UnsupportedOperationException(); } + @Override public T get(long timeout, TimeUnit unit) { + throw new UnsupportedOperationException(); } + @Override public T getNow(T valueIfAbsent) { + throw new UnsupportedOperationException(); } + @Override public T join() { + throw new UnsupportedOperationException(); } + @Override public boolean complete(T value) { + throw new UnsupportedOperationException(); } + @Override public boolean completeExceptionally(Throwable ex) { + throw new UnsupportedOperationException(); } + @Override public boolean cancel(boolean mayInterruptIfRunning) { + throw new UnsupportedOperationException(); } + @Override public void obtrudeValue(T value) { + throw new UnsupportedOperationException(); } + @Override public void obtrudeException(Throwable ex) { + throw new UnsupportedOperationException(); } + @Override public boolean isDone() { + throw new UnsupportedOperationException(); } + @Override public boolean isCancelled() { + throw new UnsupportedOperationException(); } + @Override public boolean isCompletedExceptionally() { + throw new UnsupportedOperationException(); } + @Override public int getNumberOfDependents() { + throw new UnsupportedOperationException(); } + @Override public CompletableFuture completeAsync + (Supplier supplier, Executor executor) { + throw new UnsupportedOperationException(); } + @Override public CompletableFuture completeAsync + (Supplier supplier) { + throw new UnsupportedOperationException(); } + @Override public CompletableFuture orTimeout + (long timeout, TimeUnit unit) { + throw new UnsupportedOperationException(); } + @Override public CompletableFuture completeOnTimeout + (T value, long timeout, TimeUnit unit) { + throw new UnsupportedOperationException(); } + } + // Unsafe mechanics - private static final sun.misc.Unsafe UNSAFE; + private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe(); private static final long RESULT; private static final long STACK; private static final long NEXT; static { try { - final sun.misc.Unsafe u; - UNSAFE = u = sun.misc.Unsafe.getUnsafe(); - Class k = CompletableFuture.class; - RESULT = u.objectFieldOffset(k.getDeclaredField("result")); - STACK = u.objectFieldOffset(k.getDeclaredField("stack")); - NEXT = u.objectFieldOffset + RESULT = U.objectFieldOffset + (CompletableFuture.class.getDeclaredField("result")); + STACK = U.objectFieldOffset + (CompletableFuture.class.getDeclaredField("stack")); + NEXT = U.objectFieldOffset (Completion.class.getDeclaredField("next")); - } catch (Exception x) { - throw new Error(x); + } catch (ReflectiveOperationException e) { + throw new Error(e); } + + // Reduce the risk of rare disastrous classloading in first call to + // LockSupport.park: https://bugs.openjdk.java.net/browse/JDK-8074773 + Class ensureLoaded = LockSupport.class; } } diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/CompletionStage.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/CompletionStage.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/CompletionStage.java Wed Jul 05 20:54:48 2017 +0200 @@ -34,12 +34,11 @@ */ package java.util.concurrent; -import java.util.function.Supplier; -import java.util.function.Consumer; + import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Consumer; import java.util.function.Function; -import java.util.function.BiFunction; -import java.util.concurrent.Executor; /** * A stage of a possibly asynchronous computation, that performs an @@ -56,9 +55,9 @@ * For example, {@code stage.thenApply(x -> square(x)).thenAccept(x -> * System.out.print(x)).thenRun(() -> System.out.println())}. An * additional form (compose) applies functions of stages - * themselves, rather than their results.
  • + * themselves, rather than their results. * - *
  • One stage's execution may be triggered by completion of a + *
  • One stage's execution may be triggered by completion of a * single stage, or both of two stages, or either of two stages. * Dependencies on a single stage are arranged using methods with * prefix then. Those triggered by completion of @@ -66,9 +65,9 @@ * effects, using correspondingly named methods. Those triggered by * either of two stages make no guarantees about which of the * results or effects are used for the dependent stage's - * computation.
  • + * computation. * - *
  • Dependencies among stages control the triggering of + *
  • Dependencies among stages control the triggering of * computations, but do not otherwise guarantee any particular * ordering. Additionally, execution of a new stage's computations may * be arranged in any of three ways: default execution, default @@ -81,7 +80,7 @@ * properties, and might not even support concurrent execution, but * are arranged for processing in a way that accommodates asynchrony. * - *
  • Two method forms support processing whether the triggering + *
  • Two method forms support processing whether the triggering * stage completed normally or exceptionally: Method {@link * #whenComplete whenComplete} allows injection of an action * regardless of outcome, otherwise preserving the outcome in its @@ -100,7 +99,7 @@ * stage completes normally or exceptionally. In the case of method * {@code whenComplete}, when the supplied action itself encounters an * exception, then the stage exceptionally completes with this - * exception if not already completed exceptionally.
  • + * exception if not already completed exceptionally. * * * @@ -587,7 +586,7 @@ /** * Returns a new CompletionStage that, when this stage completes - * normally, is executed with this stage as the argument + * normally, is executed with this stage's result as the argument * to the supplied function. * * See the {@link CompletionStage} documentation for rules @@ -603,7 +602,7 @@ /** * Returns a new CompletionStage that, when this stage completes * normally, is executed using this stage's default asynchronous - * execution facility, with this stage as the argument to the + * execution facility, with this stage's result as the argument to the * supplied function. * * See the {@link CompletionStage} documentation for rules @@ -652,12 +651,14 @@ * Returns a new CompletionStage with the same result or exception as * this stage, that executes the given action when this stage completes. * - *

    When this stage is complete, the given action is invoked with the - * result (or {@code null} if none) and the exception (or {@code null} - * if none) of this stage as arguments. The returned stage is completed - * when the action returns. If the supplied action itself encounters an - * exception, then the returned stage exceptionally completes with this - * exception unless this stage also completed exceptionally. + *

    When this stage is complete, the given action is invoked + * with the result (or {@code null} if none) and the exception (or + * {@code null} if none) of this stage as arguments. The returned + * stage is completed when the action returns. If the supplied + * action itself encounters an exception, then the returned stage + * exceptionally completes with this exception unless this stage + * also completed exceptionally (in which case, the returned stage + * exceptionally completes with the original exception). * * @param action the action to perform * @return the new CompletionStage diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java Wed Jul 05 20:54:48 2017 +0200 @@ -42,7 +42,6 @@ import java.util.AbstractMap; import java.util.Arrays; import java.util.Collection; -import java.util.Comparator; import java.util.Enumeration; import java.util.HashMap; import java.util.Hashtable; @@ -51,14 +50,11 @@ import java.util.NoSuchElementException; import java.util.Set; import java.util.Spliterator; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.ForkJoinPool; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.LockSupport; import java.util.concurrent.locks.ReentrantLock; import java.util.function.BiConsumer; import java.util.function.BiFunction; -import java.util.function.BinaryOperator; import java.util.function.Consumer; import java.util.function.DoubleBinaryOperator; import java.util.function.Function; @@ -154,43 +150,43 @@ * being concurrently updated by other threads; for example, when * computing a snapshot summary of the values in a shared registry. * There are three kinds of operation, each with four forms, accepting - * functions with Keys, Values, Entries, and (Key, Value) arguments - * and/or return values. Because the elements of a ConcurrentHashMap - * are not ordered in any particular way, and may be processed in - * different orders in different parallel executions, the correctness - * of supplied functions should not depend on any ordering, or on any - * other objects or values that may transiently change while - * computation is in progress; and except for forEach actions, should - * ideally be side-effect-free. Bulk operations on {@link java.util.Map.Entry} - * objects do not support method {@code setValue}. + * functions with keys, values, entries, and (key, value) pairs as + * arguments and/or return values. Because the elements of a + * ConcurrentHashMap are not ordered in any particular way, and may be + * processed in different orders in different parallel executions, the + * correctness of supplied functions should not depend on any + * ordering, or on any other objects or values that may transiently + * change while computation is in progress; and except for forEach + * actions, should ideally be side-effect-free. Bulk operations on + * {@link java.util.Map.Entry} objects do not support method {@code + * setValue}. * *

      - *
    • forEach: Perform a given action on each element. + *
    • forEach: Performs a given action on each element. * A variant form applies a given transformation on each element - * before performing the action.
    • + * before performing the action. * - *
    • search: Return the first available non-null result of + *
    • search: Returns the first available non-null result of * applying a given function on each element; skipping further - * search when a result is found.
    • + * search when a result is found. * - *
    • reduce: Accumulate each element. The supplied reduction + *
    • reduce: Accumulates each element. The supplied reduction * function cannot rely on ordering (more formally, it should be * both associative and commutative). There are five variants: * *
        * - *
      • Plain reductions. (There is not a form of this method for + *
      • Plain reductions. (There is not a form of this method for * (key, value) function arguments since there is no corresponding - * return type.)
      • + * return type.) * - *
      • Mapped reductions that accumulate the results of a given - * function applied to each element.
      • + *
      • Mapped reductions that accumulate the results of a given + * function applied to each element. * - *
      • Reductions to scalar doubles, longs, and ints, using a - * given basis value.
      • + *
      • Reductions to scalar doubles, longs, and ints, using a + * given basis value. * *
      - *
    • *
    * *

    These bulk operations accept a {@code parallelismThreshold} @@ -576,7 +572,7 @@ * The number of bits used for generation stamp in sizeCtl. * Must be at least 6 for 32bit arrays. */ - private static int RESIZE_STAMP_BITS = 16; + private static final int RESIZE_STAMP_BITS = 16; /** * The maximum number of threads that can help resize. @@ -604,7 +600,7 @@ private static final ObjectStreamField[] serialPersistentFields = { new ObjectStreamField("segments", Segment[].class), new ObjectStreamField("segmentMask", Integer.TYPE), - new ObjectStreamField("segmentShift", Integer.TYPE) + new ObjectStreamField("segmentShift", Integer.TYPE), }; /* ---------------- Nodes -------------- */ @@ -630,10 +626,12 @@ this.next = next; } - public final K getKey() { return key; } - public final V getValue() { return val; } - public final int hashCode() { return key.hashCode() ^ val.hashCode(); } - public final String toString(){ return key + "=" + val; } + public final K getKey() { return key; } + public final V getValue() { return val; } + public final int hashCode() { return key.hashCode() ^ val.hashCode(); } + public final String toString() { + return Helpers.mapEntryToString(key, val); + } public final V setValue(V value) { throw new UnsupportedOperationException(); } @@ -1057,6 +1055,8 @@ p.val = value; } } + else if (f instanceof ReservationNode) + throw new IllegalStateException("Recursive update"); } } if (binCount != 0) { @@ -1159,6 +1159,8 @@ } } } + else if (f instanceof ReservationNode) + throw new IllegalStateException("Recursive update"); } } if (validated) { @@ -1366,7 +1368,7 @@ /** * Stripped-down version of helper class used in previous version, - * declared for the sake of serialization compatibility + * declared for the sake of serialization compatibility. */ static class Segment extends ReentrantLock implements Serializable { private static final long serialVersionUID = 2249069246763182397L; @@ -1401,9 +1403,10 @@ new Segment[DEFAULT_CONCURRENCY_LEVEL]; for (int i = 0; i < segments.length; ++i) segments[i] = new Segment(LOAD_FACTOR); - s.putFields().put("segments", segments); - s.putFields().put("segmentShift", segmentShift); - s.putFields().put("segmentMask", segmentMask); + java.io.ObjectOutputStream.PutField streamFields = s.putFields(); + streamFields.put("segments", segments); + streamFields.put("segmentShift", segmentShift); + streamFields.put("segmentMask", segmentMask); s.writeFields(); Node[] t; @@ -1620,9 +1623,9 @@ } /** - * Helper method for EntrySet.removeIf + * Helper method for EntrySetView.removeIf. */ - boolean removeEntryIf(Predicate> function) { + boolean removeEntryIf(Predicate> function) { if (function == null) throw new NullPointerException(); Node[] t; boolean removed = false; @@ -1640,9 +1643,9 @@ } /** - * Helper method for Values.removeIf + * Helper method for ValuesView.removeIf. */ - boolean removeValueIf(Predicate function) { + boolean removeValueIf(Predicate function) { if (function == null) throw new NullPointerException(); Node[] t; boolean removed = false; @@ -1716,7 +1719,7 @@ if (fh >= 0) { binCount = 1; for (Node e = f;; ++binCount) { - K ek; V ev; + K ek; if (e.hash == h && ((ek = e.key) == key || (ek != null && key.equals(ek)))) { @@ -1726,6 +1729,8 @@ Node pred = e; if ((e = e.next) == null) { if ((val = mappingFunction.apply(key)) != null) { + if (pred.next != null) + throw new IllegalStateException("Recursive update"); added = true; pred.next = new Node(h, key, val, null); } @@ -1745,6 +1750,8 @@ t.putTreeVal(h, key, val); } } + else if (f instanceof ReservationNode) + throw new IllegalStateException("Recursive update"); } } if (binCount != 0) { @@ -1840,6 +1847,8 @@ } } } + else if (f instanceof ReservationNode) + throw new IllegalStateException("Recursive update"); } } if (binCount != 0) @@ -1931,6 +1940,8 @@ if ((e = e.next) == null) { val = remappingFunction.apply(key, null); if (val != null) { + if (pred.next != null) + throw new IllegalStateException("Recursive update"); delta = 1; pred.next = new Node(h, key, val, null); @@ -1963,6 +1974,8 @@ setTabAt(tab, i, untreeify(t.first)); } } + else if (f instanceof ReservationNode) + throw new IllegalStateException("Recursive update"); } } if (binCount != 0) { @@ -2072,6 +2085,8 @@ setTabAt(tab, i, untreeify(t.first)); } } + else if (f instanceof ReservationNode) + throw new IllegalStateException("Recursive update"); } } if (binCount != 0) { @@ -2089,12 +2104,13 @@ // Hashtable legacy methods /** - * Legacy method testing if some key maps into the specified value - * in this table. This method is identical in functionality to + * Tests if some key maps into the specified value in this table. + * + *

    Note that this method is identical in functionality to * {@link #containsValue(Object)}, and exists solely to ensure * full compatibility with class {@link java.util.Hashtable}, * which supported this method prior to introduction of the - * Java Collections framework. + * Java Collections Framework. * * @param value a value to search for * @return {@code true} if and only if some key maps to the @@ -2235,7 +2251,7 @@ } /** - * A place-holder node used in computeIfAbsent and compute + * A place-holder node used in computeIfAbsent and compute. */ static final class ReservationNode extends Node { ReservationNode() { @@ -2384,17 +2400,8 @@ break; else if (tab == table) { int rs = resizeStamp(n); - if (sc < 0) { - Node[] nt; - if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 || - sc == rs + MAX_RESIZERS || (nt = nextTable) == null || - transferIndex <= 0) - break; - if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) - transfer(tab, nt); - } - else if (U.compareAndSwapInt(this, SIZECTL, sc, - (rs << RESIZE_STAMP_SHIFT) + 2)) + if (U.compareAndSwapInt(this, SIZECTL, sc, + (rs << RESIZE_STAMP_SHIFT) + 2)) transfer(tab, null); } } @@ -2649,7 +2656,7 @@ * too small, in which case resizes instead. */ private final void treeifyBin(Node[] tab, int index) { - Node b; int n, sc; + Node b; int n; if (tab != null) { if ((n = tab.length) < MIN_TREEIFY_CAPACITY) tryPresize(n << 1); @@ -2693,7 +2700,7 @@ /* ---------------- TreeNodes -------------- */ /** - * Nodes for use in TreeBins + * Nodes for use in TreeBins. */ static final class TreeNode extends Node { TreeNode parent; // red-black tree links @@ -2719,7 +2726,7 @@ final TreeNode findTreeNode(int h, Object k, Class kc) { if (k != null) { TreeNode p = this; - do { + do { int ph, dir; K pk; TreeNode q; TreeNode pl = p.left, pr = p.right; if ((ph = p.hash) > h) @@ -2812,7 +2819,7 @@ (kc = comparableClassFor(k)) == null) || (dir = compareComparables(kc, k, pk)) == 0) dir = tieBreakOrder(k, pk); - TreeNode xp = p; + TreeNode xp = p; if ((p = (dir <= 0) ? p.left : p.right) == null) { x.parent = xp; if (dir <= 0) @@ -3165,7 +3172,7 @@ static TreeNode balanceDeletion(TreeNode root, TreeNode x) { - for (TreeNode xp, xpl, xpr;;) { + for (TreeNode xp, xpl, xpr;;) { if (x == null || x == root) return root; else if ((xp = x.parent) == null) { @@ -3256,7 +3263,7 @@ } /** - * Recursive invariant check + * Checks invariants recursively for the tree of Nodes rooted at t. */ static boolean checkInvariants(TreeNode t) { TreeNode tp = t.parent, tl = t.left, tr = t.right, @@ -3280,15 +3287,13 @@ return true; } - private static final sun.misc.Unsafe U; + private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe(); private static final long LOCKSTATE; static { try { - U = sun.misc.Unsafe.getUnsafe(); - Class k = TreeBin.class; LOCKSTATE = U.objectFieldOffset - (k.getDeclaredField("lockState")); - } catch (Exception e) { + (TreeBin.class.getDeclaredField("lockState")); + } catch (ReflectiveOperationException e) { throw new Error(e); } } @@ -3503,7 +3508,7 @@ } /** - * Exported Entry for EntryIterator + * Exported Entry for EntryIterator. */ static final class MapEntry implements Map.Entry { final K key; // non-null @@ -3517,7 +3522,9 @@ public K getKey() { return key; } public V getValue() { return val; } public int hashCode() { return key.hashCode() ^ val.hashCode(); } - public String toString() { return key + "=" + val; } + public String toString() { + return Helpers.mapEntryToString(key, val); + } public boolean equals(Object o) { Object k, v; Map.Entry e; @@ -3554,7 +3561,7 @@ this.est = est; } - public Spliterator trySplit() { + public KeySpliterator trySplit() { int i, f, h; return (h = ((i = baseIndex) + (f = baseLimit)) >>> 1) <= i ? null : new KeySpliterator(tab, baseSize, baseLimit = h, @@ -3593,7 +3600,7 @@ this.est = est; } - public Spliterator trySplit() { + public ValueSpliterator trySplit() { int i, f, h; return (h = ((i = baseIndex) + (f = baseLimit)) >>> 1) <= i ? null : new ValueSpliterator(tab, baseSize, baseLimit = h, @@ -3633,7 +3640,7 @@ this.est = est; } - public Spliterator> trySplit() { + public EntrySpliterator trySplit() { int i, f, h; return (h = ((i = baseIndex) + (f = baseLimit)) >>> 1) <= i ? null : new EntrySpliterator(tab, baseSize, baseLimit = h, @@ -4445,19 +4452,19 @@ public abstract boolean contains(Object o); public abstract boolean remove(Object o); - private static final String oomeMsg = "Required array size too large"; + private static final String OOME_MSG = "Required array size too large"; public final Object[] toArray() { long sz = map.mappingCount(); if (sz > MAX_ARRAY_SIZE) - throw new OutOfMemoryError(oomeMsg); + throw new OutOfMemoryError(OOME_MSG); int n = (int)sz; Object[] r = new Object[n]; int i = 0; for (E e : this) { if (i == n) { if (n >= MAX_ARRAY_SIZE) - throw new OutOfMemoryError(oomeMsg); + throw new OutOfMemoryError(OOME_MSG); if (n >= MAX_ARRAY_SIZE - (MAX_ARRAY_SIZE >>> 1) - 1) n = MAX_ARRAY_SIZE; else @@ -4473,7 +4480,7 @@ public final T[] toArray(T[] a) { long sz = map.mappingCount(); if (sz > MAX_ARRAY_SIZE) - throw new OutOfMemoryError(oomeMsg); + throw new OutOfMemoryError(OOME_MSG); int m = (int)sz; T[] r = (a.length >= m) ? a : (T[])java.lang.reflect.Array @@ -4483,7 +4490,7 @@ for (E e : this) { if (i == n) { if (n >= MAX_ARRAY_SIZE) - throw new OutOfMemoryError(oomeMsg); + throw new OutOfMemoryError(OOME_MSG); if (n >= MAX_ARRAY_SIZE - (MAX_ARRAY_SIZE >>> 1) - 1) n = MAX_ARRAY_SIZE; else @@ -4803,7 +4810,7 @@ return added; } - public boolean removeIf(Predicate> filter) { + public boolean removeIf(Predicate> filter) { return map.removeEntryIf(filter); } @@ -4878,7 +4885,7 @@ } /** - * Same as Traverser version + * Same as Traverser version. */ final Node advance() { Node e; @@ -6323,38 +6330,40 @@ } // Unsafe mechanics - private static final sun.misc.Unsafe U; + private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe(); private static final long SIZECTL; private static final long TRANSFERINDEX; private static final long BASECOUNT; private static final long CELLSBUSY; private static final long CELLVALUE; - private static final long ABASE; + private static final int ABASE; private static final int ASHIFT; static { try { - U = sun.misc.Unsafe.getUnsafe(); - Class k = ConcurrentHashMap.class; SIZECTL = U.objectFieldOffset - (k.getDeclaredField("sizeCtl")); + (ConcurrentHashMap.class.getDeclaredField("sizeCtl")); TRANSFERINDEX = U.objectFieldOffset - (k.getDeclaredField("transferIndex")); + (ConcurrentHashMap.class.getDeclaredField("transferIndex")); BASECOUNT = U.objectFieldOffset - (k.getDeclaredField("baseCount")); + (ConcurrentHashMap.class.getDeclaredField("baseCount")); CELLSBUSY = U.objectFieldOffset - (k.getDeclaredField("cellsBusy")); - Class ck = CounterCell.class; + (ConcurrentHashMap.class.getDeclaredField("cellsBusy")); + CELLVALUE = U.objectFieldOffset - (ck.getDeclaredField("value")); - Class ak = Node[].class; - ABASE = U.arrayBaseOffset(ak); - int scale = U.arrayIndexScale(ak); + (CounterCell.class.getDeclaredField("value")); + + ABASE = U.arrayBaseOffset(Node[].class); + int scale = U.arrayIndexScale(Node[].class); if ((scale & (scale - 1)) != 0) - throw new Error("data type scale not a power of two"); + throw new Error("array index scale not a power of two"); ASHIFT = 31 - Integer.numberOfLeadingZeros(scale); - } catch (Exception e) { + } catch (ReflectiveOperationException e) { throw new Error(e); } + + // Reduce the risk of rare disastrous classloading in first call to + // LockSupport.park: https://bugs.openjdk.java.net/browse/JDK-8074773 + Class ensureLoaded = LockSupport.class; } } diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedDeque.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedDeque.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedDeque.java Wed Jul 05 20:54:48 2017 +0200 @@ -36,11 +36,12 @@ package java.util.concurrent; import java.util.AbstractCollection; -import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Deque; import java.util.Iterator; import java.util.NoSuchElementException; +import java.util.Objects; import java.util.Queue; import java.util.Spliterator; import java.util.Spliterators; @@ -87,7 +88,7 @@ * @since 1.7 * @author Doug Lea * @author Martin Buchholz - * @param the type of elements held in this collection + * @param the type of elements held in this deque */ public class ConcurrentLinkedDeque extends AbstractCollection @@ -300,47 +301,45 @@ * only be seen after publication via casNext or casPrev. */ Node(E item) { - UNSAFE.putObject(this, itemOffset, item); + U.putObject(this, ITEM, item); } boolean casItem(E cmp, E val) { - return UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val); + return U.compareAndSwapObject(this, ITEM, cmp, val); } void lazySetNext(Node val) { - UNSAFE.putOrderedObject(this, nextOffset, val); + U.putOrderedObject(this, NEXT, val); } boolean casNext(Node cmp, Node val) { - return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val); + return U.compareAndSwapObject(this, NEXT, cmp, val); } void lazySetPrev(Node val) { - UNSAFE.putOrderedObject(this, prevOffset, val); + U.putOrderedObject(this, PREV, val); } boolean casPrev(Node cmp, Node val) { - return UNSAFE.compareAndSwapObject(this, prevOffset, cmp, val); + return U.compareAndSwapObject(this, PREV, cmp, val); } // Unsafe mechanics - private static final sun.misc.Unsafe UNSAFE; - private static final long prevOffset; - private static final long itemOffset; - private static final long nextOffset; + private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe(); + private static final long PREV; + private static final long ITEM; + private static final long NEXT; static { try { - UNSAFE = sun.misc.Unsafe.getUnsafe(); - Class k = Node.class; - prevOffset = UNSAFE.objectFieldOffset - (k.getDeclaredField("prev")); - itemOffset = UNSAFE.objectFieldOffset - (k.getDeclaredField("item")); - nextOffset = UNSAFE.objectFieldOffset - (k.getDeclaredField("next")); - } catch (Exception e) { + PREV = U.objectFieldOffset + (Node.class.getDeclaredField("prev")); + ITEM = U.objectFieldOffset + (Node.class.getDeclaredField("item")); + NEXT = U.objectFieldOffset + (Node.class.getDeclaredField("next")); + } catch (ReflectiveOperationException e) { throw new Error(e); } } @@ -350,8 +349,7 @@ * Links e as first element. */ private void linkFirst(E e) { - checkNotNull(e); - final Node newNode = new Node(e); + final Node newNode = new Node(Objects.requireNonNull(e)); restartFromHead: for (;;) @@ -383,8 +381,7 @@ * Links e as last element. */ private void linkLast(E e) { - checkNotNull(e); - final Node newNode = new Node(e); + final Node newNode = new Node(Objects.requireNonNull(e)); restartFromTail: for (;;) @@ -789,16 +786,6 @@ // Minor convenience utilities /** - * Throws NullPointerException if argument is null. - * - * @param v the element - */ - private static void checkNotNull(Object v) { - if (v == null) - throw new NullPointerException(); - } - - /** * Returns element unless it is null, in which case throws * NoSuchElementException. * @@ -812,22 +799,6 @@ } /** - * Creates an array list and fills it with elements of this list. - * Used by toArray. - * - * @return the array list - */ - private ArrayList toArrayList() { - ArrayList list = new ArrayList(); - for (Node p = first(); p != null; p = succ(p)) { - E item = p.item; - if (item != null) - list.add(item); - } - return list; - } - - /** * Constructs an empty deque. */ public ConcurrentLinkedDeque() { @@ -847,8 +818,7 @@ // Copy c into a private chain of Nodes Node h = null, t = null; for (E e : c) { - checkNotNull(e); - Node newNode = new Node(e); + Node newNode = new Node(Objects.requireNonNull(e)); if (h == null) h = t = newNode; else { @@ -1046,16 +1016,19 @@ public void push(E e) { addFirst(e); } /** - * Removes the first element {@code e} such that - * {@code o.equals(e)}, if such an element exists in this deque. + * Removes the first occurrence of the specified element from this deque. * If the deque does not contain the element, it is unchanged. + * More formally, removes the first element {@code e} such that + * {@code o.equals(e)} (if such an element exists). + * Returns {@code true} if this deque contained the specified element + * (or equivalently, if this deque changed as a result of the call). * * @param o element to be removed from this deque, if present * @return {@code true} if the deque contained the specified element * @throws NullPointerException if the specified element is null */ public boolean removeFirstOccurrence(Object o) { - checkNotNull(o); + Objects.requireNonNull(o); for (Node p = first(); p != null; p = succ(p)) { E item = p.item; if (item != null && o.equals(item) && p.casItem(item, null)) { @@ -1067,16 +1040,19 @@ } /** - * Removes the last element {@code e} such that - * {@code o.equals(e)}, if such an element exists in this deque. + * Removes the last occurrence of the specified element from this deque. * If the deque does not contain the element, it is unchanged. + * More formally, removes the last element {@code e} such that + * {@code o.equals(e)} (if such an element exists). + * Returns {@code true} if this deque contained the specified element + * (or equivalently, if this deque changed as a result of the call). * * @param o element to be removed from this deque, if present * @return {@code true} if the deque contained the specified element * @throws NullPointerException if the specified element is null */ public boolean removeLastOccurrence(Object o) { - checkNotNull(o); + Objects.requireNonNull(o); for (Node p = last(); p != null; p = pred(p)) { E item = p.item; if (item != null && o.equals(item) && p.casItem(item, null)) { @@ -1088,18 +1064,20 @@ } /** - * Returns {@code true} if this deque contains at least one - * element {@code e} such that {@code o.equals(e)}. + * Returns {@code true} if this deque contains the specified element. + * More formally, returns {@code true} if and only if this deque contains + * at least one element {@code e} such that {@code o.equals(e)}. * * @param o element whose presence in this deque is to be tested * @return {@code true} if this deque contains the specified element */ public boolean contains(Object o) { - if (o == null) return false; - for (Node p = first(); p != null; p = succ(p)) { - E item = p.item; - if (item != null && o.equals(item)) - return true; + if (o != null) { + for (Node p = first(); p != null; p = succ(p)) { + E item = p.item; + if (item != null && o.equals(item)) + return true; + } } return false; } @@ -1130,19 +1108,28 @@ * @return the number of elements in this deque */ public int size() { - int count = 0; - for (Node p = first(); p != null; p = succ(p)) - if (p.item != null) - // Collection.size() spec says to max out - if (++count == Integer.MAX_VALUE) - break; - return count; + restartFromHead: for (;;) { + int count = 0; + for (Node p = first(); p != null;) { + if (p.item != null) + if (++count == Integer.MAX_VALUE) + break; // @see Collection.size() + if (p == (p = p.next)) + continue restartFromHead; + } + return count; + } } /** - * Removes the first element {@code e} such that - * {@code o.equals(e)}, if such an element exists in this deque. + * Removes the first occurrence of the specified element from this deque. * If the deque does not contain the element, it is unchanged. + * More formally, removes the first element {@code e} such that + * {@code o.equals(e)} (if such an element exists). + * Returns {@code true} if this deque contained the specified element + * (or equivalently, if this deque changed as a result of the call). + * + *

    This method is equivalent to {@link #removeFirstOccurrence(Object)}. * * @param o element to be removed from this deque, if present * @return {@code true} if the deque contained the specified element @@ -1172,8 +1159,7 @@ // Copy c into a private chain of Nodes Node beginningOfTheEnd = null, last = null; for (E e : c) { - checkNotNull(e); - Node newNode = new Node(e); + Node newNode = new Node(Objects.requireNonNull(e)); if (beginningOfTheEnd == null) beginningOfTheEnd = last = newNode; else { @@ -1224,6 +1210,62 @@ ; } + public String toString() { + String[] a = null; + restartFromHead: for (;;) { + int charLength = 0; + int size = 0; + for (Node p = first(); p != null;) { + E item = p.item; + if (item != null) { + if (a == null) + a = new String[4]; + else if (size == a.length) + a = Arrays.copyOf(a, 2 * size); + String s = item.toString(); + a[size++] = s; + charLength += s.length(); + } + if (p == (p = p.next)) + continue restartFromHead; + } + + if (size == 0) + return "[]"; + + return Helpers.toString(a, size, charLength); + } + } + + private Object[] toArrayInternal(Object[] a) { + Object[] x = a; + restartFromHead: for (;;) { + int size = 0; + for (Node p = first(); p != null;) { + E item = p.item; + if (item != null) { + if (x == null) + x = new Object[4]; + else if (size == x.length) + x = Arrays.copyOf(x, 2 * (size + 4)); + x[size++] = item; + } + if (p == (p = p.next)) + continue restartFromHead; + } + if (x == null) + return new Object[0]; + else if (a != null && size <= a.length) { + if (a != x) + System.arraycopy(x, 0, a, 0, size); + if (size < a.length) + a[size] = null; + return a; + } + return (size == x.length) ? x : Arrays.copyOf(x, size); + } + } + /** * Returns an array containing all of the elements in this deque, in * proper sequence (from first to last element). @@ -1238,7 +1280,7 @@ * @return an array containing all of the elements in this deque */ public Object[] toArray() { - return toArrayList().toArray(); + return toArrayInternal(null); } /** @@ -1264,7 +1306,7 @@ * The following code can be used to dump the deque into a newly * allocated array of {@code String}: * - *

     {@code String[] y = x.toArray(new String[0]);}
    + *
     {@code String[] y = x.toArray(new String[0]);}
    * * Note that {@code toArray(new Object[0])} is identical in function to * {@code toArray()}. @@ -1278,8 +1320,10 @@ * this deque * @throws NullPointerException if the specified array is null */ + @SuppressWarnings("unchecked") public T[] toArray(T[] a) { - return toArrayList().toArray(a); + if (a == null) throw new NullPointerException(); + return (T[]) toArrayInternal(a); } /** @@ -1346,7 +1390,7 @@ Node p = (nextNode == null) ? startNode() : nextNode(nextNode); for (;; p = nextNode(p)) { if (p == null) { - // p might be active end or TERMINATOR node; both are OK + // might be at active end or TERMINATOR node; both are OK nextNode = null; nextItem = null; break; @@ -1426,8 +1470,9 @@ if (i > 0) { batch = i; return Spliterators.spliterator - (a, 0, i, Spliterator.ORDERED | Spliterator.NONNULL | - Spliterator.CONCURRENT); + (a, 0, i, (Spliterator.ORDERED | + Spliterator.NONNULL | + Spliterator.CONCURRENT)); } } } @@ -1539,8 +1584,7 @@ // Read in elements until trailing null sentinel found Node h = null, t = null; - Object item; - while ((item = s.readObject()) != null) { + for (Object item; (item = s.readObject()) != null; ) { @SuppressWarnings("unchecked") Node newNode = new Node((E) item); if (h == null) @@ -1555,31 +1599,29 @@ } private boolean casHead(Node cmp, Node val) { - return UNSAFE.compareAndSwapObject(this, headOffset, cmp, val); + return U.compareAndSwapObject(this, HEAD, cmp, val); } private boolean casTail(Node cmp, Node val) { - return UNSAFE.compareAndSwapObject(this, tailOffset, cmp, val); + return U.compareAndSwapObject(this, TAIL, cmp, val); } // Unsafe mechanics - private static final sun.misc.Unsafe UNSAFE; - private static final long headOffset; - private static final long tailOffset; + private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe(); + private static final long HEAD; + private static final long TAIL; static { PREV_TERMINATOR = new Node(); PREV_TERMINATOR.next = PREV_TERMINATOR; NEXT_TERMINATOR = new Node(); NEXT_TERMINATOR.prev = NEXT_TERMINATOR; try { - UNSAFE = sun.misc.Unsafe.getUnsafe(); - Class k = ConcurrentLinkedDeque.class; - headOffset = UNSAFE.objectFieldOffset - (k.getDeclaredField("head")); - tailOffset = UNSAFE.objectFieldOffset - (k.getDeclaredField("tail")); - } catch (Exception e) { + HEAD = U.objectFieldOffset + (ConcurrentLinkedDeque.class.getDeclaredField("head")); + TAIL = U.objectFieldOffset + (ConcurrentLinkedDeque.class.getDeclaredField("tail")); + } catch (ReflectiveOperationException e) { throw new Error(e); } } diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedQueue.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedQueue.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedQueue.java Wed Jul 05 20:54:48 2017 +0200 @@ -36,10 +36,11 @@ package java.util.concurrent; import java.util.AbstractQueue; -import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.NoSuchElementException; +import java.util.Objects; import java.util.Queue; import java.util.Spliterator; import java.util.Spliterators; @@ -60,9 +61,9 @@ * does not permit the use of {@code null} elements. * *

    This implementation employs an efficient non-blocking - * algorithm based on one described in Simple, - * Fast, and Practical Non-Blocking and Blocking Concurrent Queue + * algorithm based on one described in + * + * Simple, Fast, and Practical Non-Blocking and Blocking Concurrent Queue * Algorithms by Maged M. Michael and Michael L. Scott. * *

    Iterators are weakly consistent, returning elements @@ -100,7 +101,7 @@ * * @since 1.5 * @author Doug Lea - * @param the type of elements held in this collection + * @param the type of elements held in this queue */ public class ConcurrentLinkedQueue extends AbstractQueue implements Queue, java.io.Serializable { @@ -180,45 +181,28 @@ private static class Node { volatile E item; volatile Node next; - - /** - * Constructs a new node. Uses relaxed write because item can - * only be seen after publication via casNext. - */ - Node(E item) { - UNSAFE.putObject(this, itemOffset, item); - } - - boolean casItem(E cmp, E val) { - return UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val); - } - - void lazySetNext(Node val) { - UNSAFE.putOrderedObject(this, nextOffset, val); - } + } - boolean casNext(Node cmp, Node val) { - return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val); - } - - // Unsafe mechanics - - private static final sun.misc.Unsafe UNSAFE; - private static final long itemOffset; - private static final long nextOffset; + /** + * Returns a new node holding item. Uses relaxed write because item + * can only be seen after piggy-backing publication via casNext. + */ + static Node newNode(E item) { + Node node = new Node(); + U.putObject(node, ITEM, item); + return node; + } - static { - try { - UNSAFE = sun.misc.Unsafe.getUnsafe(); - Class k = Node.class; - itemOffset = UNSAFE.objectFieldOffset - (k.getDeclaredField("item")); - nextOffset = UNSAFE.objectFieldOffset - (k.getDeclaredField("next")); - } catch (Exception e) { - throw new Error(e); - } - } + static boolean casItem(Node node, E cmp, E val) { + return U.compareAndSwapObject(node, ITEM, cmp, val); + } + + static void lazySetNext(Node node, Node val) { + U.putOrderedObject(node, NEXT, val); + } + + static boolean casNext(Node node, Node cmp, Node val) { + return U.compareAndSwapObject(node, NEXT, cmp, val); } /** @@ -233,7 +217,7 @@ * - it is permitted for tail to lag behind head, that is, for tail * to not be reachable from head! */ - private transient volatile Node head; + transient volatile Node head; /** * A node from which the last node on list (that is, the unique @@ -253,7 +237,7 @@ * Creates a {@code ConcurrentLinkedQueue} that is initially empty. */ public ConcurrentLinkedQueue() { - head = tail = new Node(null); + head = tail = newNode(null); } /** @@ -268,17 +252,16 @@ public ConcurrentLinkedQueue(Collection c) { Node h = null, t = null; for (E e : c) { - checkNotNull(e); - Node newNode = new Node(e); + Node newNode = newNode(Objects.requireNonNull(e)); if (h == null) h = t = newNode; else { - t.lazySetNext(newNode); + lazySetNext(t, newNode); t = newNode; } } if (h == null) - h = t = new Node(null); + h = t = newNode(null); head = h; tail = t; } @@ -302,8 +285,9 @@ * as sentinel for succ(), below. */ final void updateHead(Node h, Node p) { + // assert h != null && p != null && (h == p || h.item == null); if (h != p && casHead(h, p)) - h.lazySetNext(h); + lazySetNext(h, h); } /** @@ -324,14 +308,13 @@ * @throws NullPointerException if the specified element is null */ public boolean offer(E e) { - checkNotNull(e); - final Node newNode = new Node(e); + final Node newNode = newNode(Objects.requireNonNull(e)); for (Node t = tail, p = t;;) { Node q = p.next; if (q == null) { // p is last node - if (p.casNext(null, newNode)) { + if (casNext(p, null, newNode)) { // Successful CAS is the linearization point // for e to become an element of this queue, // and for newNode to become "live". @@ -359,7 +342,7 @@ for (Node h = head, p = h, q;;) { E item = p.item; - if (item != null && p.casItem(item, null)) { + if (item != null && casItem(p, item, null)) { // Successful CAS is the linearization point // for item to be removed from this queue. if (p != h) // hop two nodes at a time @@ -446,13 +429,17 @@ * @return the number of elements in this queue */ public int size() { - int count = 0; - for (Node p = first(); p != null; p = succ(p)) - if (p.item != null) - // Collection.size() spec says to max out - if (++count == Integer.MAX_VALUE) - break; - return count; + restartFromHead: for (;;) { + int count = 0; + for (Node p = first(); p != null;) { + if (p.item != null) + if (++count == Integer.MAX_VALUE) + break; // @see Collection.size() + if (p == (p = p.next)) + continue restartFromHead; + } + return count; + } } /** @@ -464,11 +451,12 @@ * @return {@code true} if this queue contains the specified element */ public boolean contains(Object o) { - if (o == null) return false; - for (Node p = first(); p != null; p = succ(p)) { - E item = p.item; - if (item != null && o.equals(item)) - return true; + if (o != null) { + for (Node p = first(); p != null; p = succ(p)) { + E item = p.item; + if (item != null && o.equals(item)) + return true; + } } return false; } @@ -485,19 +473,25 @@ * @return {@code true} if this queue changed as a result of the call */ public boolean remove(Object o) { - if (o == null) return false; - Node pred = null; - for (Node p = first(); p != null; p = succ(p)) { - E item = p.item; - if (item != null && - o.equals(item) && - p.casItem(item, null)) { - Node next = succ(p); - if (pred != null && next != null) - pred.casNext(p, next); - return true; + if (o != null) { + Node next, pred = null; + for (Node p = first(); p != null; pred = p, p = next) { + boolean removed = false; + E item = p.item; + if (item != null) { + if (!o.equals(item)) { + next = succ(p); + continue; + } + removed = casItem(p, item, null); + } + + next = succ(p); + if (pred != null && next != null) // unlink + casNext(pred, p, next); + if (removed) + return true; } - pred = p; } return false; } @@ -522,12 +516,11 @@ // Copy c into a private chain of Nodes Node beginningOfTheEnd = null, last = null; for (E e : c) { - checkNotNull(e); - Node newNode = new Node(e); + Node newNode = newNode(Objects.requireNonNull(e)); if (beginningOfTheEnd == null) beginningOfTheEnd = last = newNode; else { - last.lazySetNext(newNode); + lazySetNext(last, newNode); last = newNode; } } @@ -539,7 +532,7 @@ Node q = p.next; if (q == null) { // p is last node - if (p.casNext(null, beginningOfTheEnd)) { + if (casNext(p, null, beginningOfTheEnd)) { // Successful CAS is the linearization point // for all elements to be added to this queue. if (!casTail(t, last)) { @@ -565,6 +558,62 @@ } } + public String toString() { + String[] a = null; + restartFromHead: for (;;) { + int charLength = 0; + int size = 0; + for (Node p = first(); p != null;) { + E item = p.item; + if (item != null) { + if (a == null) + a = new String[4]; + else if (size == a.length) + a = Arrays.copyOf(a, 2 * size); + String s = item.toString(); + a[size++] = s; + charLength += s.length(); + } + if (p == (p = p.next)) + continue restartFromHead; + } + + if (size == 0) + return "[]"; + + return Helpers.toString(a, size, charLength); + } + } + + private Object[] toArrayInternal(Object[] a) { + Object[] x = a; + restartFromHead: for (;;) { + int size = 0; + for (Node p = first(); p != null;) { + E item = p.item; + if (item != null) { + if (x == null) + x = new Object[4]; + else if (size == x.length) + x = Arrays.copyOf(x, 2 * (size + 4)); + x[size++] = item; + } + if (p == (p = p.next)) + continue restartFromHead; + } + if (x == null) + return new Object[0]; + else if (a != null && size <= a.length) { + if (a != x) + System.arraycopy(x, 0, a, 0, size); + if (size < a.length) + a[size] = null; + return a; + } + return (size == x.length) ? x : Arrays.copyOf(x, size); + } + } + /** * Returns an array containing all of the elements in this queue, in * proper sequence. @@ -579,14 +628,7 @@ * @return an array containing all of the elements in this queue */ public Object[] toArray() { - // Use ArrayList to deal with resizing. - ArrayList al = new ArrayList(); - for (Node p = first(); p != null; p = succ(p)) { - E item = p.item; - if (item != null) - al.add(item); - } - return al.toArray(); + return toArrayInternal(null); } /** @@ -610,7 +652,7 @@ * The following code can be used to dump the queue into a newly * allocated array of {@code String}: * - *

     {@code String[] y = x.toArray(new String[0]);}
    + *
     {@code String[] y = x.toArray(new String[0]);}
    * * Note that {@code toArray(new Object[0])} is identical in function to * {@code toArray()}. @@ -626,28 +668,8 @@ */ @SuppressWarnings("unchecked") public T[] toArray(T[] a) { - // try to use sent-in array - int k = 0; - Node p; - for (p = first(); p != null && k < a.length; p = succ(p)) { - E item = p.item; - if (item != null) - a[k++] = (T)item; - } - if (p == null) { - if (k < a.length) - a[k] = null; - return a; - } - - // If won't fit, use ArrayList version - ArrayList al = new ArrayList(); - for (Node q = first(); q != null; q = succ(q)) { - E item = q.item; - if (item != null) - al.add(item); - } - return al.toArray(a); + if (a == null) throw new NullPointerException(); + return (T[]) toArrayInternal(a); } /** @@ -683,54 +705,47 @@ private Node lastRet; Itr() { - advance(); - } - - /** - * Moves to next valid node and returns item to return for - * next(), or null if no such. - */ - private E advance() { - lastRet = nextNode; - E x = nextItem; - - Node pred, p; - if (nextNode == null) { - p = first(); - pred = null; - } else { - pred = nextNode; - p = succ(nextNode); - } - - for (;;) { - if (p == null) { - nextNode = null; - nextItem = null; - return x; + restartFromHead: for (;;) { + Node h, p, q; + for (p = h = head;; p = q) { + E item; + if ((item = p.item) != null) { + nextNode = p; + nextItem = item; + break; + } + else if ((q = p.next) == null) + break; + else if (p == q) + continue restartFromHead; } - E item = p.item; - if (item != null) { - nextNode = p; - nextItem = item; - return x; - } else { - // skip over nulls - Node next = succ(p); - if (pred != null && next != null) - pred.casNext(p, next); - p = next; - } + updateHead(h, p); + return; } } public boolean hasNext() { - return nextNode != null; + return nextItem != null; } public E next() { - if (nextNode == null) throw new NoSuchElementException(); - return advance(); + final Node pred = nextNode; + if (pred == null) throw new NoSuchElementException(); + // assert nextItem != null; + lastRet = pred; + E item = null; + + for (Node p = succ(pred), q;; p = q) { + if (p == null || (item = p.item) != null) { + nextNode = p; + E x = nextItem; + nextItem = item; + return x; + } + // unlink deleted nodes + if ((q = succ(p)) != null) + casNext(pred, p, q); + } } public void remove() { @@ -780,19 +795,18 @@ // Read in elements until trailing null sentinel found Node h = null, t = null; - Object item; - while ((item = s.readObject()) != null) { + for (Object item; (item = s.readObject()) != null; ) { @SuppressWarnings("unchecked") - Node newNode = new Node((E) item); + Node newNode = newNode((E) item); if (h == null) h = t = newNode; else { - t.lazySetNext(newNode); + lazySetNext(t, newNode); t = newNode; } } if (h == null) - h = t = new Node(null); + h = t = newNode(null); head = h; tail = t; } @@ -829,8 +843,9 @@ if (i > 0) { batch = i; return Spliterators.spliterator - (a, 0, i, Spliterator.ORDERED | Spliterator.NONNULL | - Spliterator.CONCURRENT); + (a, 0, i, (Spliterator.ORDERED | + Spliterator.NONNULL | + Spliterator.CONCURRENT)); } } return null; @@ -904,38 +919,32 @@ return new CLQSpliterator(this); } - /** - * Throws NullPointerException if argument is null. - * - * @param v the element - */ - private static void checkNotNull(Object v) { - if (v == null) - throw new NullPointerException(); - } - private boolean casTail(Node cmp, Node val) { - return UNSAFE.compareAndSwapObject(this, tailOffset, cmp, val); + return U.compareAndSwapObject(this, TAIL, cmp, val); } private boolean casHead(Node cmp, Node val) { - return UNSAFE.compareAndSwapObject(this, headOffset, cmp, val); + return U.compareAndSwapObject(this, HEAD, cmp, val); } // Unsafe mechanics - private static final sun.misc.Unsafe UNSAFE; - private static final long headOffset; - private static final long tailOffset; + private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe(); + private static final long HEAD; + private static final long TAIL; + private static final long ITEM; + private static final long NEXT; static { try { - UNSAFE = sun.misc.Unsafe.getUnsafe(); - Class k = ConcurrentLinkedQueue.class; - headOffset = UNSAFE.objectFieldOffset - (k.getDeclaredField("head")); - tailOffset = UNSAFE.objectFieldOffset - (k.getDeclaredField("tail")); - } catch (Exception e) { + HEAD = U.objectFieldOffset + (ConcurrentLinkedQueue.class.getDeclaredField("head")); + TAIL = U.objectFieldOffset + (ConcurrentLinkedQueue.class.getDeclaredField("tail")); + ITEM = U.objectFieldOffset + (Node.class.getDeclaredField("item")); + NEXT = U.objectFieldOffset + (Node.class.getDeclaredField("next")); + } catch (ReflectiveOperationException e) { throw new Error(e); } } diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentMap.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentMap.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentMap.java Wed Jul 05 20:54:48 2017 +0200 @@ -89,7 +89,7 @@ return ((v = get(key)) != null) ? v : defaultValue; } - /** + /** * {@inheritDoc} * * @implSpec The default implementation is equivalent to, for this @@ -181,10 +181,10 @@ * is not supported by this map * @throws ClassCastException if the key or value is of an inappropriate * type for this map - * (optional) + * (optional) * @throws NullPointerException if the specified key or value is null, * and this map does not permit null keys or values - * (optional) + * (optional) */ boolean remove(Object key, Object value); diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentNavigableMap.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentNavigableMap.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentNavigableMap.java Wed Jul 05 20:54:48 2017 +0200 @@ -34,7 +34,9 @@ */ package java.util.concurrent; -import java.util.*; + +import java.util.NavigableMap; +import java.util.NavigableSet; /** * A {@link ConcurrentMap} supporting {@link NavigableMap} operations, @@ -101,7 +103,7 @@ * reflected in the descending map, and vice-versa. * *

    The returned map has an ordering equivalent to - * {@link Collections#reverseOrder(Comparator) Collections.reverseOrder}{@code (comparator())}. + * {@link java.util.Collections#reverseOrder(Comparator) Collections.reverseOrder}{@code (comparator())}. * The expression {@code m.descendingMap().descendingMap()} returns a * view of {@code m} essentially equivalent to {@code m}. * @@ -125,7 +127,7 @@ * * @return a navigable set view of the keys in this map */ - public NavigableSet navigableKeySet(); + NavigableSet navigableKeySet(); /** * Returns a {@link NavigableSet} view of the keys contained in this map. @@ -163,5 +165,5 @@ * * @return a reverse order navigable set view of the keys in this map */ - public NavigableSet descendingKeySet(); + NavigableSet descendingKeySet(); } diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListMap.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListMap.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListMap.java Wed Jul 05 20:54:48 2017 +0200 @@ -34,6 +34,7 @@ */ package java.util.concurrent; + import java.io.Serializable; import java.util.AbstractCollection; import java.util.AbstractMap; @@ -50,13 +51,10 @@ import java.util.NoSuchElementException; import java.util.Set; import java.util.SortedMap; -import java.util.SortedSet; import java.util.Spliterator; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.ConcurrentNavigableMap; +import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.Consumer; -import java.util.function.BiConsumer; import java.util.function.Function; import java.util.function.Predicate; @@ -359,9 +357,9 @@ private static final long serialVersionUID = -8627078645895051609L; /** - * Special value used to identify base-level header + * Special value used to identify base-level header. */ - private static final Object BASE_HEADER = new Object(); + static final Object BASE_HEADER = new Object(); /** * The topmost head index of the skiplist. @@ -377,11 +375,11 @@ final Comparator comparator; /** Lazily initialized key set */ - private transient KeySet keySet; + private transient KeySet keySet; /** Lazily initialized entry set */ private transient EntrySet entrySet; /** Lazily initialized values collection */ - private transient Values values; + private transient Values values; /** Lazily initialized descending key set */ private transient ConcurrentNavigableMap descendingMap; @@ -400,10 +398,10 @@ } /** - * compareAndSet head node + * compareAndSet head node. */ private boolean casHead(HeadIndex cmp, HeadIndex val) { - return UNSAFE.compareAndSwapObject(this, headOffset, cmp, val); + return U.compareAndSwapObject(this, HEAD, cmp, val); } /* ---------------- Nodes -------------- */ @@ -443,17 +441,17 @@ } /** - * compareAndSet value field + * compareAndSet value field. */ boolean casValue(Object cmp, Object val) { - return UNSAFE.compareAndSwapObject(this, valueOffset, cmp, val); + return U.compareAndSwapObject(this, VALUE, cmp, val); } /** - * compareAndSet next field + * compareAndSet next field. */ boolean casNext(Node cmp, Node val) { - return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val); + return U.compareAndSwapObject(this, NEXT, cmp, val); } /** @@ -534,21 +532,19 @@ return new AbstractMap.SimpleImmutableEntry(key, vv); } - // UNSAFE mechanics + // Unsafe mechanics - private static final sun.misc.Unsafe UNSAFE; - private static final long valueOffset; - private static final long nextOffset; + private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe(); + private static final long VALUE; + private static final long NEXT; static { try { - UNSAFE = sun.misc.Unsafe.getUnsafe(); - Class k = Node.class; - valueOffset = UNSAFE.objectFieldOffset - (k.getDeclaredField("value")); - nextOffset = UNSAFE.objectFieldOffset - (k.getDeclaredField("next")); - } catch (Exception e) { + VALUE = U.objectFieldOffset + (Node.class.getDeclaredField("value")); + NEXT = U.objectFieldOffset + (Node.class.getDeclaredField("next")); + } catch (ReflectiveOperationException e) { throw new Error(e); } } @@ -578,10 +574,10 @@ } /** - * compareAndSet right field + * compareAndSet right field. */ final boolean casRight(Index cmp, Index val) { - return UNSAFE.compareAndSwapObject(this, rightOffset, cmp, val); + return U.compareAndSwapObject(this, RIGHT, cmp, val); } /** @@ -618,15 +614,13 @@ } // Unsafe mechanics - private static final sun.misc.Unsafe UNSAFE; - private static final long rightOffset; + private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe(); + private static final long RIGHT; static { try { - UNSAFE = sun.misc.Unsafe.getUnsafe(); - Class k = Index.class; - rightOffset = UNSAFE.objectFieldOffset - (k.getDeclaredField("right")); - } catch (Exception e) { + RIGHT = U.objectFieldOffset + (Index.class.getDeclaredField("right")); + } catch (ReflectiveOperationException e) { throw new Error(e); } } @@ -730,10 +724,10 @@ * * The traversal loops in doPut, doRemove, and findNear all * include the same three kinds of checks. And specialized - * versions appear in findFirst, and findLast and their - * variants. They can't easily share code because each uses the - * reads of fields held in locals occurring in the orders they - * were performed. + * versions appear in findFirst, and findLast and their variants. + * They can't easily share code because each uses the reads of + * fields held in locals occurring in the orders they were + * performed. * * @param key the key * @return node holding key, or null if no such @@ -1364,7 +1358,7 @@ // Track the current rightmost node at each level. Uses an // ArrayList to avoid committing to initial or maximum level. - ArrayList> preds = new ArrayList>(); + ArrayList> preds = new ArrayList<>(); // initialize for (int i = 0; i <= h.level; ++i) @@ -1461,12 +1455,12 @@ * distinct because readObject calls can't be nicely adapted * as the kind of iterator needed by buildFromSorted. (They * can be, but doing so requires type cheats and/or creation - * of adaptor classes.) It is simpler to just adapt the code. + * of adapter classes.) It is simpler to just adapt the code. */ HeadIndex h = head; Node basepred = h.node; - ArrayList> preds = new ArrayList>(); + ArrayList> preds = new ArrayList<>(); for (int i = 0; i <= h.level; ++i) preds.add(null); Index q = h; @@ -1833,13 +1827,13 @@ * @return a navigable set view of the keys in this map */ public NavigableSet keySet() { - KeySet ks = keySet; - return (ks != null) ? ks : (keySet = new KeySet(this)); + KeySet ks = keySet; + return (ks != null) ? ks : (keySet = new KeySet<>(this)); } public NavigableSet navigableKeySet() { - KeySet ks = keySet; - return (ks != null) ? ks : (keySet = new KeySet(this)); + KeySet ks = keySet; + return (ks != null) ? ks : (keySet = new KeySet<>(this)); } /** @@ -1862,8 +1856,8 @@ * weakly consistent. */ public Collection values() { - Values vs = values; - return (vs != null) ? vs : (values = new Values(this)); + Values vs = values; + return (vs != null) ? vs : (values = new Values<>(this)); } /** @@ -2346,20 +2340,6 @@ } } - // Factory methods for iterators needed by ConcurrentSkipListSet etc - - Iterator keyIterator() { - return new KeyIterator(); - } - - Iterator valueIterator() { - return new ValueIterator(); - } - - Iterator> entryIterator() { - return new EntryIterator(); - } - /* ---------------- View Classes -------------- */ /* @@ -2376,36 +2356,34 @@ return list; } - static final class KeySet - extends AbstractSet implements NavigableSet { - final ConcurrentNavigableMap m; - KeySet(ConcurrentNavigableMap map) { m = map; } + static final class KeySet + extends AbstractSet implements NavigableSet { + final ConcurrentNavigableMap m; + KeySet(ConcurrentNavigableMap map) { m = map; } public int size() { return m.size(); } public boolean isEmpty() { return m.isEmpty(); } public boolean contains(Object o) { return m.containsKey(o); } public boolean remove(Object o) { return m.remove(o) != null; } public void clear() { m.clear(); } - public E lower(E e) { return m.lowerKey(e); } - public E floor(E e) { return m.floorKey(e); } - public E ceiling(E e) { return m.ceilingKey(e); } - public E higher(E e) { return m.higherKey(e); } - public Comparator comparator() { return m.comparator(); } - public E first() { return m.firstKey(); } - public E last() { return m.lastKey(); } - public E pollFirst() { - Map.Entry e = m.pollFirstEntry(); + public K lower(K e) { return m.lowerKey(e); } + public K floor(K e) { return m.floorKey(e); } + public K ceiling(K e) { return m.ceilingKey(e); } + public K higher(K e) { return m.higherKey(e); } + public Comparator comparator() { return m.comparator(); } + public K first() { return m.firstKey(); } + public K last() { return m.lastKey(); } + public K pollFirst() { + Map.Entry e = m.pollFirstEntry(); return (e == null) ? null : e.getKey(); } - public E pollLast() { - Map.Entry e = m.pollLastEntry(); + public K pollLast() { + Map.Entry e = m.pollLastEntry(); return (e == null) ? null : e.getKey(); } - @SuppressWarnings("unchecked") - public Iterator iterator() { - if (m instanceof ConcurrentSkipListMap) - return ((ConcurrentSkipListMap)m).keyIterator(); - else - return ((ConcurrentSkipListMap.SubMap)m).keyIterator(); + public Iterator iterator() { + return (m instanceof ConcurrentSkipListMap) + ? ((ConcurrentSkipListMap)m).new KeyIterator() + : ((SubMap)m).new SubMapKeyIterator(); } public boolean equals(Object o) { if (o == this) @@ -2423,87 +2401,76 @@ } public Object[] toArray() { return toList(this).toArray(); } public T[] toArray(T[] a) { return toList(this).toArray(a); } - public Iterator descendingIterator() { + public Iterator descendingIterator() { return descendingSet().iterator(); } - public NavigableSet subSet(E fromElement, + public NavigableSet subSet(K fromElement, boolean fromInclusive, - E toElement, + K toElement, boolean toInclusive) { - return new KeySet(m.subMap(fromElement, fromInclusive, - toElement, toInclusive)); + return new KeySet<>(m.subMap(fromElement, fromInclusive, + toElement, toInclusive)); } - public NavigableSet headSet(E toElement, boolean inclusive) { - return new KeySet(m.headMap(toElement, inclusive)); + public NavigableSet headSet(K toElement, boolean inclusive) { + return new KeySet<>(m.headMap(toElement, inclusive)); } - public NavigableSet tailSet(E fromElement, boolean inclusive) { - return new KeySet(m.tailMap(fromElement, inclusive)); + public NavigableSet tailSet(K fromElement, boolean inclusive) { + return new KeySet<>(m.tailMap(fromElement, inclusive)); } - public NavigableSet subSet(E fromElement, E toElement) { + public NavigableSet subSet(K fromElement, K toElement) { return subSet(fromElement, true, toElement, false); } - public NavigableSet headSet(E toElement) { + public NavigableSet headSet(K toElement) { return headSet(toElement, false); } - public NavigableSet tailSet(E fromElement) { + public NavigableSet tailSet(K fromElement) { return tailSet(fromElement, true); } - public NavigableSet descendingSet() { - return new KeySet(m.descendingMap()); + public NavigableSet descendingSet() { + return new KeySet<>(m.descendingMap()); } - @SuppressWarnings("unchecked") - public Spliterator spliterator() { - if (m instanceof ConcurrentSkipListMap) - return ((ConcurrentSkipListMap)m).keySpliterator(); - else - return (Spliterator)((SubMap)m).keyIterator(); + + public Spliterator spliterator() { + return (m instanceof ConcurrentSkipListMap) + ? ((ConcurrentSkipListMap)m).keySpliterator() + : ((SubMap)m).new SubMapKeyIterator(); } } - static final class Values extends AbstractCollection { - final ConcurrentNavigableMap m; - Values(ConcurrentNavigableMap map) { + static final class Values extends AbstractCollection { + final ConcurrentNavigableMap m; + Values(ConcurrentNavigableMap map) { m = map; } - @SuppressWarnings("unchecked") - public Iterator iterator() { - if (m instanceof ConcurrentSkipListMap) - return ((ConcurrentSkipListMap)m).valueIterator(); - else - return ((SubMap)m).valueIterator(); - } - public boolean isEmpty() { - return m.isEmpty(); + public Iterator iterator() { + return (m instanceof ConcurrentSkipListMap) + ? ((ConcurrentSkipListMap)m).new ValueIterator() + : ((SubMap)m).new SubMapValueIterator(); } - public int size() { - return m.size(); - } - public boolean contains(Object o) { - return m.containsValue(o); - } - public void clear() { - m.clear(); - } + public int size() { return m.size(); } + public boolean isEmpty() { return m.isEmpty(); } + public boolean contains(Object o) { return m.containsValue(o); } + public void clear() { m.clear(); } public Object[] toArray() { return toList(this).toArray(); } public T[] toArray(T[] a) { return toList(this).toArray(a); } - @SuppressWarnings("unchecked") - public Spliterator spliterator() { - if (m instanceof ConcurrentSkipListMap) - return ((ConcurrentSkipListMap)m).valueSpliterator(); - else - return (Spliterator)((SubMap)m).valueIterator(); + + public Spliterator spliterator() { + return (m instanceof ConcurrentSkipListMap) + ? ((ConcurrentSkipListMap)m).valueSpliterator() + : ((SubMap)m).new SubMapValueIterator(); } - public boolean removeIf(Predicate filter) { + + public boolean removeIf(Predicate filter) { if (filter == null) throw new NullPointerException(); if (m instanceof ConcurrentSkipListMap) - return ((ConcurrentSkipListMap)m).removeValueIf(filter); + return ((ConcurrentSkipListMap)m).removeValueIf(filter); // else use iterator - @SuppressWarnings("unchecked") Iterator> it = - ((SubMap)m).entryIterator(); + Iterator> it = + ((SubMap)m).new SubMapEntryIterator(); boolean removed = false; while (it.hasNext()) { - Map.Entry e = it.next(); - E v = e.getValue(); + Map.Entry e = it.next(); + V v = e.getValue(); if (filter.test(v) && m.remove(e.getKey(), v)) removed = true; } @@ -2511,24 +2478,22 @@ } } - static final class EntrySet extends AbstractSet> { - final ConcurrentNavigableMap m; - EntrySet(ConcurrentNavigableMap map) { + static final class EntrySet extends AbstractSet> { + final ConcurrentNavigableMap m; + EntrySet(ConcurrentNavigableMap map) { m = map; } - @SuppressWarnings("unchecked") - public Iterator> iterator() { - if (m instanceof ConcurrentSkipListMap) - return ((ConcurrentSkipListMap)m).entryIterator(); - else - return ((SubMap)m).entryIterator(); + public Iterator> iterator() { + return (m instanceof ConcurrentSkipListMap) + ? ((ConcurrentSkipListMap)m).new EntryIterator() + : ((SubMap)m).new SubMapEntryIterator(); } public boolean contains(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry e = (Map.Entry)o; - V1 v = m.get(e.getKey()); + V v = m.get(e.getKey()); return v != null && v.equals(e.getValue()); } public boolean remove(Object o) { @@ -2563,23 +2528,22 @@ } public Object[] toArray() { return toList(this).toArray(); } public T[] toArray(T[] a) { return toList(this).toArray(a); } - @SuppressWarnings("unchecked") - public Spliterator> spliterator() { - if (m instanceof ConcurrentSkipListMap) - return ((ConcurrentSkipListMap)m).entrySpliterator(); - else - return (Spliterator>) - ((SubMap)m).entryIterator(); + + public Spliterator> spliterator() { + return (m instanceof ConcurrentSkipListMap) + ? ((ConcurrentSkipListMap)m).entrySpliterator() + : ((SubMap)m).new SubMapEntryIterator(); } - public boolean removeIf(Predicate> filter) { + public boolean removeIf(Predicate> filter) { if (filter == null) throw new NullPointerException(); if (m instanceof ConcurrentSkipListMap) - return ((ConcurrentSkipListMap)m).removeEntryIf(filter); + return ((ConcurrentSkipListMap)m).removeEntryIf(filter); // else use iterator - Iterator> it = ((SubMap)m).entryIterator(); + Iterator> it = + ((SubMap)m).new SubMapEntryIterator(); boolean removed = false; while (it.hasNext()) { - Map.Entry e = it.next(); + Map.Entry e = it.next(); if (filter.test(e) && m.remove(e.getKey(), e.getValue())) removed = true; } @@ -2589,13 +2553,13 @@ /** * Submaps returned by {@link ConcurrentSkipListMap} submap operations - * represent a subrange of mappings of their underlying - * maps. Instances of this class support all methods of their - * underlying maps, differing in that mappings outside their range are - * ignored, and attempts to add mappings outside their ranges result - * in {@link IllegalArgumentException}. Instances of this class are - * constructed only using the {@code subMap}, {@code headMap}, and - * {@code tailMap} methods of their underlying maps. + * represent a subrange of mappings of their underlying maps. + * Instances of this class support all methods of their underlying + * maps, differing in that mappings outside their range are ignored, + * and attempts to add mappings outside their ranges result in {@link + * IllegalArgumentException}. Instances of this class are constructed + * only using the {@code subMap}, {@code headMap}, and {@code tailMap} + * methods of their underlying maps. * * @serial include */ @@ -2604,7 +2568,7 @@ private static final long serialVersionUID = -7647078645895051609L; /** Underlying map */ - private final ConcurrentSkipListMap m; + final ConcurrentSkipListMap m; /** lower bound key, or null if from start */ private final K lo; /** upper bound key, or null if to end */ @@ -2614,10 +2578,10 @@ /** inclusion flag for hi */ private final boolean hiInclusive; /** direction */ - private final boolean isDescending; + final boolean isDescending; // Lazily initialized view holders - private transient KeySet keySetView; + private transient KeySet keySetView; private transient Set> entrySetView; private transient Collection valuesView; @@ -2790,7 +2754,7 @@ } /** - * Submap version of ConcurrentSkipListMap.getNearEntry + * Submap version of ConcurrentSkipListMap.getNearEntry. */ Map.Entry getNearEntry(K key, int rel) { Comparator cmp = m.comparator; @@ -3085,18 +3049,18 @@ /* ---------------- Submap Views -------------- */ public NavigableSet keySet() { - KeySet ks = keySetView; - return (ks != null) ? ks : (keySetView = new KeySet(this)); + KeySet ks = keySetView; + return (ks != null) ? ks : (keySetView = new KeySet<>(this)); } public NavigableSet navigableKeySet() { - KeySet ks = keySetView; - return (ks != null) ? ks : (keySetView = new KeySet(this)); + KeySet ks = keySetView; + return (ks != null) ? ks : (keySetView = new KeySet<>(this)); } public Collection values() { Collection vs = valuesView; - return (vs != null) ? vs : (valuesView = new Values(this)); + return (vs != null) ? vs : (valuesView = new Values<>(this)); } public Set> entrySet() { @@ -3108,21 +3072,9 @@ return descendingMap().navigableKeySet(); } - Iterator keyIterator() { - return new SubMapKeyIterator(); - } - - Iterator valueIterator() { - return new SubMapValueIterator(); - } - - Iterator> entryIterator() { - return new SubMapEntryIterator(); - } - /** * Variant of main Iter class to traverse through submaps. - * Also serves as back-up Spliterator for views + * Also serves as back-up Spliterator for views. */ abstract class SubMapIter implements Iterator, Spliterator { /** the last node returned by next() */ @@ -3298,9 +3250,9 @@ } /** - * Helper method for EntrySet.removeIf + * Helper method for EntrySet.removeIf. */ - boolean removeEntryIf(Predicate> function) { + boolean removeEntryIf(Predicate> function) { if (function == null) throw new NullPointerException(); boolean removed = false; for (Node n = findFirst(); n != null; n = n.next) { @@ -3316,7 +3268,7 @@ } /** - * Helper method for Values.removeIf + * Helper method for Values.removeIf. */ boolean removeValueIf(Predicate function) { if (function == null) throw new NullPointerException(); @@ -3371,7 +3323,7 @@ super(comparator, row, origin, fence, est); } - public Spliterator trySplit() { + public KeySpliterator trySplit() { Node e; K ek; Comparator cmp = comparator; K f = fence; @@ -3459,7 +3411,7 @@ super(comparator, row, origin, fence, est); } - public Spliterator trySplit() { + public ValueSpliterator trySplit() { Node e; K ek; Comparator cmp = comparator; K f = fence; @@ -3546,7 +3498,7 @@ super(comparator, row, origin, fence, est); } - public Spliterator> trySplit() { + public EntrySpliterator trySplit() { Node e; K ek; Comparator cmp = comparator; K f = fence; @@ -3644,20 +3596,13 @@ } // Unsafe mechanics - private static final sun.misc.Unsafe UNSAFE; - private static final long headOffset; - private static final long SECONDARY; + private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe(); + private static final long HEAD; static { try { - UNSAFE = sun.misc.Unsafe.getUnsafe(); - Class k = ConcurrentSkipListMap.class; - headOffset = UNSAFE.objectFieldOffset - (k.getDeclaredField("head")); - Class tk = Thread.class; - SECONDARY = UNSAFE.objectFieldOffset - (tk.getDeclaredField("threadLocalRandomSecondarySeed")); - - } catch (Exception e) { + HEAD = U.objectFieldOffset + (ConcurrentSkipListMap.class.getDeclaredField("head")); + } catch (ReflectiveOperationException e) { throw new Error(e); } } diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListSet.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListSet.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListSet.java Wed Jul 05 20:54:48 2017 +0200 @@ -34,6 +34,7 @@ */ package java.util.concurrent; + import java.util.AbstractSet; import java.util.Collection; import java.util.Collections; @@ -323,8 +324,9 @@ * * @param c collection containing elements to be removed from this set * @return {@code true} if this set changed as a result of the call - * @throws ClassCastException if the types of one or more elements in this - * set are incompatible with the specified collection + * @throws ClassCastException if the class of an element of this set + * is incompatible with the specified collection + * (optional) * @throws NullPointerException if the specified collection or any * of its elements are null */ @@ -384,7 +386,6 @@ /* ---------------- SortedSet operations -------------- */ - public Comparator comparator() { return m.comparator(); } @@ -498,28 +499,24 @@ * @return a {@code Spliterator} over the elements in this set * @since 1.8 */ - @SuppressWarnings("unchecked") public Spliterator spliterator() { - if (m instanceof ConcurrentSkipListMap) - return ((ConcurrentSkipListMap)m).keySpliterator(); - else - return (Spliterator)((ConcurrentSkipListMap.SubMap)m).keyIterator(); + return (m instanceof ConcurrentSkipListMap) + ? ((ConcurrentSkipListMap)m).keySpliterator() + : ((ConcurrentSkipListMap.SubMap)m).new SubMapKeyIterator(); } // Support for resetting map in clone private void setMap(ConcurrentNavigableMap map) { - UNSAFE.putObjectVolatile(this, mapOffset, map); + U.putObjectVolatile(this, MAP, map); } - private static final sun.misc.Unsafe UNSAFE; - private static final long mapOffset; + private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe(); + private static final long MAP; static { try { - UNSAFE = sun.misc.Unsafe.getUnsafe(); - Class k = ConcurrentSkipListSet.class; - mapOffset = UNSAFE.objectFieldOffset - (k.getDeclaredField("m")); - } catch (Exception e) { + MAP = U.objectFieldOffset + (ConcurrentSkipListSet.class.getDeclaredField("m")); + } catch (ReflectiveOperationException e) { throw new Error(e); } } diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArrayList.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArrayList.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArrayList.java Wed Jul 05 20:54:48 2017 +0200 @@ -33,6 +33,7 @@ */ package java.util.concurrent; + import java.util.AbstractList; import java.util.Arrays; import java.util.Collection; @@ -46,7 +47,6 @@ import java.util.RandomAccess; import java.util.Spliterator; import java.util.Spliterators; -import java.util.concurrent.locks.ReentrantLock; import java.util.function.Consumer; import java.util.function.Predicate; import java.util.function.UnaryOperator; @@ -86,14 +86,17 @@ * * @since 1.5 * @author Doug Lea - * @param the type of elements held in this collection + * @param the type of elements held in this list */ public class CopyOnWriteArrayList implements List, RandomAccess, Cloneable, java.io.Serializable { private static final long serialVersionUID = 8673264195747942595L; - /** The lock protecting all mutators */ - final transient ReentrantLock lock = new ReentrantLock(); + /** + * The lock protecting all mutators. (We have a mild preference + * for builtin monitors over ReentrantLock when either will do.) + */ + final transient Object lock = new Object(); /** The array, accessed only via getArray/setArray. */ private transient volatile Object[] array; @@ -172,13 +175,6 @@ } /** - * Tests for equality, coping with nulls. - */ - private static boolean eq(Object o1, Object o2) { - return (o1 == null) ? o2 == null : o1.equals(o2); - } - - /** * static version of indexOf, to allow repeated calls without * needing to re-acquire array each time. * @param o element to search for @@ -224,8 +220,7 @@ /** * Returns {@code true} if this list contains the specified element. * More formally, returns {@code true} if and only if this list contains - * at least one element {@code e} such that - * (o==null ? e==null : o.equals(e)). + * at least one element {@code e} such that {@code Objects.equals(o, e)}. * * @param o element whose presence in this list is to be tested * @return {@code true} if this list contains the specified element @@ -248,7 +243,7 @@ * this list, searching forwards from {@code index}, or returns -1 if * the element is not found. * More formally, returns the lowest index {@code i} such that - * (i >= index && (e==null ? get(i)==null : e.equals(get(i)))), + * {@code i >= index && Objects.equals(get(i), e)}, * or -1 if there is no such index. * * @param e element to search for @@ -276,7 +271,7 @@ * this list, searching backwards from {@code index}, or returns -1 if * the element is not found. * More formally, returns the highest index {@code i} such that - * (i <= index && (e==null ? get(i)==null : e.equals(get(i)))), + * {@code i <= index && Objects.equals(get(i), e)}, * or -1 if there is no such index. * * @param e element to search for @@ -353,7 +348,7 @@ * The following code can be used to dump the list into a newly * allocated array of {@code String}: * - *

     {@code String[] y = x.toArray(new String[0]);}
    + *
     {@code String[] y = x.toArray(new String[0]);}
    * * Note that {@code toArray(new Object[0])} is identical in function to * {@code toArray()}. @@ -368,7 +363,7 @@ * @throws NullPointerException if the specified array is null */ @SuppressWarnings("unchecked") - public T[] toArray(T a[]) { + public T[] toArray(T[] a) { Object[] elements = getArray(); int len = elements.length; if (a.length < len) @@ -388,6 +383,10 @@ return (E) a[index]; } + static String outOfBounds(int index, int size) { + return "Index: " + index + ", Size: " + size; + } + /** * {@inheritDoc} * @@ -404,9 +403,7 @@ * @throws IndexOutOfBoundsException {@inheritDoc} */ public E set(int index, E element) { - final ReentrantLock lock = this.lock; - lock.lock(); - try { + synchronized (lock) { Object[] elements = getArray(); E oldValue = get(elements, index); @@ -420,8 +417,6 @@ setArray(elements); } return oldValue; - } finally { - lock.unlock(); } } @@ -432,17 +427,13 @@ * @return {@code true} (as specified by {@link Collection#add}) */ public boolean add(E e) { - final ReentrantLock lock = this.lock; - lock.lock(); - try { + synchronized (lock) { Object[] elements = getArray(); int len = elements.length; Object[] newElements = Arrays.copyOf(elements, len + 1); newElements[len] = e; setArray(newElements); return true; - } finally { - lock.unlock(); } } @@ -454,14 +445,11 @@ * @throws IndexOutOfBoundsException {@inheritDoc} */ public void add(int index, E element) { - final ReentrantLock lock = this.lock; - lock.lock(); - try { + synchronized (lock) { Object[] elements = getArray(); int len = elements.length; if (index > len || index < 0) - throw new IndexOutOfBoundsException("Index: "+index+ - ", Size: "+len); + throw new IndexOutOfBoundsException(outOfBounds(index, len)); Object[] newElements; int numMoved = len - index; if (numMoved == 0) @@ -474,8 +462,6 @@ } newElements[index] = element; setArray(newElements); - } finally { - lock.unlock(); } } @@ -487,9 +473,7 @@ * @throws IndexOutOfBoundsException {@inheritDoc} */ public E remove(int index) { - final ReentrantLock lock = this.lock; - lock.lock(); - try { + synchronized (lock) { Object[] elements = getArray(); int len = elements.length; E oldValue = get(elements, index); @@ -504,8 +488,6 @@ setArray(newElements); } return oldValue; - } finally { - lock.unlock(); } } @@ -513,8 +495,7 @@ * Removes the first occurrence of the specified element from this list, * if it is present. If this list does not contain the element, it is * unchanged. More formally, removes the element with the lowest index - * {@code i} such that - * (o==null ? get(i)==null : o.equals(get(i))) + * {@code i} such that {@code Objects.equals(o, get(i))} * (if such an element exists). Returns {@code true} if this list * contained the specified element (or equivalently, if this list * changed as a result of the call). @@ -533,15 +514,14 @@ * recent snapshot contains o at the given index. */ private boolean remove(Object o, Object[] snapshot, int index) { - final ReentrantLock lock = this.lock; - lock.lock(); - try { + synchronized (lock) { Object[] current = getArray(); int len = current.length; if (snapshot != current) findIndex: { int prefix = Math.min(index, len); for (int i = 0; i < prefix; i++) { - if (current[i] != snapshot[i] && eq(o, current[i])) { + if (current[i] != snapshot[i] + && Objects.equals(o, current[i])) { index = i; break findIndex; } @@ -561,8 +541,6 @@ len - index - 1); setArray(newElements); return true; - } finally { - lock.unlock(); } } @@ -579,9 +557,7 @@ * ({@code fromIndex < 0 || toIndex > size() || toIndex < fromIndex}) */ void removeRange(int fromIndex, int toIndex) { - final ReentrantLock lock = this.lock; - lock.lock(); - try { + synchronized (lock) { Object[] elements = getArray(); int len = elements.length; @@ -598,8 +574,6 @@ fromIndex, numMoved); setArray(newElements); } - } finally { - lock.unlock(); } } @@ -620,16 +594,15 @@ * recent snapshot does not contain e. */ private boolean addIfAbsent(E e, Object[] snapshot) { - final ReentrantLock lock = this.lock; - lock.lock(); - try { + synchronized (lock) { Object[] current = getArray(); int len = current.length; if (snapshot != current) { // Optimize for lost race to another addXXX operation int common = Math.min(snapshot.length, len); for (int i = 0; i < common; i++) - if (current[i] != snapshot[i] && eq(e, current[i])) + if (current[i] != snapshot[i] + && Objects.equals(e, current[i])) return false; if (indexOf(e, current, common, len) >= 0) return false; @@ -638,8 +611,6 @@ newElements[len] = e; setArray(newElements); return true; - } finally { - lock.unlock(); } } @@ -672,18 +643,16 @@ * @return {@code true} if this list changed as a result of the call * @throws ClassCastException if the class of an element of this list * is incompatible with the specified collection - * (optional) + * (optional) * @throws NullPointerException if this list contains a null element and the * specified collection does not permit null elements - * (optional), + * (optional), * or if the specified collection is null * @see #remove(Object) */ public boolean removeAll(Collection c) { if (c == null) throw new NullPointerException(); - final ReentrantLock lock = this.lock; - lock.lock(); - try { + synchronized (lock) { Object[] elements = getArray(); int len = elements.length; if (len != 0) { @@ -701,8 +670,6 @@ } } return false; - } finally { - lock.unlock(); } } @@ -715,18 +682,16 @@ * @return {@code true} if this list changed as a result of the call * @throws ClassCastException if the class of an element of this list * is incompatible with the specified collection - * (optional) + * (optional) * @throws NullPointerException if this list contains a null element and the * specified collection does not permit null elements - * (optional), + * (optional), * or if the specified collection is null * @see #remove(Object) */ public boolean retainAll(Collection c) { if (c == null) throw new NullPointerException(); - final ReentrantLock lock = this.lock; - lock.lock(); - try { + synchronized (lock) { Object[] elements = getArray(); int len = elements.length; if (len != 0) { @@ -744,8 +709,6 @@ } } return false; - } finally { - lock.unlock(); } } @@ -764,9 +727,7 @@ Object[] cs = c.toArray(); if (cs.length == 0) return 0; - final ReentrantLock lock = this.lock; - lock.lock(); - try { + synchronized (lock) { Object[] elements = getArray(); int len = elements.length; int added = 0; @@ -783,8 +744,6 @@ setArray(newElements); } return added; - } finally { - lock.unlock(); } } @@ -793,12 +752,8 @@ * The list will be empty after this call returns. */ public void clear() { - final ReentrantLock lock = this.lock; - lock.lock(); - try { + synchronized (lock) { setArray(new Object[0]); - } finally { - lock.unlock(); } } @@ -817,9 +772,7 @@ ((CopyOnWriteArrayList)c).getArray() : c.toArray(); if (cs.length == 0) return false; - final ReentrantLock lock = this.lock; - lock.lock(); - try { + synchronized (lock) { Object[] elements = getArray(); int len = elements.length; if (len == 0 && cs.getClass() == Object[].class) @@ -830,8 +783,6 @@ setArray(newElements); } return true; - } finally { - lock.unlock(); } } @@ -853,14 +804,11 @@ */ public boolean addAll(int index, Collection c) { Object[] cs = c.toArray(); - final ReentrantLock lock = this.lock; - lock.lock(); - try { + synchronized (lock) { Object[] elements = getArray(); int len = elements.length; if (index > len || index < 0) - throw new IndexOutOfBoundsException("Index: "+index+ - ", Size: "+len); + throw new IndexOutOfBoundsException(outOfBounds(index, len)); if (cs.length == 0) return false; int numMoved = len - index; @@ -877,52 +825,47 @@ System.arraycopy(cs, 0, newElements, index, cs.length); setArray(newElements); return true; - } finally { - lock.unlock(); } } public void forEach(Consumer action) { if (action == null) throw new NullPointerException(); - Object[] elements = getArray(); - int len = elements.length; - for (int i = 0; i < len; ++i) { - @SuppressWarnings("unchecked") E e = (E) elements[i]; + for (Object x : getArray()) { + @SuppressWarnings("unchecked") E e = (E) x; action.accept(e); } } public boolean removeIf(Predicate filter) { if (filter == null) throw new NullPointerException(); - final ReentrantLock lock = this.lock; - lock.lock(); - try { - Object[] elements = getArray(); - int len = elements.length; - if (len != 0) { - int newlen = 0; - Object[] temp = new Object[len]; - for (int i = 0; i < len; ++i) { - @SuppressWarnings("unchecked") E e = (E) elements[i]; - if (!filter.test(e)) - temp[newlen++] = e; - } - if (newlen != len) { - setArray(Arrays.copyOf(temp, newlen)); + synchronized (lock) { + final Object[] elements = getArray(); + final int len = elements.length; + int i; + for (i = 0; i < len; i++) { + @SuppressWarnings("unchecked") E e = (E) elements[i]; + if (filter.test(e)) { + int newlen = i; + final Object[] newElements = new Object[len - 1]; + System.arraycopy(elements, 0, newElements, 0, newlen); + for (i++; i < len; i++) { + @SuppressWarnings("unchecked") E x = (E) elements[i]; + if (!filter.test(x)) + newElements[newlen++] = x; + } + setArray((newlen == len - 1) + ? newElements // one match => one copy + : Arrays.copyOf(newElements, newlen)); return true; } } - return false; - } finally { - lock.unlock(); + return false; // zero matches => zero copies } } public void replaceAll(UnaryOperator operator) { if (operator == null) throw new NullPointerException(); - final ReentrantLock lock = this.lock; - lock.lock(); - try { + synchronized (lock) { Object[] elements = getArray(); int len = elements.length; Object[] newElements = Arrays.copyOf(elements, len); @@ -931,22 +874,16 @@ newElements[i] = operator.apply(e); } setArray(newElements); - } finally { - lock.unlock(); } } public void sort(Comparator c) { - final ReentrantLock lock = this.lock; - lock.lock(); - try { + synchronized (lock) { Object[] elements = getArray(); Object[] newElements = Arrays.copyOf(elements, elements.length); @SuppressWarnings("unchecked") E[] es = (E[])newElements; Arrays.sort(es, c); setArray(newElements); - } finally { - lock.unlock(); } } @@ -1022,7 +959,7 @@ * be the same if they have the same length and corresponding * elements at the same position in the sequence are equal. * Two elements {@code e1} and {@code e2} are considered - * equal if {@code (e1==null ? e2==null : e1.equals(e2))}. + * equal if {@code Objects.equals(e1, e2)}. * * @param o the object to be compared for equality with this list * @return {@code true} if the specified object is equal to this list @@ -1033,12 +970,11 @@ if (!(o instanceof List)) return false; - List list = (List)(o); + List list = (List)o; Iterator it = list.iterator(); Object[] elements = getArray(); - int len = elements.length; - for (int i = 0; i < len; ++i) - if (!it.hasNext() || !eq(elements[i], it.next())) + for (int i = 0, len = elements.length; i < len; i++) + if (!it.hasNext() || !Objects.equals(elements[i], it.next())) return false; if (it.hasNext()) return false; @@ -1054,12 +990,8 @@ */ public int hashCode() { int hashCode = 1; - Object[] elements = getArray(); - int len = elements.length; - for (int i = 0; i < len; ++i) { - Object obj = elements[i]; - hashCode = 31*hashCode + (obj==null ? 0 : obj.hashCode()); - } + for (Object x : getArray()) + hashCode = 31 * hashCode + (x == null ? 0 : x.hashCode()); return hashCode; } @@ -1103,7 +1035,7 @@ Object[] elements = getArray(); int len = elements.length; if (index < 0 || index > len) - throw new IndexOutOfBoundsException("Index: "+index); + throw new IndexOutOfBoundsException(outOfBounds(index, len)); return new COWIterator(elements, index); } @@ -1133,7 +1065,7 @@ /** Index of element to be returned by subsequent call to next. */ private int cursor; - private COWIterator(Object[] elements, int initialCursor) { + COWIterator(Object[] elements, int initialCursor) { cursor = initialCursor; snapshot = elements; } @@ -1196,13 +1128,12 @@ } @Override + @SuppressWarnings("unchecked") public void forEachRemaining(Consumer action) { Objects.requireNonNull(action); - Object[] elements = snapshot; - final int size = elements.length; + final int size = snapshot.length; for (int i = cursor; i < size; i++) { - @SuppressWarnings("unchecked") E e = (E) elements[i]; - action.accept(e); + action.accept((E) snapshot[i]); } cursor = size; } @@ -1224,16 +1155,12 @@ * @throws IndexOutOfBoundsException {@inheritDoc} */ public List subList(int fromIndex, int toIndex) { - final ReentrantLock lock = this.lock; - lock.lock(); - try { + synchronized (lock) { Object[] elements = getArray(); int len = elements.length; if (fromIndex < 0 || toIndex > len || fromIndex > toIndex) throw new IndexOutOfBoundsException(); return new COWSubList(this, fromIndex, toIndex); - } finally { - lock.unlock(); } } @@ -1264,6 +1191,7 @@ // only call this holding l's lock COWSubList(CopyOnWriteArrayList list, int fromIndex, int toIndex) { + // assert Thread.holdsLock(list.lock); l = list; expectedArray = l.getArray(); offset = fromIndex; @@ -1272,94 +1200,72 @@ // only call this holding l's lock private void checkForComodification() { + // assert Thread.holdsLock(l.lock); if (l.getArray() != expectedArray) throw new ConcurrentModificationException(); } // only call this holding l's lock private void rangeCheck(int index) { + // assert Thread.holdsLock(l.lock); if (index < 0 || index >= size) - throw new IndexOutOfBoundsException("Index: "+index+ - ",Size: "+size); + throw new IndexOutOfBoundsException(outOfBounds(index, size)); } public E set(int index, E element) { - final ReentrantLock lock = l.lock; - lock.lock(); - try { + synchronized (l.lock) { rangeCheck(index); checkForComodification(); E x = l.set(index+offset, element); expectedArray = l.getArray(); return x; - } finally { - lock.unlock(); } } public E get(int index) { - final ReentrantLock lock = l.lock; - lock.lock(); - try { + synchronized (l.lock) { rangeCheck(index); checkForComodification(); return l.get(index+offset); - } finally { - lock.unlock(); } } public int size() { - final ReentrantLock lock = l.lock; - lock.lock(); - try { + synchronized (l.lock) { checkForComodification(); return size; - } finally { - lock.unlock(); } } public void add(int index, E element) { - final ReentrantLock lock = l.lock; - lock.lock(); - try { + synchronized (l.lock) { checkForComodification(); if (index < 0 || index > size) - throw new IndexOutOfBoundsException(); + throw new IndexOutOfBoundsException + (outOfBounds(index, size)); l.add(index+offset, element); expectedArray = l.getArray(); size++; - } finally { - lock.unlock(); } } public void clear() { - final ReentrantLock lock = l.lock; - lock.lock(); - try { + synchronized (l.lock) { checkForComodification(); l.removeRange(offset, offset+size); expectedArray = l.getArray(); size = 0; - } finally { - lock.unlock(); } } public E remove(int index) { - final ReentrantLock lock = l.lock; - lock.lock(); - try { + synchronized (l.lock) { rangeCheck(index); checkForComodification(); E result = l.remove(index+offset); expectedArray = l.getArray(); size--; return result; - } finally { - lock.unlock(); } } @@ -1372,41 +1278,29 @@ } public Iterator iterator() { - final ReentrantLock lock = l.lock; - lock.lock(); - try { + synchronized (l.lock) { checkForComodification(); return new COWSubListIterator(l, 0, offset, size); - } finally { - lock.unlock(); } } public ListIterator listIterator(int index) { - final ReentrantLock lock = l.lock; - lock.lock(); - try { + synchronized (l.lock) { checkForComodification(); if (index < 0 || index > size) - throw new IndexOutOfBoundsException("Index: "+index+ - ", Size: "+size); + throw new IndexOutOfBoundsException + (outOfBounds(index, size)); return new COWSubListIterator(l, index, offset, size); - } finally { - lock.unlock(); } } public List subList(int fromIndex, int toIndex) { - final ReentrantLock lock = l.lock; - lock.lock(); - try { + synchronized (l.lock) { checkForComodification(); if (fromIndex < 0 || toIndex > size || fromIndex > toIndex) throw new IndexOutOfBoundsException(); return new COWSubList(l, fromIndex + offset, toIndex + offset); - } finally { - lock.unlock(); } } @@ -1427,9 +1321,7 @@ public void replaceAll(UnaryOperator operator) { if (operator == null) throw new NullPointerException(); - final ReentrantLock lock = l.lock; - lock.lock(); - try { + synchronized (l.lock) { int lo = offset; int hi = offset + size; Object[] elements = expectedArray; @@ -1444,15 +1336,11 @@ newElements[i] = operator.apply(e); } l.setArray(expectedArray = newElements); - } finally { - lock.unlock(); } } public void sort(Comparator c) { - final ReentrantLock lock = l.lock; - lock.lock(); - try { + synchronized (l.lock) { int lo = offset; int hi = offset + size; Object[] elements = expectedArray; @@ -1465,17 +1353,13 @@ @SuppressWarnings("unchecked") E[] es = (E[])newElements; Arrays.sort(es, lo, hi, c); l.setArray(expectedArray = newElements); - } finally { - lock.unlock(); } } public boolean removeAll(Collection c) { if (c == null) throw new NullPointerException(); boolean removed = false; - final ReentrantLock lock = l.lock; - lock.lock(); - try { + synchronized (l.lock) { int n = size; if (n > 0) { int lo = offset; @@ -1504,8 +1388,6 @@ l.setArray(expectedArray = newElements); } } - } finally { - lock.unlock(); } return removed; } @@ -1513,9 +1395,7 @@ public boolean retainAll(Collection c) { if (c == null) throw new NullPointerException(); boolean removed = false; - final ReentrantLock lock = l.lock; - lock.lock(); - try { + synchronized (l.lock) { int n = size; if (n > 0) { int lo = offset; @@ -1544,8 +1424,6 @@ l.setArray(expectedArray = newElements); } } - } finally { - lock.unlock(); } return removed; } @@ -1553,9 +1431,7 @@ public boolean removeIf(Predicate filter) { if (filter == null) throw new NullPointerException(); boolean removed = false; - final ReentrantLock lock = l.lock; - lock.lock(); - try { + synchronized (l.lock) { int n = size; if (n > 0) { int lo = offset; @@ -1584,8 +1460,6 @@ l.setArray(expectedArray = newElements); } } - } finally { - lock.unlock(); } return removed; } @@ -1658,29 +1532,26 @@ } @Override + @SuppressWarnings("unchecked") public void forEachRemaining(Consumer action) { Objects.requireNonNull(action); - int s = size; - ListIterator i = it; - while (nextIndex() < s) { - action.accept(i.next()); + while (nextIndex() < size) { + action.accept(it.next()); } } } // Support for resetting lock while deserializing private void resetLock() { - UNSAFE.putObjectVolatile(this, lockOffset, new ReentrantLock()); + U.putObjectVolatile(this, LOCK, new Object()); } - private static final sun.misc.Unsafe UNSAFE; - private static final long lockOffset; + private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe(); + private static final long LOCK; static { try { - UNSAFE = sun.misc.Unsafe.getUnsafe(); - Class k = CopyOnWriteArrayList.class; - lockOffset = UNSAFE.objectFieldOffset - (k.getDeclaredField("lock")); - } catch (Exception e) { + LOCK = U.objectFieldOffset + (CopyOnWriteArrayList.class.getDeclaredField("lock")); + } catch (ReflectiveOperationException e) { throw new Error(e); } } diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArraySet.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArraySet.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArraySet.java Wed Jul 05 20:54:48 2017 +0200 @@ -34,14 +34,16 @@ */ package java.util.concurrent; + +import java.util.AbstractSet; import java.util.Collection; +import java.util.Iterator; +import java.util.Objects; import java.util.Set; -import java.util.AbstractSet; -import java.util.Iterator; import java.util.Spliterator; import java.util.Spliterators; +import java.util.function.Consumer; import java.util.function.Predicate; -import java.util.function.Consumer; /** * A {@link java.util.Set} that uses an internal {@link CopyOnWriteArrayList} @@ -66,12 +68,12 @@ * copy-on-write set to maintain a set of Handler objects that * perform some action upon state updates. * - *
     {@code
    + * 
     {@code
      * class Handler { void handle(); ... }
      *
      * class X {
      *   private final CopyOnWriteArraySet handlers
    - *     = new CopyOnWriteArraySet();
    + *     = new CopyOnWriteArraySet<>();
      *   public void addHandler(Handler h) { handlers.add(h); }
      *
      *   private long internalState;
    @@ -91,7 +93,7 @@
      * @see CopyOnWriteArrayList
      * @since 1.5
      * @author Doug Lea
    - * @param  the type of elements held in this collection
    + * @param  the type of elements held in this set
      */
     public class CopyOnWriteArraySet extends AbstractSet
             implements java.io.Serializable {
    @@ -146,8 +148,7 @@
         /**
          * Returns {@code true} if this set contains the specified element.
          * More formally, returns {@code true} if and only if this set
    -     * contains an element {@code e} such that
    -     * (o==null ? e==null : o.equals(e)).
    +     * contains an element {@code e} such that {@code Objects.equals(o, e)}.
          *
          * @param o element whose presence in this set is to be tested
          * @return {@code true} if this set contains the specified element
    @@ -203,7 +204,7 @@
          * The following code can be used to dump the set into a newly allocated
          * array of {@code String}:
          *
    -     *  
     {@code String[] y = x.toArray(new String[0]);}
    + *
     {@code String[] y = x.toArray(new String[0]);}
    * * Note that {@code toArray(new Object[0])} is identical in function to * {@code toArray()}. @@ -232,11 +233,10 @@ /** * Removes the specified element from this set if it is present. * More formally, removes an element {@code e} such that - * (o==null ? e==null : o.equals(e)), - * if this set contains such an element. Returns {@code true} if - * this set contained the element (or equivalently, if this set - * changed as a result of the call). (This set will not contain the - * element once the call returns.) + * {@code Objects.equals(o, e)}, if this set contains such an element. + * Returns {@code true} if this set contained the element (or + * equivalently, if this set changed as a result of the call). + * (This set will not contain the element once the call returns.) * * @param o object to be removed from this set, if present * @return {@code true} if this set contained the specified element @@ -249,7 +249,7 @@ * Adds the specified element to this set if it is not already present. * More formally, adds the specified element {@code e} to this set if * the set contains no element {@code e2} such that - * (e==null ? e2==null : e.equals(e2)). + * {@code Objects.equals(e, e2)}. * If this set already contains the element, the call leaves the set * unchanged and returns {@code false}. * @@ -273,7 +273,44 @@ * @see #contains(Object) */ public boolean containsAll(Collection c) { - return al.containsAll(c); + return (c instanceof Set) + ? compareSets(al.getArray(), (Set) c) >= 0 + : al.containsAll(c); + } + + /** + * Tells whether the objects in snapshot (regarded as a set) are a + * superset of the given set. + * + * @return -1 if snapshot is not a superset, 0 if the two sets + * contain precisely the same elements, and 1 if snapshot is a + * proper superset of the given set + */ + private static int compareSets(Object[] snapshot, Set set) { + // Uses O(n^2) algorithm, that is only appropriate for small + // sets, which CopyOnWriteArraySets should be. + // + // Optimize up to O(n) if the two sets share a long common prefix, + // as might happen if one set was created as a copy of the other set. + + final int len = snapshot.length; + // Mark matched elements to avoid re-checking + final boolean[] matched = new boolean[len]; + + // j is the largest int with matched[i] true for { i | 0 <= i < j } + int j = 0; + outer: for (Object x : set) { + for (int i = j; i < len; i++) { + if (!matched[i] && Objects.equals(x, snapshot[i])) { + matched[i] = true; + if (i == j) + do { j++; } while (j < len && matched[j]); + continue outer; + } + } + return -1; + } + return (j == len) ? 0 : 1; } /** @@ -302,9 +339,11 @@ * @param c collection containing elements to be removed from this set * @return {@code true} if this set changed as a result of the call * @throws ClassCastException if the class of an element of this set - * is incompatible with the specified collection (optional) + * is incompatible with the specified collection + * (optional) * @throws NullPointerException if this set contains a null element and the - * specified collection does not permit null elements (optional), + * specified collection does not permit null elements + * (optional), * or if the specified collection is null * @see #remove(Object) */ @@ -323,9 +362,11 @@ * @param c collection containing elements to be retained in this set * @return {@code true} if this set changed as a result of the call * @throws ClassCastException if the class of an element of this set - * is incompatible with the specified collection (optional) + * is incompatible with the specified collection + * (optional) * @throws NullPointerException if this set contains a null element and the - * specified collection does not permit null elements (optional), + * specified collection does not permit null elements + * (optional), * or if the specified collection is null * @see #remove(Object) */ @@ -359,41 +400,15 @@ * number of elements and for every element {@code e1} returned by * the iterator over the specified set, there is an element * {@code e2} returned by the iterator over this set such that - * {@code (e1==null ? e2==null : e1.equals(e2))}. + * {@code Objects.equals(e1, e2)}. * * @param o object to be compared for equality with this set * @return {@code true} if the specified object is equal to this set */ public boolean equals(Object o) { - if (o == this) - return true; - if (!(o instanceof Set)) - return false; - Set set = (Set)(o); - Iterator it = set.iterator(); - - // Uses O(n^2) algorithm that is only appropriate - // for small sets, which CopyOnWriteArraySets should be. - - // Use a single snapshot of underlying array - Object[] elements = al.getArray(); - int len = elements.length; - // Mark matched elements to avoid re-checking - boolean[] matched = new boolean[len]; - int k = 0; - outer: while (it.hasNext()) { - if (++k > len) - return false; - Object x = it.next(); - for (int i = 0; i < len; ++i) { - if (!matched[i] && eq(x, elements[i])) { - matched[i] = true; - continue outer; - } - } - return false; - } - return k == len; + return (o == this) + || ((o instanceof Set) + && compareSets(al.getArray(), (Set) o) == 0); } public boolean removeIf(Predicate filter) { @@ -423,11 +438,4 @@ return Spliterators.spliterator (al.getArray(), Spliterator.IMMUTABLE | Spliterator.DISTINCT); } - - /** - * Tests for equality, coping with nulls. - */ - private static boolean eq(Object o1, Object o2) { - return (o1 == null) ? o2 == null : o1.equals(o2); - } } diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/CountDownLatch.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/CountDownLatch.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/CountDownLatch.java Wed Jul 05 20:54:48 2017 +0200 @@ -34,6 +34,7 @@ */ package java.util.concurrent; + import java.util.concurrent.locks.AbstractQueuedSynchronizer; /** @@ -72,7 +73,7 @@ * until all workers have completed. * * - *
     {@code
    + * 
     {@code
      * class Driver { // ...
      *   void main() throws InterruptedException {
      *     CountDownLatch startSignal = new CountDownLatch(1);
    @@ -113,7 +114,7 @@
      * will be able to pass through await. (When threads must repeatedly
      * count down in this way, instead use a {@link CyclicBarrier}.)
      *
    - *  
     {@code
    + * 
     {@code
      * class Driver2 { // ...
      *   void main() throws InterruptedException {
      *     CountDownLatch doneSignal = new CountDownLatch(N);
    @@ -179,7 +180,7 @@
                     int c = getState();
                     if (c == 0)
                         return false;
    -                int nextc = c-1;
    +                int nextc = c - 1;
                     if (compareAndSetState(c, nextc))
                         return nextc == 0;
                 }
    diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/CountedCompleter.java
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/CountedCompleter.java	Wed Oct 21 15:15:26 2015 -0700
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/CountedCompleter.java	Wed Jul 05 20:54:48 2017 +0200
    @@ -168,7 +168,8 @@
      * {@code tryComplete}) the pending count is set to one:
      *
      * 
     {@code
    - * class ForEach ...
    + * class ForEach ... {
    + *   ...
      *   public void compute() { // version 2
      *     if (hi - lo >= 2) {
      *       int mid = (lo + hi) >>> 1;
    @@ -182,7 +183,7 @@
      *       tryComplete();
      *     }
      *   }
    - * }
    + * }}
    * * As a further improvement, notice that the left task need not even exist. * Instead of creating a new one, we can iterate using the original task, @@ -191,9 +192,10 @@ * {@code tryComplete()} can be replaced with {@link #propagateCompletion}. * *
     {@code
    - * class ForEach ...
    + * class ForEach ... {
    + *   ...
      *   public void compute() { // version 3
    - *     int l = lo,  h = hi;
    + *     int l = lo, h = hi;
      *     while (h - l >= 2) {
      *       int mid = (l + h) >>> 1;
      *       addToPendingCount(1);
    @@ -204,7 +206,7 @@
      *       op.apply(array[l]);
      *     propagateCompletion();
      *   }
    - * }
    + * }}
    * * Additional improvements of such classes might entail precomputing * pending counts so that they can be established in constructors, @@ -233,7 +235,7 @@ * } * public E getRawResult() { return result.get(); } * public void compute() { // similar to ForEach version 3 - * int l = lo, h = hi; + * int l = lo, h = hi; * while (result.get() == null && h >= l) { * if (h - l >= 2) { * int mid = (l + h) >>> 1; @@ -363,7 +365,7 @@ * this.next = next; * } * public void compute() { - * int l = lo, h = hi; + * int l = lo, h = hi; * while (h - l >= 2) { * int mid = (l + h) >>> 1; * addToPendingCount(1); @@ -374,7 +376,7 @@ * result = mapper.apply(array[l]); * // process completions by reducing along and advancing subtask links * for (CountedCompleter c = firstComplete(); c != null; c = c.nextComplete()) { - * for (MapReducer t = (MapReducer)c, s = t.forks; s != null; s = t.forks = s.next) + * for (MapReducer t = (MapReducer)c, s = t.forks; s != null; s = t.forks = s.next) * t.result = reducer.apply(t.result, s.result); * } * } @@ -402,8 +404,7 @@ * // sample use: * PacketSender p = new PacketSender(); * new HeaderBuilder(p, ...).fork(); - * new BodyBuilder(p, ...).fork(); - * }
    + * new BodyBuilder(p, ...).fork();}
    * * @since 1.8 * @author Doug Lea @@ -733,7 +734,7 @@ } /** - * Returns the result of the computation. By default + * Returns the result of the computation. By default, * returns {@code null}, which is appropriate for {@code Void} * actions, but in other cases should be overridden, almost * always to return a field or function of a field that @@ -753,14 +754,13 @@ protected void setRawResult(T t) { } // Unsafe mechanics - private static final sun.misc.Unsafe U; + private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe(); private static final long PENDING; static { try { - U = sun.misc.Unsafe.getUnsafe(); PENDING = U.objectFieldOffset (CountedCompleter.class.getDeclaredField("pending")); - } catch (Exception e) { + } catch (ReflectiveOperationException e) { throw new Error(e); } } diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/CyclicBarrier.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/CyclicBarrier.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/CyclicBarrier.java Wed Jul 05 20:54:48 2017 +0200 @@ -34,6 +34,7 @@ */ package java.util.concurrent; + import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; @@ -54,7 +55,7 @@ *

    Sample usage: Here is an example of using a barrier in a * parallel decomposition design: * - *

     {@code
    + * 
     {@code
      * class Solver {
      *   final int N;
      *   final float[][] data;
    @@ -85,7 +86,7 @@
      *       new Runnable() { public void run() { mergeRows(...); }};
      *     barrier = new CyclicBarrier(N, barrierAction);
      *
    - *     List threads = new ArrayList(N);
    + *     List threads = new ArrayList<>(N);
      *     for (int i = 0; i < N; i++) {
      *       Thread thread = new Thread(new Worker(i));
      *       threads.add(thread);
    @@ -111,7 +112,7 @@
      * {@link #await} returns the arrival index of that thread at the barrier.
      * You can then choose which thread should execute the barrier action, for
      * example:
    - *  
     {@code
    + * 
     {@code
      * if (barrier.await() == 0) {
      *   // log the completion of this iteration
      * }}
    @@ -149,7 +150,7 @@ * but no subsequent reset. */ private static class Generation { - boolean broken = false; + boolean broken; // initially false } /** The lock for guarding barrier entry */ @@ -158,7 +159,7 @@ private final Condition trip = lock.newCondition(); /** The number of parties */ private final int parties; - /* The command to run when tripped */ + /** The command to run when tripped */ private final Runnable barrierCommand; /** The current generation */ private Generation generation = new Generation(); diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/DelayQueue.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/DelayQueue.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/DelayQueue.java Wed Jul 05 20:54:48 2017 +0200 @@ -34,10 +34,16 @@ */ package java.util.concurrent; + import static java.util.concurrent.TimeUnit.NANOSECONDS; + +import java.util.AbstractQueue; +import java.util.Collection; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.PriorityQueue; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; -import java.util.*; /** * An unbounded {@linkplain BlockingQueue blocking queue} of @@ -65,7 +71,7 @@ * * @since 1.5 * @author Doug Lea - * @param the type of elements held in this collection + * @param the type of elements held in this queue */ public class DelayQueue extends AbstractQueue implements BlockingQueue { @@ -89,7 +95,7 @@ * signalled. So waiting threads must be prepared to acquire * and lose leadership while waiting. */ - private Thread leader = null; + private Thread leader; /** * Condition signalled when a newer element becomes available @@ -185,10 +191,9 @@ lock.lock(); try { E first = q.peek(); - if (first == null || first.getDelay(NANOSECONDS) > 0) - return null; - else - return q.poll(); + return (first == null || first.getDelay(NANOSECONDS) > 0) + ? null + : q.poll(); } finally { lock.unlock(); } @@ -211,7 +216,7 @@ available.await(); else { long delay = first.getDelay(NANOSECONDS); - if (delay <= 0) + if (delay <= 0L) return q.poll(); first = null; // don't retain ref while waiting if (leader != null) @@ -253,15 +258,15 @@ for (;;) { E first = q.peek(); if (first == null) { - if (nanos <= 0) + if (nanos <= 0L) return null; else nanos = available.awaitNanos(nanos); } else { long delay = first.getDelay(NANOSECONDS); - if (delay <= 0) + if (delay <= 0L) return q.poll(); - if (nanos <= 0) + if (nanos <= 0L) return null; first = null; // don't retain ref while waiting if (nanos < delay || leader != null) @@ -490,7 +495,7 @@ } /** - * Identity-based version for use in Itr.remove + * Identity-based version for use in Itr.remove. */ void removeEQ(Object o) { final ReentrantLock lock = this.lock; diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/Exchanger.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/Exchanger.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/Exchanger.java Wed Jul 05 20:54:48 2017 +0200 @@ -35,9 +35,6 @@ */ package java.util.concurrent; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; -import java.util.concurrent.locks.LockSupport; /** * A synchronization point at which threads can pair and swap elements @@ -53,9 +50,9 @@ * to swap buffers between threads so that the thread filling the * buffer gets a freshly emptied one when it needs it, handing off the * filled one to the thread emptying the buffer. - *
     {@code
    + * 
     {@code
      * class FillAndEmpty {
    - *   Exchanger exchanger = new Exchanger();
    + *   Exchanger exchanger = new Exchanger<>();
      *   DataBuffer initialEmptyBuffer = ... a made-up type
      *   DataBuffer initialFullBuffer = ...
      *
    @@ -326,7 +323,7 @@
         }
     
         /**
    -     * Per-thread state
    +     * Per-thread state.
          */
         private final Participant participant;
     
    @@ -628,37 +625,33 @@
         }
     
         // Unsafe mechanics
    -    private static final sun.misc.Unsafe U;
    +    private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
         private static final long BOUND;
         private static final long SLOT;
         private static final long MATCH;
         private static final long BLOCKER;
         private static final int ABASE;
         static {
    -        int s;
             try {
    -            U = sun.misc.Unsafe.getUnsafe();
    -            Class ek = Exchanger.class;
    -            Class nk = Node.class;
    -            Class ak = Node[].class;
    -            Class tk = Thread.class;
                 BOUND = U.objectFieldOffset
    -                (ek.getDeclaredField("bound"));
    +                (Exchanger.class.getDeclaredField("bound"));
                 SLOT = U.objectFieldOffset
    -                (ek.getDeclaredField("slot"));
    +                (Exchanger.class.getDeclaredField("slot"));
    +
                 MATCH = U.objectFieldOffset
    -                (nk.getDeclaredField("match"));
    +                (Node.class.getDeclaredField("match"));
    +
                 BLOCKER = U.objectFieldOffset
    -                (tk.getDeclaredField("parkBlocker"));
    -            s = U.arrayIndexScale(ak);
    +                (Thread.class.getDeclaredField("parkBlocker"));
    +
    +            int scale = U.arrayIndexScale(Node[].class);
    +            if ((scale & (scale - 1)) != 0 || scale > (1 << ASHIFT))
    +                throw new Error("Unsupported array scale");
                 // ABASE absorbs padding in front of element 0
    -            ABASE = U.arrayBaseOffset(ak) + (1 << ASHIFT);
    -
    -        } catch (Exception e) {
    +            ABASE = U.arrayBaseOffset(Node[].class) + (1 << ASHIFT);
    +        } catch (ReflectiveOperationException e) {
                 throw new Error(e);
             }
    -        if ((s & (s-1)) != 0 || s > (1 << ASHIFT))
    -            throw new Error("Unsupported array scale");
         }
     
     }
    diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/Executor.java
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/Executor.java	Wed Oct 21 15:15:26 2015 -0700
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/Executor.java	Wed Jul 05 20:54:48 2017 +0200
    @@ -41,33 +41,31 @@
      * mechanics of how each task will be run, including details of thread
      * use, scheduling, etc.  An {@code Executor} is normally used
      * instead of explicitly creating threads. For example, rather than
    - * invoking {@code new Thread(new(RunnableTask())).start()} for each
    + * invoking {@code new Thread(new RunnableTask()).start()} for each
      * of a set of tasks, you might use:
      *
    - * 
    - * Executor executor = anExecutor;
    + * 
     {@code
    + * Executor executor = anExecutor();
      * executor.execute(new RunnableTask1());
      * executor.execute(new RunnableTask2());
    - * ...
    - * 
    + * ...}
    * - * However, the {@code Executor} interface does not strictly - * require that execution be asynchronous. In the simplest case, an - * executor can run the submitted task immediately in the caller's - * thread: + * However, the {@code Executor} interface does not strictly require + * that execution be asynchronous. In the simplest case, an executor + * can run the submitted task immediately in the caller's thread: * - *
     {@code
    + * 
     {@code
      * class DirectExecutor implements Executor {
      *   public void execute(Runnable r) {
      *     r.run();
      *   }
      * }}
    * - * More typically, tasks are executed in some thread other - * than the caller's thread. The executor below spawns a new thread - * for each task. + * More typically, tasks are executed in some thread other than the + * caller's thread. The executor below spawns a new thread for each + * task. * - *
     {@code
    + * 
     {@code
      * class ThreadPerTaskExecutor implements Executor {
      *   public void execute(Runnable r) {
      *     new Thread(r).start();
    @@ -79,9 +77,9 @@
      * serializes the submission of tasks to a second executor,
      * illustrating a composite executor.
      *
    - *  
     {@code
    + * 
     {@code
      * class SerialExecutor implements Executor {
    - *   final Queue tasks = new ArrayDeque();
    + *   final Queue tasks = new ArrayDeque<>();
      *   final Executor executor;
      *   Runnable active;
      *
    @@ -90,7 +88,7 @@
      *   }
      *
      *   public synchronized void execute(final Runnable r) {
    - *     tasks.offer(new Runnable() {
    + *     tasks.add(new Runnable() {
      *       public void run() {
      *         try {
      *           r.run();
    diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/ExecutorCompletionService.java
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/ExecutorCompletionService.java	Wed Oct 21 15:15:26 2015 -0700
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ExecutorCompletionService.java	Wed Jul 05 20:54:48 2017 +0200
    @@ -56,16 +56,16 @@
      * void solve(Executor e,
      *            Collection> solvers)
      *     throws InterruptedException, ExecutionException {
    - *     CompletionService ecs
    - *         = new ExecutorCompletionService(e);
    - *     for (Callable s : solvers)
    - *         ecs.submit(s);
    - *     int n = solvers.size();
    - *     for (int i = 0; i < n; ++i) {
    - *         Result r = ecs.take().get();
    - *         if (r != null)
    - *             use(r);
    - *     }
    + *   CompletionService ecs
    + *       = new ExecutorCompletionService(e);
    + *   for (Callable s : solvers)
    + *     ecs.submit(s);
    + *   int n = solvers.size();
    + *   for (int i = 0; i < n; ++i) {
    + *     Result r = ecs.take().get();
    + *     if (r != null)
    + *       use(r);
    + *   }
      * }}
    * * Suppose instead that you would like to use the first non-null result @@ -76,32 +76,31 @@ * void solve(Executor e, * Collection> solvers) * throws InterruptedException { - * CompletionService ecs - * = new ExecutorCompletionService(e); - * int n = solvers.size(); - * List> futures - * = new ArrayList>(n); - * Result result = null; - * try { - * for (Callable s : solvers) - * futures.add(ecs.submit(s)); - * for (int i = 0; i < n; ++i) { - * try { - * Result r = ecs.take().get(); - * if (r != null) { - * result = r; - * break; - * } - * } catch (ExecutionException ignore) {} + * CompletionService ecs + * = new ExecutorCompletionService(e); + * int n = solvers.size(); + * List> futures = new ArrayList<>(n); + * Result result = null; + * try { + * for (Callable s : solvers) + * futures.add(ecs.submit(s)); + * for (int i = 0; i < n; ++i) { + * try { + * Result r = ecs.take().get(); + * if (r != null) { + * result = r; + * break; * } + * } catch (ExecutionException ignore) {} * } - * finally { - * for (Future f : futures) - * f.cancel(true); - * } + * } + * finally { + * for (Future f : futures) + * f.cancel(true); + * } * - * if (result != null) - * use(result); + * if (result != null) + * use(result); * }}
    */ public class ExecutorCompletionService implements CompletionService { @@ -110,15 +109,18 @@ private final BlockingQueue> completionQueue; /** - * FutureTask extension to enqueue upon completion + * FutureTask extension to enqueue upon completion. */ - private class QueueingFuture extends FutureTask { - QueueingFuture(RunnableFuture task) { + private static class QueueingFuture extends FutureTask { + QueueingFuture(RunnableFuture task, + BlockingQueue> completionQueue) { super(task, null); this.task = task; + this.completionQueue = completionQueue; } + private final Future task; + private final BlockingQueue> completionQueue; protected void done() { completionQueue.add(task); } - private final Future task; } private RunnableFuture newTaskFor(Callable task) { @@ -178,14 +180,14 @@ public Future submit(Callable task) { if (task == null) throw new NullPointerException(); RunnableFuture f = newTaskFor(task); - executor.execute(new QueueingFuture(f)); + executor.execute(new QueueingFuture(f, completionQueue)); return f; } public Future submit(Runnable task, V result) { if (task == null) throw new NullPointerException(); RunnableFuture f = newTaskFor(task, result); - executor.execute(new QueueingFuture(f)); + executor.execute(new QueueingFuture(f, completionQueue)); return f; } diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/ExecutorService.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/ExecutorService.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ExecutorService.java Wed Jul 05 20:54:48 2017 +0200 @@ -34,8 +34,9 @@ */ package java.util.concurrent; + +import java.util.Collection; import java.util.List; -import java.util.Collection; /** * An {@link Executor} that provides methods to manage termination and @@ -71,7 +72,7 @@ * pool service incoming requests. It uses the preconfigured {@link * Executors#newFixedThreadPool} factory method: * - *
     {@code
    + * 
     {@code
      * class NetworkService implements Runnable {
      *   private final ServerSocket serverSocket;
      *   private final ExecutorService pool;
    @@ -105,7 +106,7 @@
      * first by calling {@code shutdown} to reject incoming tasks, and then
      * calling {@code shutdownNow}, if necessary, to cancel any lingering tasks:
      *
    - *  
     {@code
    + * 
     {@code
      * void shutdownAndAwaitTermination(ExecutorService pool) {
      *   pool.shutdown(); // Disable new tasks from being submitted
      *   try {
    diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/Executors.java
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/Executors.java	Wed Oct 21 15:15:26 2015 -0700
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/Executors.java	Wed Jul 05 20:54:48 2017 +0200
    @@ -34,14 +34,16 @@
      */
     
     package java.util.concurrent;
    -import java.util.*;
    -import java.util.concurrent.atomic.AtomicInteger;
    +
     import java.security.AccessControlContext;
    +import java.security.AccessControlException;
     import java.security.AccessController;
     import java.security.PrivilegedAction;
    +import java.security.PrivilegedActionException;
     import java.security.PrivilegedExceptionAction;
    -import java.security.PrivilegedActionException;
    -import java.security.AccessControlException;
    +import java.util.Collection;
    +import java.util.List;
    +import java.util.concurrent.atomic.AtomicInteger;
     import sun.security.util.SecurityConstants;
     
     /**
    @@ -51,18 +53,18 @@
      * package. This class supports the following kinds of methods:
      *
      * 
      - *
    • Methods that create and return an {@link ExecutorService} - * set up with commonly useful configuration settings. - *
    • Methods that create and return a {@link ScheduledExecutorService} - * set up with commonly useful configuration settings. - *
    • Methods that create and return a "wrapped" ExecutorService, that - * disables reconfiguration by making implementation-specific methods - * inaccessible. - *
    • Methods that create and return a {@link ThreadFactory} - * that sets newly created threads to a known state. - *
    • Methods that create and return a {@link Callable} - * out of other closure-like forms, so they can be used - * in execution methods requiring {@code Callable}. + *
    • Methods that create and return an {@link ExecutorService} + * set up with commonly useful configuration settings. + *
    • Methods that create and return a {@link ScheduledExecutorService} + * set up with commonly useful configuration settings. + *
    • Methods that create and return a "wrapped" ExecutorService, that + * disables reconfiguration by making implementation-specific methods + * inaccessible. + *
    • Methods that create and return a {@link ThreadFactory} + * that sets newly created threads to a known state. + *
    • Methods that create and return a {@link Callable} + * out of other closure-like forms, so they can be used + * in execution methods requiring {@code Callable}. *
    * * @since 1.5 @@ -114,9 +116,10 @@ } /** - * Creates a work-stealing thread pool using all - * {@link Runtime#availableProcessors available processors} + * Creates a work-stealing thread pool using the number of + * {@linkplain Runtime#availableProcessors available processors} * as its target parallelism level. + * * @return the newly created thread pool * @see #newWorkStealingPool(int) * @since 1.8 @@ -498,11 +501,11 @@ // Non-public classes supporting the public methods /** - * A callable that runs given task and returns given result + * A callable that runs given task and returns given result. */ - static final class RunnableAdapter implements Callable { - final Runnable task; - final T result; + private static final class RunnableAdapter implements Callable { + private final Runnable task; + private final T result; RunnableAdapter(Runnable task, T result) { this.task = task; this.result = result; @@ -514,11 +517,11 @@ } /** - * A callable that runs under established access control settings + * A callable that runs under established access control settings. */ - static final class PrivilegedCallable implements Callable { - private final Callable task; - private final AccessControlContext acc; + private static final class PrivilegedCallable implements Callable { + final Callable task; + final AccessControlContext acc; PrivilegedCallable(Callable task) { this.task = task; @@ -541,12 +544,13 @@ /** * A callable that runs under established access control settings and - * current ClassLoader + * current ClassLoader. */ - static final class PrivilegedCallableUsingCurrentClassLoader implements Callable { - private final Callable task; - private final AccessControlContext acc; - private final ClassLoader ccl; + private static final class PrivilegedCallableUsingCurrentClassLoader + implements Callable { + final Callable task; + final AccessControlContext acc; + final ClassLoader ccl; PrivilegedCallableUsingCurrentClassLoader(Callable task) { SecurityManager sm = System.getSecurityManager(); @@ -591,9 +595,9 @@ } /** - * The default thread factory + * The default thread factory. */ - static class DefaultThreadFactory implements ThreadFactory { + private static class DefaultThreadFactory implements ThreadFactory { private static final AtomicInteger poolNumber = new AtomicInteger(1); private final ThreadGroup group; private final AtomicInteger threadNumber = new AtomicInteger(1); @@ -621,11 +625,11 @@ } /** - * Thread factory capturing access control context and class loader + * Thread factory capturing access control context and class loader. */ - static class PrivilegedThreadFactory extends DefaultThreadFactory { - private final AccessControlContext acc; - private final ClassLoader ccl; + private static class PrivilegedThreadFactory extends DefaultThreadFactory { + final AccessControlContext acc; + final ClassLoader ccl; PrivilegedThreadFactory() { super(); @@ -662,7 +666,8 @@ * A wrapper class that exposes only the ExecutorService methods * of an ExecutorService implementation. */ - static class DelegatedExecutorService extends AbstractExecutorService { + private static class DelegatedExecutorService + extends AbstractExecutorService { private final ExecutorService e; DelegatedExecutorService(ExecutorService executor) { e = executor; } public void execute(Runnable command) { e.execute(command); } @@ -703,8 +708,8 @@ } } - static class FinalizableDelegatedExecutorService - extends DelegatedExecutorService { + private static class FinalizableDelegatedExecutorService + extends DelegatedExecutorService { FinalizableDelegatedExecutorService(ExecutorService executor) { super(executor); } @@ -717,7 +722,7 @@ * A wrapper class that exposes only the ScheduledExecutorService * methods of a ScheduledExecutorService implementation. */ - static class DelegatedScheduledExecutorService + private static class DelegatedScheduledExecutorService extends DelegatedExecutorService implements ScheduledExecutorService { private final ScheduledExecutorService e; diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/Flow.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/Flow.java Wed Jul 05 20:54:48 2017 +0200 @@ -0,0 +1,319 @@ +/* + * 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +package java.util.concurrent; + +/** + * Interrelated interfaces and static methods for establishing + * flow-controlled components in which {@link Publisher Publishers} + * produce items consumed by one or more {@link Subscriber + * Subscribers}, each managed by a {@link Subscription + * Subscription}. + * + *

    These interfaces correspond to the reactive-streams + * specification. They apply in both concurrent and distributed + * asynchronous settings: All (seven) methods are defined in {@code + * void} "one-way" message style. Communication relies on a simple form + * of flow control (method {@link Subscription#request}) that can be + * used to avoid resource management problems that may otherwise occur + * in "push" based systems. + * + *

    Examples. A {@link Publisher} usually defines its own + * {@link Subscription} implementation; constructing one in method + * {@code subscribe} and issuing it to the calling {@link + * Subscriber}. It publishes items to the subscriber asynchronously, + * normally using an {@link Executor}. For example, here is a very + * simple publisher that only issues (when requested) a single {@code + * TRUE} item to a single subscriber. Because the subscriber receives + * only a single item, this class does not use buffering and ordering + * control required in most implementations (for example {@link + * SubmissionPublisher}). + * + *

     {@code
    + * class OneShotPublisher implements Publisher {
    + *   private final ExecutorService executor = ForkJoinPool.commonPool(); // daemon-based
    + *   private boolean subscribed; // true after first subscribe
    + *   public synchronized void subscribe(Subscriber subscriber) {
    + *     if (subscribed)
    + *        subscriber.onError(new IllegalStateException()); // only one allowed
    + *     else {
    + *       subscribed = true;
    + *       subscriber.onSubscribe(new OneShotSubscription(subscriber, executor));
    + *     }
    + *   }
    + *   static class OneShotSubscription implements Subscription {
    + *     private final Subscriber subscriber;
    + *     private final ExecutorService executor;
    + *     private Future future; // to allow cancellation
    + *     private boolean completed;
    + *     OneShotSubscription(Subscriber subscriber,
    + *                         ExecutorService executor) {
    + *       this.subscriber = subscriber;
    + *       this.executor = executor;
    + *     }
    + *     public synchronized void request(long n) {
    + *       if (n != 0 && !completed) {
    + *         completed = true;
    + *         if (n < 0) {
    + *           IllegalArgumentException ex = new IllegalArgumentException();
    + *           executor.execute(() -> subscriber.onError(ex));
    + *         } else {
    + *           future = executor.submit(() -> {
    + *             subscriber.onNext(Boolean.TRUE);
    + *             subscriber.onComplete();
    + *           });
    + *         }
    + *       }
    + *     }
    + *     public synchronized void cancel() {
    + *       completed = true;
    + *       if (future != null) future.cancel(false);
    + *     }
    + *   }
    + * }}
    + * + *

    A {@link Subscriber} arranges that items be requested and + * processed. Items (invocations of {@link Subscriber#onNext}) are + * not issued unless requested, but multiple items may be requested. + * Many Subscriber implementations can arrange this in the style of + * the following example, where a buffer size of 1 single-steps, and + * larger sizes usually allow for more efficient overlapped processing + * with less communication; for example with a value of 64, this keeps + * total outstanding requests between 32 and 64. + * Because Subscriber method invocations for a given {@link + * Subscription} are strictly ordered, there is no need for these + * methods to use locks or volatiles unless a Subscriber maintains + * multiple Subscriptions (in which case it is better to instead + * define multiple Subscribers, each with its own Subscription). + * + *

     {@code
    + * class SampleSubscriber implements Subscriber {
    + *   final Consumer consumer;
    + *   Subscription subscription;
    + *   final long bufferSize;
    + *   long count;
    + *   SampleSubscriber(long bufferSize, Consumer consumer) {
    + *     this.bufferSize = bufferSize;
    + *     this.consumer = consumer;
    + *   }
    + *   public void onSubscribe(Subscription subscription) {
    + *     long initialRequestSize = bufferSize;
    + *     count = bufferSize - bufferSize / 2; // re-request when half consumed
    + *     (this.subscription = subscription).request(initialRequestSize);
    + *   }
    + *   public void onNext(T item) {
    + *     if (--count <= 0)
    + *       subscription.request(count = bufferSize - bufferSize / 2);
    + *     consumer.accept(item);
    + *   }
    + *   public void onError(Throwable ex) { ex.printStackTrace(); }
    + *   public void onComplete() {}
    + * }}
    + * + *

    The default value of {@link #defaultBufferSize} may provide a + * useful starting point for choosing request sizes and capacities in + * Flow components based on expected rates, resources, and usages. + * Or, when flow control is never needed, a subscriber may initially + * request an effectively unbounded number of items, as in: + * + *

     {@code
    + * class UnboundedSubscriber implements Subscriber {
    + *   public void onSubscribe(Subscription subscription) {
    + *     subscription.request(Long.MAX_VALUE); // effectively unbounded
    + *   }
    + *   public void onNext(T item) { use(item); }
    + *   public void onError(Throwable ex) { ex.printStackTrace(); }
    + *   public void onComplete() {}
    + *   void use(T item) { ... }
    + * }}
    + * + * @author Doug Lea + * @since 1.9 + */ +public final class Flow { + + private Flow() {} // uninstantiable + + /** + * A producer of items (and related control messages) received by + * Subscribers. Each current {@link Subscriber} receives the same + * items (via method {@code onNext}) in the same order, unless + * drops or errors are encountered. If a Publisher encounters an + * error that does not allow items to be issued to a Subscriber, + * that Subscriber receives {@code onError}, and then receives no + * further messages. Otherwise, when it is known that no further + * messages will be issued to it, a subscriber receives {@code + * onComplete}. Publishers ensure that Subscriber method + * invocations for each subscription are strictly ordered in happens-before + * order. + * + *

    Publishers may vary in policy about whether drops (failures + * to issue an item because of resource limitations) are treated + * as unrecoverable errors. Publishers may also vary about + * whether Subscribers receive items that were produced or + * available before they subscribed. + * + * @param the published item type + */ + @FunctionalInterface + public static interface Publisher { + /** + * Adds the given Subscriber if possible. If already + * subscribed, or the attempt to subscribe fails due to policy + * violations or errors, the Subscriber's {@code onError} + * method is invoked with an {@link IllegalStateException}. + * Otherwise, the Subscriber's {@code onSubscribe} method is + * invoked with a new {@link Subscription}. Subscribers may + * enable receiving items by invoking the {@code request} + * method of this Subscription, and may unsubscribe by + * invoking its {@code cancel} method. + * + * @param subscriber the subscriber + * @throws NullPointerException if subscriber is null + */ + public void subscribe(Subscriber subscriber); + } + + /** + * A receiver of messages. The methods in this interface are + * invoked in strict sequential order for each {@link + * Subscription}. + * + * @param the subscribed item type + */ + public static interface Subscriber { + /** + * Method invoked prior to invoking any other Subscriber + * methods for the given Subscription. If this method throws + * an exception, resulting behavior is not guaranteed, but may + * cause the Subscription not to be established or to be cancelled. + * + *

    Typically, implementations of this method invoke {@code + * subscription.request} to enable receiving items. + * + * @param subscription a new subscription + */ + public void onSubscribe(Subscription subscription); + + /** + * Method invoked with a Subscription's next item. If this + * method throws an exception, resulting behavior is not + * guaranteed, but may cause the Subscription to be cancelled. + * + * @param item the item + */ + public void onNext(T item); + + /** + * Method invoked upon an unrecoverable error encountered by a + * Publisher or Subscription, after which no other Subscriber + * methods are invoked by the Subscription. If this method + * itself throws an exception, resulting behavior is + * undefined. + * + * @param throwable the exception + */ + public void onError(Throwable throwable); + + /** + * Method invoked when it is known that no additional + * Subscriber method invocations will occur for a Subscription + * that is not already terminated by error, after which no + * other Subscriber methods are invoked by the Subscription. + * If this method throws an exception, resulting behavior is + * undefined. + */ + public void onComplete(); + } + + /** + * Message control linking a {@link Publisher} and {@link + * Subscriber}. Subscribers receive items only when requested, + * and may cancel at any time. The methods in this interface are + * intended to be invoked only by their Subscribers; usages in + * other contexts have undefined effects. + */ + public static interface Subscription { + /** + * Adds the given number {@code n} of items to the current + * unfulfilled demand for this subscription. If {@code n} is + * negative, the Subscriber will receive an {@code onError} + * signal with an {@link IllegalArgumentException} argument. + * Otherwise, the Subscriber will receive up to {@code n} + * additional {@code onNext} invocations (or fewer if + * terminated). + * + * @param n the increment of demand; a value of {@code + * Long.MAX_VALUE} may be considered as effectively unbounded + */ + public void request(long n); + + /** + * Causes the Subscriber to (eventually) stop receiving + * messages. Implementation is best-effort -- additional + * messages may be received after invoking this method. + * A cancelled subscription need not ever receive an + * {@code onComplete} or {@code onError} signal. + */ + public void cancel(); + } + + /** + * A component that acts as both a Subscriber and Publisher. + * + * @param the subscribed item type + * @param the published item type + */ + public static interface Processor extends Subscriber, Publisher { + } + + static final int DEFAULT_BUFFER_SIZE = 256; + + /** + * Returns a default value for Publisher or Subscriber buffering, + * that may be used in the absence of other constraints. + * + * @implNote + * The current value returned is 256. + * + * @return the buffer size value + */ + public static int defaultBufferSize() { + return DEFAULT_BUFFER_SIZE; + } + +} diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java Wed Jul 05 20:54:48 2017 +0200 @@ -36,23 +36,16 @@ package java.util.concurrent; import java.lang.Thread.UncaughtExceptionHandler; +import java.security.AccessControlContext; +import java.security.Permissions; +import java.security.ProtectionDomain; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.concurrent.AbstractExecutorService; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Future; -import java.util.concurrent.RejectedExecutionException; -import java.util.concurrent.RunnableFuture; -import java.util.concurrent.ThreadLocalRandom; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; -import java.security.AccessControlContext; -import java.security.ProtectionDomain; -import java.security.Permissions; +import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.locks.LockSupport; /** * An {@link ExecutorService} for running {@link ForkJoinTask}s. @@ -216,42 +209,60 @@ * arbitrating pop vs poll (steal) from being on the indices * ("base" and "top") to the slots themselves. * - * Adding tasks then takes the form of a classic array push(task): - * q.array[q.top] = task; ++q.top; + * Adding tasks then takes the form of a classic array push(task) + * in a circular buffer: + * q.array[q.top++ % length] = task; * * (The actual code needs to null-check and size-check the array, - * properly fence the accesses, and possibly signal waiting - * workers to start scanning -- see below.) Both a successful pop - * and poll mainly entail a CAS of a slot from non-null to null. + * uses masking, not mod, for indexing a power-of-two-sized array, + * properly fences accesses, and possibly signals waiting workers + * to start scanning -- see below.) Both a successful pop and + * poll mainly entail a CAS of a slot from non-null to null. * * The pop operation (always performed by owner) is: - * if ((base != top) and - * (the task at top slot is not null) and + * if ((the task at top slot is not null) and * (CAS slot to null)) * decrement top and return task; * * And the poll operation (usually by a stealer) is - * if ((base != top) and - * (the task at base slot is not null) and - * (base has not changed) and + * if ((the task at base slot is not null) and * (CAS slot to null)) * increment base and return task; * - * Because we rely on CASes of references, we do not need tag bits - * on base or top. They are simple ints as used in any circular - * array-based queue (see for example ArrayDeque). Updates to the - * indices guarantee that top == base means the queue is empty, - * but otherwise may err on the side of possibly making the queue - * appear nonempty when a push, pop, or poll have not fully - * committed. (Method isEmpty() checks the case of a partially + * There are several variants of each of these; for example most + * versions of poll pre-screen the CAS by rechecking that the base + * has not changed since reading the slot, and most methods only + * attempt the CAS if base appears not to be equal to top. + * + * Memory ordering. See "Correct and Efficient Work-Stealing for + * Weak Memory Models" by Le, Pop, Cohen, and Nardelli, PPoPP 2013 + * (http://www.di.ens.fr/~zappa/readings/ppopp13.pdf) for an + * analysis of memory ordering requirements in work-stealing + * algorithms similar to (but different than) the one used here. + * Extracting tasks in array slots via (fully fenced) CAS provides + * primary synchronization. The base and top indices imprecisely + * guide where to extract from. We do not always require strict + * orderings of array and index updates, so sometimes let them be + * subject to compiler and processor reorderings. However, the + * volatile "base" index also serves as a basis for memory + * ordering: Slot accesses are preceded by a read of base, + * ensuring happens-before ordering with respect to stealers (so + * the slots themselves can be read via plain array reads.) The + * only other memory orderings relied on are maintained in the + * course of signalling and activation (see below). A check that + * base == top indicates (momentary) emptiness, but otherwise may + * err on the side of possibly making the queue appear nonempty + * when a push, pop, or poll have not fully committed, or making + * it appear empty when an update of top has not yet been visibly + * written. (Method isEmpty() checks the case of a partially * completed removal of the last element.) Because of this, the * poll operation, considered individually, is not wait-free. One * thief cannot successfully continue until another in-progress - * one (or, if previously empty, a push) completes. However, in - * the aggregate, we ensure at least probabilistic - * non-blockingness. If an attempted steal fails, a thief always - * chooses a different random victim target to try next. So, in - * order for one thief to progress, it suffices for any + * one (or, if previously empty, a push) visibly completes. + * However, in the aggregate, we ensure at least probabilistic + * non-blockingness. If an attempted steal fails, a scanning + * thief chooses a different random victim target to try next. So, + * in order for one thief to progress, it suffices for any * in-progress poll or new push on any empty queue to * complete. (This is why we normally use method pollAt and its * variants that try once at the apparent base index, else @@ -262,19 +273,6 @@ * local task processing is in FIFO, not LIFO order, simply by * using poll rather than pop. This can be useful in * message-passing frameworks in which tasks are never joined. - * However neither mode considers affinities, loads, cache - * localities, etc, so rarely provide the best possible - * performance on a given machine, but portably provide good - * throughput by averaging over these factors. Further, even if - * we did try to use such information, we do not usually have a - * basis for exploiting it. For example, some sets of tasks - * profit from cache affinities, but others are harmed by cache - * pollution effects. Additionally, even though it requires - * scanning, long-term throughput is often best using random - * selection rather than directed selection policies, so cheap - * randomization of sufficient quality is used whenever - * applicable. Various Marsaglia XorShifts (some with different - * shift constants) are inlined at use points. * * WorkQueues are also used in a similar way for tasks submitted * to the pool. We cannot mix these tasks in the same queues used @@ -286,14 +284,14 @@ * like workers except that they are restricted to executing local * tasks that they submitted (or in the case of CountedCompleters, * others with the same root task). Insertion of tasks in shared - * mode requires a lock (mainly to protect in the case of - * resizing) but we use only a simple spinlock (using field - * qlock), because submitters encountering a busy queue move on to - * try or create other queues -- they block only when creating and - * registering new queues. Additionally, "qlock" saturates to an - * unlockable value (-1) at shutdown. Unlocking still can be and - * is performed by cheaper ordered writes of "qlock" in successful - * cases, but uses CAS in unsuccessful cases. + * mode requires a lock but we use only a simple spinlock (using + * field qlock), because submitters encountering a busy queue move + * on to try or create other queues -- they block only when + * creating and registering new queues. Because it is used only as + * a spinlock, unlocking requires only a "releasing" store (using + * putOrderedInt). The qlock is also used during termination + * detection, in which case it is forced to a negative + * non-lockable value. * * Management * ========== @@ -320,46 +318,36 @@ * and their negations (used for thresholding) to fit into 16bit * subfields. * - * Field "runState" holds lockable state bits (STARTED, STOP, etc) - * also protecting updates to the workQueues array. When used as - * a lock, it is normally held only for a few instructions (the - * only exceptions are one-time array initialization and uncommon - * resizing), so is nearly always available after at most a brief - * spin. But to be extra-cautious, after spinning, method - * awaitRunStateLock (called only if an initial CAS fails), uses a - * wait/notify mechanics on a builtin monitor to block when - * (rarely) needed. This would be a terrible idea for a highly - * contended lock, but most pools run without the lock ever - * contending after the spin limit, so this works fine as a more - * conservative alternative. Because we don't otherwise have an - * internal Object to use as a monitor, the "stealCounter" (an - * AtomicLong) is used when available (it too must be lazily - * initialized; see externalSubmit). + * Field "runState" holds lifetime status, atomically and + * monotonically setting STARTED, SHUTDOWN, STOP, and finally + * TERMINATED bits. + * + * Field "auxState" is a ReentrantLock subclass that also + * opportunistically holds some other bookkeeping fields accessed + * only when locked. It is mainly used to lock (infrequent) + * updates to workQueues. The auxState instance is itself lazily + * constructed (see tryInitialize), requiring a double-check-style + * bootstrapping use of field runState, and locking a private + * static. * - * Usages of "runState" vs "ctl" interact in only one case: - * deciding to add a worker thread (see tryAddWorker), in which - * case the ctl CAS is performed while the lock is held. - * - * Recording WorkQueues. WorkQueues are recorded in the - * "workQueues" array. The array is created upon first use (see - * externalSubmit) and expanded if necessary. Updates to the - * array while recording new workers and unrecording terminated - * ones are protected from each other by the runState lock, but - * the array is otherwise concurrently readable, and accessed + * Field "workQueues" holds references to WorkQueues. It is + * updated (only during worker creation and termination) under the + * lock, but is otherwise concurrently readable, and accessed * directly. We also ensure that reads of the array reference - * itself never become too stale. To simplify index-based - * operations, the array size is always a power of two, and all - * readers must tolerate null slots. Worker queues are at odd - * indices. Shared (submission) queues are at even indices, up to - * a maximum of 64 slots, to limit growth even if array needs to - * expand to add more workers. Grouping them together in this way - * simplifies and speeds up task scanning. + * itself never become too stale (for example, re-reading before + * each scan). To simplify index-based operations, the array size + * is always a power of two, and all readers must tolerate null + * slots. Worker queues are at odd indices. Shared (submission) + * queues are at even indices, up to a maximum of 64 slots, to + * limit growth even if array needs to expand to add more + * workers. Grouping them together in this way simplifies and + * speeds up task scanning. * * All worker thread creation is on-demand, triggered by task * submissions, replacement of terminated workers, and/or * compensation for blocked workers. However, all other support * code is set up to work with other policies. To ensure that we - * do not hold on to worker references that would prevent GC, All + * do not hold on to worker references that would prevent GC, all * accesses to workQueues are via indices into the workQueues * array (which is one source of some of the messy code * constructions here). In essence, the workQueues array serves as @@ -386,7 +374,7 @@ * activating threads in most-recently used order. This improves * performance and locality, outweighing the disadvantages of * being prone to contention and inability to release a worker - * unless it is topmost on stack. We park/unpark workers after + * unless it is topmost on stack. We block/unblock workers after * pushing on the idle worker stack (represented by the lower * 32bit subfield of ctl) when they cannot find work. The top * stack state holds the value of the "scanState" field of the @@ -394,48 +382,14 @@ * addition to the count subfields (also serving as version * stamps) provide protection against Treiber stack ABA effects. * - * Field scanState is used by both workers and the pool to manage - * and track whether a worker is INACTIVE (possibly blocked - * waiting for a signal), or SCANNING for tasks (when neither hold - * it is busy running tasks). When a worker is inactivated, its - * scanState field is set, and is prevented from executing tasks, - * even though it must scan once for them to avoid queuing - * races. Note that scanState updates lag queue CAS releases so - * usage requires care. When queued, the lower 16 bits of - * scanState must hold its pool index. So we place the index there - * upon initialization (see registerWorker) and otherwise keep it - * there or restore it when necessary. - * - * Memory ordering. See "Correct and Efficient Work-Stealing for - * Weak Memory Models" by Le, Pop, Cohen, and Nardelli, PPoPP 2013 - * (http://www.di.ens.fr/~zappa/readings/ppopp13.pdf) for an - * analysis of memory ordering requirements in work-stealing - * algorithms similar to the one used here. We usually need - * stronger than minimal ordering because we must sometimes signal - * workers, requiring Dekker-like full-fences to avoid lost - * signals. Arranging for enough ordering without expensive - * over-fencing requires tradeoffs among the supported means of - * expressing access constraints. The most central operations, - * taking from queues and updating ctl state, require full-fence - * CAS. Array slots are read using the emulation of volatiles - * provided by Unsafe. Access from other threads to WorkQueue - * base, top, and array requires a volatile load of the first of - * any of these read. We use the convention of declaring the - * "base" index volatile, and always read it before other fields. - * The owner thread must ensure ordered updates, so writes use - * ordered intrinsics unless they can piggyback on those for other - * writes. Similar conventions and rationales hold for other - * WorkQueue fields (such as "currentSteal") that are only written - * by owners but observed by others. - * * Creating workers. To create a worker, we pre-increment total * count (serving as a reservation), and attempt to construct a * ForkJoinWorkerThread via its factory. Upon construction, the * new thread invokes registerWorker, where it constructs a * WorkQueue and is assigned an index in the workQueues array - * (expanding the array if necessary). The thread is then - * started. Upon any exception across these steps, or null return - * from factory, deregisterWorker adjusts counts and records + * (expanding the array if necessary). The thread is then started. + * Upon any exception across these steps, or null return from + * factory, deregisterWorker adjusts counts and records * accordingly. If a null return, the pool continues running with * fewer than the target number workers. If exceptional, the * exception is propagated, generally to some external caller. @@ -448,80 +402,106 @@ * probability of collision low. We cannot use * ThreadLocalRandom.getProbe() for similar purposes here because * the thread has not started yet, but do so for creating - * submission queues for existing external threads. + * submission queues for existing external threads (see + * externalPush). + * + * WorkQueue field scanState is used by both workers and the pool + * to manage and track whether a worker is UNSIGNALLED (possibly + * blocked waiting for a signal). When a worker is inactivated, + * its scanState field is set, and is prevented from executing + * tasks, even though it must scan once for them to avoid queuing + * races. Note that scanState updates lag queue CAS releases so + * usage requires care. When queued, the lower 16 bits of + * scanState must hold its pool index. So we place the index there + * upon initialization (see registerWorker) and otherwise keep it + * there or restore it when necessary. + * + * The ctl field also serves as the basis for memory + * synchronization surrounding activation. This uses a more + * efficient version of a Dekker-like rule that task producers and + * consumers sync with each other by both writing/CASing ctl (even + * if to its current value). This would be extremely costly. So + * we relax it in several ways: (1) Producers only signal when + * their queue is empty. Other workers propagate this signal (in + * method scan) when they find tasks. (2) Workers only enqueue + * after scanning (see below) and not finding any tasks. (3) + * Rather than CASing ctl to its current value in the common case + * where no action is required, we reduce write contention by + * equivalently prefacing signalWork when called by an external + * task producer using a memory access with full-volatile + * semantics or a "fullFence". (4) For internal task producers we + * rely on the fact that even if no other workers awaken, the + * producer itself will eventually see the task and execute it. + * + * Almost always, too many signals are issued. A task producer + * cannot in general tell if some existing worker is in the midst + * of finishing one task (or already scanning) and ready to take + * another without being signalled. So the producer might instead + * activate a different worker that does not find any work, and + * then inactivates. This scarcely matters in steady-state + * computations involving all workers, but can create contention + * and bookkeeping bottlenecks during ramp-up, ramp-down, and small + * computations involving only a few workers. + * + * Scanning. Method scan() performs top-level scanning for tasks. + * Each scan traverses (and tries to poll from) each queue in + * pseudorandom permutation order by randomly selecting an origin + * index and a step value. (The pseudorandom generator need not + * have high-quality statistical properties in the long term, but + * just within computations; We use 64bit and 32bit Marsaglia + * XorShifts, which are cheap and suffice here.) Scanning also + * employs contention reduction: When scanning workers fail a CAS + * polling for work, they soon restart with a different + * pseudorandom scan order (thus likely retrying at different + * intervals). This improves throughput when many threads are + * trying to take tasks from few queues. Scans do not otherwise + * explicitly take into account core affinities, loads, cache + * localities, etc, However, they do exploit temporal locality + * (which usually approximates these) by preferring to re-poll (up + * to POLL_LIMIT times) from the same queue after a successful + * poll before trying others. Restricted forms of scanning occur + * in methods helpComplete and findNonEmptyStealQueue, and take + * similar but simpler forms. * * Deactivation and waiting. Queuing encounters several intrinsic - * races; most notably that a task-producing thread can miss - * seeing (and signalling) another thread that gave up looking for - * work but has not yet entered the wait queue. When a worker - * cannot find a task to steal, it deactivates and enqueues. Very - * often, the lack of tasks is transient due to GC or OS - * scheduling. To reduce false-alarm deactivation, scanners - * compute checksums of queue states during sweeps. (The - * stability checks used here and elsewhere are probabilistic - * variants of snapshot techniques -- see Herlihy & Shavit.) - * Workers give up and try to deactivate only after the sum is - * stable across scans. Further, to avoid missed signals, they - * repeat this scanning process after successful enqueuing until - * again stable. In this state, the worker cannot take/run a task - * it sees until it is released from the queue, so the worker - * itself eventually tries to release itself or any successor (see - * tryRelease). Otherwise, upon an empty scan, a deactivated - * worker uses an adaptive local spin construction (see awaitWork) - * before blocking (via park). Note the unusual conventions about - * Thread.interrupts surrounding parking and other blocking: - * Because interrupts are used solely to alert threads to check - * termination, which is checked anyway upon blocking, we clear - * status (using Thread.interrupted) before any call to park, so - * that park does not immediately return due to status being set - * via some other unrelated call to interrupt in user code. + * races; most notably that an inactivating scanning worker can + * miss seeing a task produced during a scan. So when a worker + * cannot find a task to steal, it inactivates and enqueues, and + * then rescans to ensure that it didn't miss one, reactivating + * upon seeing one with probability approximately proportional to + * probability of a miss. (In most cases, the worker will be + * signalled before self-signalling, avoiding cascades of multiple + * signals for the same task). * - * Signalling and activation. Workers are created or activated - * only when there appears to be at least one task they might be - * able to find and execute. Upon push (either by a worker or an - * external submission) to a previously (possibly) empty queue, - * workers are signalled if idle, or created if fewer exist than - * the given parallelism level. These primary signals are - * buttressed by others whenever other threads remove a task from - * a queue and notice that there are other tasks there as well. - * On most platforms, signalling (unpark) overhead time is - * noticeably long, and the time between signalling a thread and - * it actually making progress can be very noticeably long, so it - * is worth offloading these delays from critical paths as much as - * possible. Also, because inactive workers are often rescanning - * or spinning rather than blocking, we set and clear the "parker" - * field of WorkQueues to reduce unnecessary calls to unpark. - * (This requires a secondary recheck to avoid missed signals.) + * Workers block (in method awaitWork) using park/unpark; + * advertising the need for signallers to unpark by setting their + * "parker" fields. * * Trimming workers. To release resources after periods of lack of * use, a worker starting to wait when the pool is quiescent will * time out and terminate (see awaitWork) if the pool has remained - * quiescent for period IDLE_TIMEOUT, increasing the period as the - * number of threads decreases, eventually removing all workers. - * Also, when more than two spare threads exist, excess threads - * are immediately terminated at the next quiescent point. - * (Padding by two avoids hysteresis.) + * quiescent for period given by IDLE_TIMEOUT_MS, increasing the + * period as the number of threads decreases, eventually removing + * all workers. * * Shutdown and Termination. A call to shutdownNow invokes * tryTerminate to atomically set a runState bit. The calling * thread, as well as every other worker thereafter terminating, * helps terminate others by setting their (qlock) status, * cancelling their unprocessed tasks, and waking them up, doing - * so repeatedly until stable (but with a loop bounded by the - * number of workers). Calls to non-abrupt shutdown() preface - * this by checking whether termination should commence. This - * relies primarily on the active count bits of "ctl" maintaining - * consensus -- tryTerminate is called from awaitWork whenever - * quiescent. However, external submitters do not take part in - * this consensus. So, tryTerminate sweeps through queues (until - * stable) to ensure lack of in-flight submissions and workers - * about to process them before triggering the "STOP" phase of - * termination. (Note: there is an intrinsic conflict if + * so repeatedly until stable. Calls to non-abrupt shutdown() + * preface this by checking whether termination should commence. + * This relies primarily on the active count bits of "ctl" + * maintaining consensus -- tryTerminate is called from awaitWork + * whenever quiescent. However, external submitters do not take + * part in this consensus. So, tryTerminate sweeps through queues + * (until stable) to ensure lack of in-flight submissions and + * workers about to process them before triggering the "STOP" + * phase of termination. (Note: there is an intrinsic conflict if * helpQuiescePool is called when shutdown is enabled. Both wait * for quiescence, but tryTerminate is biased to not trigger until * helpQuiescePool completes.) * - * * Joining Tasks * ============= * @@ -605,8 +585,13 @@ * continuation tasks) blocks on a join and there still remain * enough threads to ensure liveness. * + * Spare threads are removed as soon as they notice that the + * target parallelism level has been exceeded, in method + * tryDropSpare. (Method scan arranges returns for rechecks upon + * each probe via the "bound" parameter.) + * * The compensation mechanism may be bounded. Bounds for the - * commonPool (see commonMaxSpares) better enable JVMs to cope + * commonPool (see COMMON_MAX_SPARES) better enable JVMs to cope * with programming errors and abuse before running out of * resources to do so. In other cases, users may supply factories * that limit thread construction. The effects of bounding in this @@ -728,7 +713,7 @@ * Default ForkJoinWorkerThreadFactory implementation; creates a * new ForkJoinWorkerThread. */ - static final class DefaultForkJoinWorkerThreadFactory + private static final class DefaultForkJoinWorkerThreadFactory implements ForkJoinWorkerThreadFactory { public final ForkJoinWorkerThread newThread(ForkJoinPool pool) { return new ForkJoinWorkerThread(pool); @@ -741,7 +726,7 @@ * in WorkQueue.tryRemoveAndExec. We don't need the proxy to * actually do anything beyond having a unique identity. */ - static final class EmptyTask extends ForkJoinTask { + private static final class EmptyTask extends ForkJoinTask { private static final long serialVersionUID = -7721805057305804111L; EmptyTask() { status = ForkJoinTask.NORMAL; } // force done public final Void getRawResult() { return null; } @@ -749,6 +734,16 @@ public final boolean exec() { return true; } } + /** + * Additional fields and lock created upon initialization. + */ + private static final class AuxState extends ReentrantLock { + private static final long serialVersionUID = -6001602636862214147L; + volatile long stealCount; // cumulative steal count + long indexSeed; // index bits for registerWorker + AuxState() {} + } + // Constants shared across ForkJoinPool and WorkQueue // Bounds @@ -758,15 +753,23 @@ static final int SQMASK = 0x007e; // max 64 (even) slots // Masks and units for WorkQueue.scanState and ctl sp subfield - static final int SCANNING = 1; // false when running tasks - static final int INACTIVE = 1 << 31; // must be negative + static final int UNSIGNALLED = 1 << 31; // must be negative static final int SS_SEQ = 1 << 16; // version count // Mode bits for ForkJoinPool.config and WorkQueue.config static final int MODE_MASK = 0xffff << 16; // top half of int - static final int LIFO_QUEUE = 0; - static final int FIFO_QUEUE = 1 << 16; - static final int SHARED_QUEUE = 1 << 31; // must be negative + static final int SPARE_WORKER = 1 << 17; // set if tc > 0 on creation + static final int UNREGISTERED = 1 << 18; // to skip some of deregister + static final int FIFO_QUEUE = 1 << 31; // must be negative + static final int LIFO_QUEUE = 0; // for clarity + static final int IS_OWNED = 1; // low bit 0 if shared + + /** + * The maximum number of task executions from the same queue + * before checking other queues, bounding unfairness and impact of + * infinite user task recursion. Must be a power of two minus 1. + */ + static final int POLL_LIMIT = (1 << 10) - 1; /** * Queues supporting work-stealing as well as external task @@ -801,7 +804,8 @@ static final int MAXIMUM_QUEUE_CAPACITY = 1 << 26; // 64M // Instance fields - volatile int scanState; // versioned, <0: inactive; odd:scanning + + volatile int scanState; // versioned, negative if inactive int stackPred; // pool stack (ctl) predecessor int nsteals; // number of steals int hint; // randomization and stealer index hint @@ -814,7 +818,8 @@ final ForkJoinWorkerThread owner; // owning thread or null if shared volatile Thread parker; // == owner during call to park; else null volatile ForkJoinTask currentJoin; // task being joined in awaitJoin - volatile ForkJoinTask currentSteal; // mainly used by helpStealer + @sun.misc.Contended("group2") // separate from other fields + volatile ForkJoinTask currentSteal; // nonnull when running some task WorkQueue(ForkJoinPool pool, ForkJoinWorkerThread owner) { this.pool = pool; @@ -834,7 +839,7 @@ * Returns the approximate number of tasks in the queue. */ final int queueSize() { - int n = base - top; // non-owner callers must read base first + int n = base - top; // read base first return (n >= 0) ? 0 : -n; // ignore transient negative } @@ -844,33 +849,31 @@ * near-empty queue has at least one unclaimed task. */ final boolean isEmpty() { - ForkJoinTask[] a; int n, m, s; - return ((n = base - (s = top)) >= 0 || - (n == -1 && // possibly one task - ((a = array) == null || (m = a.length - 1) < 0 || - U.getObject - (a, (long)((m & (s - 1)) << ASHIFT) + ABASE) == null))); + ForkJoinTask[] a; int n, al, s; + return ((n = base - (s = top)) >= 0 || // possibly one task + (n == -1 && ((a = array) == null || + (al = a.length) == 0 || + a[(al - 1) & (s - 1)] == null))); } /** - * Pushes a task. Call only by owner in unshared queues. (The - * shared-queue version is embedded in method externalPush.) + * Pushes a task. Call only by owner in unshared queues. * * @param task the task. Caller must ensure non-null. * @throws RejectedExecutionException if array cannot be resized */ final void push(ForkJoinTask task) { - ForkJoinTask[] a; ForkJoinPool p; - int b = base, s = top, n; - if ((a = array) != null) { // ignore if queue removed - int m = a.length - 1; // fenced write for task visibility - U.putOrderedObject(a, ((m & s) << ASHIFT) + ABASE, task); - U.putOrderedInt(this, QTOP, s + 1); - if ((n = s - b) <= 1) { - if ((p = pool) != null) - p.signalWork(p.workQueues, this); + U.storeFence(); // ensure safe publication + int s = top, al, d; ForkJoinTask[] a; + if ((a = array) != null && (al = a.length) > 0) { + a[(al - 1) & s] = task; // relaxed writes OK + top = s + 1; + ForkJoinPool p = pool; + if ((d = base - s) == 0 && p != null) { + U.fullFence(); + p.signalWork(); } - else if (n >= m) + else if (al + d == 1) growArray(); } } @@ -883,22 +886,23 @@ final ForkJoinTask[] growArray() { ForkJoinTask[] oldA = array; int size = oldA != null ? oldA.length << 1 : INITIAL_QUEUE_CAPACITY; - if (size > MAXIMUM_QUEUE_CAPACITY) + if (size < INITIAL_QUEUE_CAPACITY || size > MAXIMUM_QUEUE_CAPACITY) throw new RejectedExecutionException("Queue capacity exceeded"); int oldMask, t, b; ForkJoinTask[] a = array = new ForkJoinTask[size]; - if (oldA != null && (oldMask = oldA.length - 1) >= 0 && + if (oldA != null && (oldMask = oldA.length - 1) > 0 && (t = top) - (b = base) > 0) { int mask = size - 1; do { // emulate poll from old array, push to new array - ForkJoinTask x; - int oldj = ((b & oldMask) << ASHIFT) + ABASE; - int j = ((b & mask) << ASHIFT) + ABASE; - x = (ForkJoinTask)U.getObjectVolatile(oldA, oldj); + int index = b & oldMask; + long offset = ((long)index << ASHIFT) + ABASE; + ForkJoinTask x = (ForkJoinTask) + U.getObjectVolatile(oldA, offset); if (x != null && - U.compareAndSwapObject(oldA, oldj, x, null)) - U.putObjectVolatile(a, j, x); + U.compareAndSwapObject(oldA, offset, x, null)) + a[b & mask] = x; } while (++b != t); + U.storeFence(); } return a; } @@ -908,16 +912,16 @@ * by owner in unshared queues. */ final ForkJoinTask pop() { - ForkJoinTask[] a; ForkJoinTask t; int m; - if ((a = array) != null && (m = a.length - 1) >= 0) { - for (int s; (s = top - 1) - base >= 0;) { - long j = ((m & s) << ASHIFT) + ABASE; - if ((t = (ForkJoinTask)U.getObject(a, j)) == null) - break; - if (U.compareAndSwapObject(a, j, t, null)) { - U.putOrderedInt(this, QTOP, s); - return t; - } + int b = base, s = top, al, i; ForkJoinTask[] a; + if ((a = array) != null && b != s && (al = a.length) > 0) { + int index = (al - 1) & --s; + long offset = ((long)index << ASHIFT) + ABASE; + ForkJoinTask t = (ForkJoinTask) + U.getObject(a, offset); + if (t != null && + U.compareAndSwapObject(a, offset, t, null)) { + top = s; + return t; } } return null; @@ -929,12 +933,15 @@ * appear in ForkJoinPool methods scan and helpStealer. */ final ForkJoinTask pollAt(int b) { - ForkJoinTask t; ForkJoinTask[] a; - if ((a = array) != null) { - int j = (((a.length - 1) & b) << ASHIFT) + ABASE; - if ((t = (ForkJoinTask)U.getObjectVolatile(a, j)) != null && - base == b && U.compareAndSwapObject(a, j, t, null)) { - base = b + 1; + ForkJoinTask[] a; int al; + if ((a = array) != null && (al = a.length) > 0) { + int index = (al - 1) & b; + long offset = ((long)index << ASHIFT) + ABASE; + ForkJoinTask t = (ForkJoinTask) + U.getObjectVolatile(a, offset); + if (t != null && b++ == base && + U.compareAndSwapObject(a, offset, t, null)) { + base = b; return t; } } @@ -945,20 +952,27 @@ * Takes next task, if one exists, in FIFO order. */ final ForkJoinTask poll() { - ForkJoinTask[] a; int b; ForkJoinTask t; - while ((b = base) - top < 0 && (a = array) != null) { - int j = (((a.length - 1) & b) << ASHIFT) + ABASE; - t = (ForkJoinTask)U.getObjectVolatile(a, j); - if (base == b) { - if (t != null) { - if (U.compareAndSwapObject(a, j, t, null)) { - base = b + 1; - return t; + for (;;) { + int b = base, s = top, d, al; ForkJoinTask[] a; + if ((a = array) != null && (d = b - s) < 0 && + (al = a.length) > 0) { + int index = (al - 1) & b; + long offset = ((long)index << ASHIFT) + ABASE; + ForkJoinTask t = (ForkJoinTask) + U.getObjectVolatile(a, offset); + if (b++ == base) { + if (t != null) { + if (U.compareAndSwapObject(a, offset, t, null)) { + base = b; + return t; + } } + else if (d == -1) + break; // now empty } - else if (b + 1 == top) // now empty - break; } + else + break; } return null; } @@ -967,37 +981,100 @@ * Takes next task, if one exists, in order specified by mode. */ final ForkJoinTask nextLocalTask() { - return (config & FIFO_QUEUE) == 0 ? pop() : poll(); + return (config < 0) ? poll() : pop(); } /** * Returns next task, if one exists, in order specified by mode. */ final ForkJoinTask peek() { - ForkJoinTask[] a = array; int m; - if (a == null || (m = a.length - 1) < 0) - return null; - int i = (config & FIFO_QUEUE) == 0 ? top - 1 : base; - int j = ((i & m) << ASHIFT) + ABASE; - return (ForkJoinTask)U.getObjectVolatile(a, j); + int al; ForkJoinTask[] a; + return ((a = array) != null && (al = a.length) > 0) ? + a[(al - 1) & (config < 0 ? base : top - 1)] : null; } /** * Pops the given task only if it is at the current top. - * (A shared version is available only via FJP.tryExternalUnpush) - */ - final boolean tryUnpush(ForkJoinTask t) { - ForkJoinTask[] a; int s; - if ((a = array) != null && (s = top) != base && - U.compareAndSwapObject - (a, (((a.length - 1) & --s) << ASHIFT) + ABASE, t, null)) { - U.putOrderedInt(this, QTOP, s); - return true; + */ + final boolean tryUnpush(ForkJoinTask task) { + int b = base, s = top, al; ForkJoinTask[] a; + if ((a = array) != null && b != s && (al = a.length) > 0) { + int index = (al - 1) & --s; + long offset = ((long)index << ASHIFT) + ABASE; + if (U.compareAndSwapObject(a, offset, task, null)) { + top = s; + return true; + } } return false; } /** + * Shared version of push. Fails if already locked. + * + * @return status: > 0 locked, 0 possibly was empty, < 0 was nonempty + */ + final int sharedPush(ForkJoinTask task) { + int stat; + if (U.compareAndSwapInt(this, QLOCK, 0, 1)) { + int b = base, s = top, al, d; ForkJoinTask[] a; + if ((a = array) != null && (al = a.length) > 0 && + al - 1 + (d = b - s) > 0) { + a[(al - 1) & s] = task; + top = s + 1; // relaxed writes OK here + qlock = 0; + stat = (d < 0 && b == base) ? d : 0; + } + else { + growAndSharedPush(task); + stat = 0; + } + } + else + stat = 1; + return stat; + } + + /** + * Helper for sharedPush; called only when locked and resize + * needed. + */ + private void growAndSharedPush(ForkJoinTask task) { + try { + growArray(); + int s = top, al; ForkJoinTask[] a; + if ((a = array) != null && (al = a.length) > 0) { + a[(al - 1) & s] = task; + top = s + 1; + } + } finally { + qlock = 0; + } + } + + /** + * Shared version of pop. + */ + final boolean trySharedUnpush(ForkJoinTask task) { + boolean popped = false; + int s = top - 1, al; ForkJoinTask[] a; + if ((a = array) != null && (al = a.length) > 0) { + int index = (al - 1) & s; + long offset = ((long)index << ASHIFT) + ABASE; + ForkJoinTask t = (ForkJoinTask) U.getObject(a, offset); + if (t == task && + U.compareAndSwapInt(this, QLOCK, 0, 1)) { + if (U.compareAndSwapObject(a, offset, task, null)) { + popped = true; + top = s; + } + U.putOrderedInt(this, QLOCK, 0); + } + } + return popped; + } + + /** * Removes and cancels all known tasks, ignoring any exceptions. */ final void cancelAll() { @@ -1017,66 +1094,88 @@ // Specialized execution methods /** - * Polls and runs tasks until empty. + * Pops and executes up to POLL_LIMIT tasks or until empty. */ - final void pollAndExecAll() { - for (ForkJoinTask t; (t = poll()) != null;) - t.doExec(); + final void localPopAndExec() { + for (int nexec = 0;;) { + int b = base, s = top, al; ForkJoinTask[] a; + if ((a = array) != null && b != s && (al = a.length) > 0) { + int index = (al - 1) & --s; + long offset = ((long)index << ASHIFT) + ABASE; + ForkJoinTask t = (ForkJoinTask) + U.getAndSetObject(a, offset, null); + if (t != null) { + top = s; + (currentSteal = t).doExec(); + if (++nexec > POLL_LIMIT) + break; + } + else + break; + } + else + break; + } } /** - * Removes and executes all local tasks. If LIFO, invokes - * pollAndExecAll. Otherwise implements a specialized pop loop - * to exec until empty. + * Polls and executes up to POLL_LIMIT tasks or until empty. */ - final void execLocalTasks() { - int b = base, m, s; - ForkJoinTask[] a = array; - if (b - (s = top - 1) <= 0 && a != null && - (m = a.length - 1) >= 0) { - if ((config & FIFO_QUEUE) == 0) { - for (ForkJoinTask t;;) { - if ((t = (ForkJoinTask)U.getAndSetObject - (a, ((m & s) << ASHIFT) + ABASE, null)) == null) - break; - U.putOrderedInt(this, QTOP, s); + final void localPollAndExec() { + for (int nexec = 0;;) { + int b = base, s = top, al; ForkJoinTask[] a; + if ((a = array) != null && b != s && (al = a.length) > 0) { + int index = (al - 1) & b++; + long offset = ((long)index << ASHIFT) + ABASE; + ForkJoinTask t = (ForkJoinTask) + U.getAndSetObject(a, offset, null); + if (t != null) { + base = b; t.doExec(); - if (base - (s = top - 1) > 0) + if (++nexec > POLL_LIMIT) break; } } else - pollAndExecAll(); + break; } } /** - * Executes the given task and any remaining local tasks. + * Executes the given task and (some) remaining local tasks. */ final void runTask(ForkJoinTask task) { if (task != null) { - scanState &= ~SCANNING; // mark as busy - (currentSteal = task).doExec(); - U.putOrderedObject(this, QCURRENTSTEAL, null); // release for GC - execLocalTasks(); + task.doExec(); + if (config < 0) + localPollAndExec(); + else + localPopAndExec(); + int ns = ++nsteals; ForkJoinWorkerThread thread = owner; - if (++nsteals < 0) // collect on overflow + currentSteal = null; + if (ns < 0) // collect on overflow transferStealCount(pool); - scanState |= SCANNING; if (thread != null) thread.afterTopLevelExec(); } } /** - * Adds steal count to pool stealCounter if it exists, and resets. + * Adds steal count to pool steal count if it exists, and resets. */ final void transferStealCount(ForkJoinPool p) { - AtomicLong sc; - if (p != null && (sc = p.stealCounter) != null) { - int s = nsteals; + AuxState aux; + if (p != null && (aux = p.auxState) != null) { + long s = nsteals; nsteals = 0; // if negative, correct for overflow - sc.getAndAdd((long)(s < 0 ? Integer.MAX_VALUE : s)); + if (s < 0) s = Integer.MAX_VALUE; + aux.lock(); + try { + aux.stealCount += s; + } finally { + aux.unlock(); + } } } @@ -1087,36 +1186,46 @@ * @return true if queue empty and task not known to be done */ final boolean tryRemoveAndExec(ForkJoinTask task) { - ForkJoinTask[] a; int m, s, b, n; - if ((a = array) != null && (m = a.length - 1) >= 0 && - task != null) { - while ((n = (s = top) - (b = base)) > 0) { - for (ForkJoinTask t;;) { // traverse from s to b - long j = ((--s & m) << ASHIFT) + ABASE; - if ((t = (ForkJoinTask)U.getObject(a, j)) == null) - return s + 1 == top; // shorter than expected + if (task != null && task.status >= 0) { + int b, s, d, al; ForkJoinTask[] a; + while ((d = (b = base) - (s = top)) < 0 && + (a = array) != null && (al = a.length) > 0) { + for (;;) { // traverse from s to b + int index = --s & (al - 1); + long offset = (index << ASHIFT) + ABASE; + ForkJoinTask t = (ForkJoinTask) + U.getObjectVolatile(a, offset); + if (t == null) + break; // restart else if (t == task) { boolean removed = false; if (s + 1 == top) { // pop - if (U.compareAndSwapObject(a, j, task, null)) { - U.putOrderedInt(this, QTOP, s); + if (U.compareAndSwapObject(a, offset, t, null)) { + top = s; removed = true; } } else if (base == b) // replace with proxy - removed = U.compareAndSwapObject( - a, j, task, new EmptyTask()); - if (removed) - task.doExec(); + removed = U.compareAndSwapObject(a, offset, t, + new EmptyTask()); + if (removed) { + ForkJoinTask ps = currentSteal; + (currentSteal = task).doExec(); + currentSteal = ps; + } break; } else if (t.status < 0 && s + 1 == top) { - if (U.compareAndSwapObject(a, j, t, null)) - U.putOrderedInt(this, QTOP, s); + if (U.compareAndSwapObject(a, offset, t, null)) { + top = s; + } break; // was cancelled } - if (--n == 0) + else if (++d == 0) { + if (base != b) // rescan + break; return false; + } } if (task.status < 0) return false; @@ -1130,27 +1239,31 @@ * in either shared or owned mode. Used only by helpComplete. */ final CountedCompleter popCC(CountedCompleter task, int mode) { - int s; ForkJoinTask[] a; Object o; - if (base - (s = top) < 0 && (a = array) != null) { - long j = (((a.length - 1) & (s - 1)) << ASHIFT) + ABASE; - if ((o = U.getObjectVolatile(a, j)) != null && - (o instanceof CountedCompleter)) { + int b = base, s = top, al; ForkJoinTask[] a; + if ((a = array) != null && b != s && (al = a.length) > 0) { + int index = (al - 1) & (s - 1); + long offset = ((long)index << ASHIFT) + ABASE; + ForkJoinTask o = (ForkJoinTask) + U.getObjectVolatile(a, offset); + if (o instanceof CountedCompleter) { CountedCompleter t = (CountedCompleter)o; for (CountedCompleter r = t;;) { if (r == task) { - if (mode < 0) { // must lock + if ((mode & IS_OWNED) == 0) { + boolean popped; if (U.compareAndSwapInt(this, QLOCK, 0, 1)) { - if (top == s && array == a && - U.compareAndSwapObject(a, j, t, null)) { - U.putOrderedInt(this, QTOP, s - 1); - U.putOrderedInt(this, QLOCK, 0); + if (popped = + U.compareAndSwapObject(a, offset, + t, null)) + top = s - 1; + U.putOrderedInt(this, QLOCK, 0); + if (popped) return t; - } - U.compareAndSwapInt(this, QLOCK, 1, 0); } } - else if (U.compareAndSwapObject(a, j, t, null)) { - U.putOrderedInt(this, QTOP, s - 1); + else if (U.compareAndSwapObject(a, offset, + t, null)) { + top = s - 1; return t; } break; @@ -1174,36 +1287,40 @@ * the base index, forced negative. */ final int pollAndExecCC(CountedCompleter task) { - int b, h; ForkJoinTask[] a; Object o; - if ((b = base) - top >= 0 || (a = array) == null) - h = b | Integer.MIN_VALUE; // to sense movement on re-poll - else { - long j = (((a.length - 1) & b) << ASHIFT) + ABASE; - if ((o = U.getObjectVolatile(a, j)) == null) - h = 2; // retryable + ForkJoinTask[] a; + int b = base, s = top, al, h; + if ((a = array) != null && b != s && (al = a.length) > 0) { + int index = (al - 1) & b; + long offset = ((long)index << ASHIFT) + ABASE; + ForkJoinTask o = (ForkJoinTask) + U.getObjectVolatile(a, offset); + if (o == null) + h = 2; // retryable else if (!(o instanceof CountedCompleter)) - h = -1; // unmatchable + h = -1; // unmatchable else { CountedCompleter t = (CountedCompleter)o; for (CountedCompleter r = t;;) { if (r == task) { - if (base == b && - U.compareAndSwapObject(a, j, t, null)) { - base = b + 1; + if (b++ == base && + U.compareAndSwapObject(a, offset, t, null)) { + base = b; t.doExec(); - h = 1; // success + h = 1; // success } else - h = 2; // lost CAS + h = 2; // lost CAS break; } else if ((r = r.completer) == null) { - h = -1; // unmatched + h = -1; // unmatched break; } } } } + else + h = b | Integer.MIN_VALUE; // to sense movement on re-poll return h; } @@ -1220,29 +1337,20 @@ } // Unsafe mechanics. Note that some are (and must be) the same as in FJP - private static final sun.misc.Unsafe U; - private static final int ABASE; - private static final int ASHIFT; - private static final long QTOP; + private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe(); private static final long QLOCK; - private static final long QCURRENTSTEAL; + private static final int ABASE; + private static final int ASHIFT; static { try { - U = sun.misc.Unsafe.getUnsafe(); - Class wk = WorkQueue.class; - Class ak = ForkJoinTask[].class; - QTOP = U.objectFieldOffset - (wk.getDeclaredField("top")); QLOCK = U.objectFieldOffset - (wk.getDeclaredField("qlock")); - QCURRENTSTEAL = U.objectFieldOffset - (wk.getDeclaredField("currentSteal")); - ABASE = U.arrayBaseOffset(ak); - int scale = U.arrayIndexScale(ak); + (WorkQueue.class.getDeclaredField("qlock")); + ABASE = U.arrayBaseOffset(ForkJoinTask[].class); + int scale = U.arrayIndexScale(ForkJoinTask[].class); if ((scale & (scale - 1)) != 0) - throw new Error("data type scale not a power of two"); + throw new Error("array index scale not a power of two"); ASHIFT = 31 - Integer.numberOfLeadingZeros(scale); - } catch (Exception e) { + } catch (ReflectiveOperationException e) { throw new Error(e); } } @@ -1259,9 +1367,9 @@ /** * Permission required for callers of methods that may start or - * kill threads. + * kill threads. Also used as a static lock in tryInitialize. */ - private static final RuntimePermission modifyThreadPermission; + static final RuntimePermission modifyThreadPermission; /** * Common (static) pool. Non-null for public use unless a static @@ -1277,12 +1385,12 @@ * common.parallelism field to be zero, but in that case still report * parallelism as 1 to reflect resulting caller-runs mechanics. */ - static final int commonParallelism; + static final int COMMON_PARALLELISM; /** * Limit on spare thread construction in tryCompensate. */ - private static int commonMaxSpares; + private static final int COMMON_MAX_SPARES; /** * Sequence number for creating workerNamePrefix. @@ -1300,44 +1408,28 @@ // static configuration constants /** - * Initial timeout value (in nanoseconds) for the thread + * Initial timeout value (in milliseconds) for the thread * triggering quiescence to park waiting for new work. On timeout, - * the thread will instead try to shrink the number of - * workers. The value should be large enough to avoid overly - * aggressive shrinkage during most transient stalls (long GCs - * etc). + * the thread will instead try to shrink the number of workers. + * The value should be large enough to avoid overly aggressive + * shrinkage during most transient stalls (long GCs etc). */ - private static final long IDLE_TIMEOUT = 2000L * 1000L * 1000L; // 2sec - - /** - * Tolerance for idle timeouts, to cope with timer undershoots - */ - private static final long TIMEOUT_SLOP = 20L * 1000L * 1000L; // 20ms + private static final long IDLE_TIMEOUT_MS = 2000L; // 2sec /** - * The initial value for commonMaxSpares during static - * initialization unless overridden using System property - * "java.util.concurrent.ForkJoinPool.common.maximumSpares". The - * default value is far in excess of normal requirements, but also - * far short of MAX_CAP and typical OS thread limits, so allows - * JVMs to catch misuse/abuse before running out of resources - * needed to do so. + * Tolerance for idle timeouts, to cope with timer undershoots. */ - private static final int DEFAULT_COMMON_MAX_SPARES = 256; + private static final long TIMEOUT_SLOP_MS = 20L; // 20ms /** - * Number of times to spin-wait before blocking. The spins (in - * awaitRunStateLock and awaitWork) currently use randomized - * spins. Currently set to zero to reduce CPU usage. - * - * If greater than zero the value of SPINS must be a power - * of two, at least 4. A value of 2048 causes spinning for a - * small fraction of typical context-switch times. - * - * If/when MWAIT-like intrinsics becomes available, they - * may allow quieter spinning. + * The default value for COMMON_MAX_SPARES. Overridable using the + * "java.util.concurrent.ForkJoinPool.common.maximumSpares" system + * property. The default value is far in excess of normal + * requirements, but also far short of MAX_CAP and typical OS + * thread limits, so allows JVMs to catch misuse/abuse before + * running out of resources needed to do so. */ - private static final int SPINS = 0; + private static final int DEFAULT_COMMON_MAX_SPARES = 256; /** * Increment for seed generators. See class ThreadLocal for @@ -1384,92 +1476,49 @@ private static final long ADD_WORKER = 0x0001L << (TC_SHIFT + 15); // sign // runState bits: SHUTDOWN must be negative, others arbitrary powers of two - private static final int RSLOCK = 1; - private static final int RSIGNAL = 1 << 1; - private static final int STARTED = 1 << 2; - private static final int STOP = 1 << 29; - private static final int TERMINATED = 1 << 30; + private static final int STARTED = 1; + private static final int STOP = 1 << 1; + private static final int TERMINATED = 1 << 2; private static final int SHUTDOWN = 1 << 31; // Instance fields volatile long ctl; // main pool control - volatile int runState; // lockable status + volatile int runState; final int config; // parallelism, mode - int indexSeed; // to generate worker index + AuxState auxState; // lock, steal counts volatile WorkQueue[] workQueues; // main registry + final String workerNamePrefix; // to create worker name string final ForkJoinWorkerThreadFactory factory; final UncaughtExceptionHandler ueh; // per-worker UEH - final String workerNamePrefix; // to create worker name string - volatile AtomicLong stealCounter; // also used as sync monitor - - /** - * Acquires the runState lock; returns current (locked) runState. - */ - private int lockRunState() { - int rs; - return ((((rs = runState) & RSLOCK) != 0 || - !U.compareAndSwapInt(this, RUNSTATE, rs, rs |= RSLOCK)) ? - awaitRunStateLock() : rs); - } /** - * Spins and/or blocks until runstate lock is available. See - * above for explanation. + * Instantiates fields upon first submission, or upon shutdown if + * no submissions. If checkTermination true, also responds to + * termination by external calls submitting tasks. */ - private int awaitRunStateLock() { - Object lock; - boolean wasInterrupted = false; - for (int spins = SPINS, r = 0, rs, ns;;) { - if (((rs = runState) & RSLOCK) == 0) { - if (U.compareAndSwapInt(this, RUNSTATE, rs, ns = rs | RSLOCK)) { - if (wasInterrupted) { - try { - Thread.currentThread().interrupt(); - } catch (SecurityException ignore) { - } - } - return ns; - } - } - else if (r == 0) - r = ThreadLocalRandom.nextSecondarySeed(); - else if (spins > 0) { - r ^= r << 6; r ^= r >>> 21; r ^= r << 7; // xorshift - if (r >= 0) - --spins; - } - else if ((rs & STARTED) == 0 || (lock = stealCounter) == null) - Thread.yield(); // initialization race - else if (U.compareAndSwapInt(this, RUNSTATE, rs, rs | RSIGNAL)) { - synchronized (lock) { - if ((runState & RSIGNAL) != 0) { - try { - lock.wait(); - } catch (InterruptedException ie) { - if (!(Thread.currentThread() instanceof - ForkJoinWorkerThread)) - wasInterrupted = true; - } - } - else - lock.notifyAll(); + private void tryInitialize(boolean checkTermination) { + if (runState == 0) { // bootstrap by locking static field + int p = config & SMASK; + int n = (p > 1) ? p - 1 : 1; // ensure at least 2 slots + n |= n >>> 1; // create workQueues array with size a power of two + n |= n >>> 2; + n |= n >>> 4; + n |= n >>> 8; + n |= n >>> 16; + n = ((n + 1) << 1) & SMASK; + AuxState aux = new AuxState(); + WorkQueue[] ws = new WorkQueue[n]; + synchronized (modifyThreadPermission) { // double-check + if (runState == 0) { + workQueues = ws; + auxState = aux; + runState = STARTED; } } } - } - - /** - * Unlocks and sets runState to newRunState. - * - * @param oldRunState a value returned from lockRunState - * @param newRunState the next value (must have lock bit clear). - */ - private void unlockRunState(int oldRunState, int newRunState) { - if (!U.compareAndSwapInt(this, RUNSTATE, oldRunState, newRunState)) { - Object lock = stealCounter; - runState = newRunState; // clears RSIGNAL bit - if (lock != null) - synchronized (lock) { lock.notifyAll(); } + if (checkTermination && runState < 0) { + tryTerminate(false, false); // help terminate + throw new RejectedExecutionException(); } } @@ -1480,14 +1529,18 @@ * count has already been incremented as a reservation. Invokes * deregisterWorker on any failure. * + * @param isSpare true if this is a spare thread * @return true if successful */ - private boolean createWorker() { + private boolean createWorker(boolean isSpare) { ForkJoinWorkerThreadFactory fac = factory; Throwable ex = null; ForkJoinWorkerThread wt = null; + WorkQueue q; try { if (fac != null && (wt = fac.newThread(this)) != null) { + if (isSpare && (q = wt.workQueue) != null) + q.config |= SPARE_WORKER; wt.start(); return true; } @@ -1507,21 +1560,12 @@ * this holds (otherwise, a new worker is not needed). */ private void tryAddWorker(long c) { - boolean add = false; do { long nc = ((AC_MASK & (c + AC_UNIT)) | (TC_MASK & (c + TC_UNIT))); - if (ctl == c) { - int rs, stop; // check if terminating - if ((stop = (rs = lockRunState()) & STOP) == 0) - add = U.compareAndSwapLong(this, CTL, c, nc); - unlockRunState(rs, rs & ~RSLOCK); - if (stop != 0) - break; - if (add) { - createWorker(); - break; - } + if (ctl == c && U.compareAndSwapLong(this, CTL, c, nc)) { + createWorker(false); + break; } } while (((c = ctl) & ADD_WORKER) != 0L && (int)c == 0); } @@ -1535,37 +1579,39 @@ */ final WorkQueue registerWorker(ForkJoinWorkerThread wt) { UncaughtExceptionHandler handler; + AuxState aux; wt.setDaemon(true); // configure thread if ((handler = ueh) != null) wt.setUncaughtExceptionHandler(handler); WorkQueue w = new WorkQueue(this, wt); int i = 0; // assign a pool index int mode = config & MODE_MASK; - int rs = lockRunState(); - try { - WorkQueue[] ws; int n; // skip if no array - if ((ws = workQueues) != null && (n = ws.length) > 0) { - int s = indexSeed += SEED_INCREMENT; // unlikely to collide - int m = n - 1; - i = ((s << 1) | 1) & m; // odd-numbered indices - if (ws[i] != null) { // collision - int probes = 0; // step by approx half n - int step = (n <= 4) ? 2 : ((n >>> 1) & EVENMASK) + 2; - while (ws[i = (i + step) & m] != null) { - if (++probes >= n) { - workQueues = ws = Arrays.copyOf(ws, n <<= 1); - m = n - 1; - probes = 0; + if ((aux = auxState) != null) { + aux.lock(); + try { + int s = (int)(aux.indexSeed += SEED_INCREMENT), n, m; + WorkQueue[] ws = workQueues; + if (ws != null && (n = ws.length) > 0) { + i = (m = n - 1) & ((s << 1) | 1); // odd-numbered indices + if (ws[i] != null) { // collision + int probes = 0; // step by approx half n + int step = (n <= 4) ? 2 : ((n >>> 1) & EVENMASK) + 2; + while (ws[i = (i + step) & m] != null) { + if (++probes >= n) { + workQueues = ws = Arrays.copyOf(ws, n <<= 1); + m = n - 1; + probes = 0; + } } } + w.hint = s; // use as random seed + w.config = i | mode; + w.scanState = i | (s & 0x7fff0000); // random seq bits + ws[i] = w; } - w.hint = s; // use as random seed - w.config = i | mode; - w.scanState = i; // publication fence - ws[i] = w; + } finally { + aux.unlock(); } - } finally { - unlockRunState(rs, rs & ~RSLOCK); } wt.setName(workerNamePrefix.concat(Integer.toString(i >>> 1))); return w; @@ -1583,31 +1629,40 @@ final void deregisterWorker(ForkJoinWorkerThread wt, Throwable ex) { WorkQueue w = null; if (wt != null && (w = wt.workQueue) != null) { - WorkQueue[] ws; // remove index from array + AuxState aux; WorkQueue[] ws; // remove index from array int idx = w.config & SMASK; - int rs = lockRunState(); - if ((ws = workQueues) != null && ws.length > idx && ws[idx] == w) - ws[idx] = null; - unlockRunState(rs, rs & ~RSLOCK); + int ns = w.nsteals; + if ((aux = auxState) != null) { + aux.lock(); + try { + if ((ws = workQueues) != null && ws.length > idx && + ws[idx] == w) + ws[idx] = null; + aux.stealCount += ns; + } finally { + aux.unlock(); + } + } } - long c; // decrement counts - do {} while (!U.compareAndSwapLong - (this, CTL, c = ctl, ((AC_MASK & (c - AC_UNIT)) | - (TC_MASK & (c - TC_UNIT)) | - (SP_MASK & c)))); + if (w == null || (w.config & UNREGISTERED) == 0) { // else pre-adjusted + long c; // decrement counts + do {} while (!U.compareAndSwapLong + (this, CTL, c = ctl, ((AC_MASK & (c - AC_UNIT)) | + (TC_MASK & (c - TC_UNIT)) | + (SP_MASK & c)))); + } if (w != null) { + w.currentSteal = null; w.qlock = -1; // ensure set - w.transferStealCount(this); w.cancelAll(); // cancel remaining tasks } - for (;;) { // possibly replace - WorkQueue[] ws; int m, sp; - if (tryTerminate(false, false) || w == null || w.array == null || - (runState & STOP) != 0 || (ws = workQueues) == null || - (m = ws.length - 1) < 0) // already terminating + while (tryTerminate(false, false) >= 0) { // possibly replace + WorkQueue[] ws; int wl, sp; long c; + if (w == null || w.array == null || + (ws = workQueues) == null || (wl = ws.length) <= 0) break; - if ((sp = (int)(c = ctl)) != 0) { // wake up replacement - if (tryRelease(c, ws[sp & m], AC_UNIT)) + else if ((sp = (int)(c = ctl)) != 0) { // wake up replacement + if (tryRelease(c, ws[(wl - 1) & sp], AC_UNIT)) break; } else if (ex != null && (c & ADD_WORKER) != 0L) { @@ -1627,35 +1682,33 @@ /** * Tries to create or activate a worker if too few are active. - * - * @param ws the worker array to use to find signallees - * @param q a WorkQueue --if non-null, don't retry if now empty */ - final void signalWork(WorkQueue[] ws, WorkQueue q) { - long c; int sp, i; WorkQueue v; Thread p; - while ((c = ctl) < 0L) { // too few active - if ((sp = (int)c) == 0) { // no idle workers - if ((c & ADD_WORKER) != 0L) // too few workers + final void signalWork() { + for (;;) { + long c; int sp, i; WorkQueue v; WorkQueue[] ws; + if ((c = ctl) >= 0L) // enough workers + break; + else if ((sp = (int)c) == 0) { // no idle workers + if ((c & ADD_WORKER) != 0L) // too few workers tryAddWorker(c); break; } - if (ws == null) // unstarted/terminated - break; - if (ws.length <= (i = sp & SMASK)) // terminated - break; - if ((v = ws[i]) == null) // terminating - break; - int vs = (sp + SS_SEQ) & ~INACTIVE; // next scanState - int d = sp - v.scanState; // screen CAS - long nc = (UC_MASK & (c + AC_UNIT)) | (SP_MASK & v.stackPred); - if (d == 0 && U.compareAndSwapLong(this, CTL, c, nc)) { - v.scanState = vs; // activate v - if ((p = v.parker) != null) - U.unpark(p); - break; + else if ((ws = workQueues) == null) + break; // unstarted/terminated + else if (ws.length <= (i = sp & SMASK)) + break; // terminated + else if ((v = ws[i]) == null) + break; // terminating + else { + int ns = sp & ~UNSIGNALLED; + int vs = v.scanState; + long nc = (v.stackPred & SP_MASK) | (UC_MASK & (c + AC_UNIT)); + if (sp == vs && U.compareAndSwapLong(this, CTL, c, nc)) { + v.scanState = ns; + LockSupport.unpark(v.parker); + break; + } } - if (q != null && q.base == q.top) // no more work - break; } } @@ -1670,174 +1723,287 @@ * @return true if successful */ private boolean tryRelease(long c, WorkQueue v, long inc) { - int sp = (int)c, vs = (sp + SS_SEQ) & ~INACTIVE; Thread p; - if (v != null && v.scanState == sp) { // v is at top of stack - long nc = (UC_MASK & (c + inc)) | (SP_MASK & v.stackPred); - if (U.compareAndSwapLong(this, CTL, c, nc)) { - v.scanState = vs; - if ((p = v.parker) != null) - U.unpark(p); + int sp = (int)c, ns = sp & ~UNSIGNALLED; + if (v != null) { + int vs = v.scanState; + long nc = (v.stackPred & SP_MASK) | (UC_MASK & (c + inc)); + if (sp == vs && U.compareAndSwapLong(this, CTL, c, nc)) { + v.scanState = ns; + LockSupport.unpark(v.parker); return true; } } return false; } - // Scanning for tasks + /** + * With approx probability of a missed signal, tries (once) to + * reactivate worker w (or some other worker), failing if stale or + * known to be already active. + * + * @param w the worker + * @param ws the workQueue array to use + * @param r random seed + */ + private void tryReactivate(WorkQueue w, WorkQueue[] ws, int r) { + long c; int sp, wl; WorkQueue v; + if ((sp = (int)(c = ctl)) != 0 && w != null && + ws != null && (wl = ws.length) > 0 && + ((sp ^ r) & SS_SEQ) == 0 && + (v = ws[(wl - 1) & sp]) != null) { + long nc = (v.stackPred & SP_MASK) | (UC_MASK & (c + AC_UNIT)); + int ns = sp & ~UNSIGNALLED; + if (w.scanState < 0 && + v.scanState == sp && + U.compareAndSwapLong(this, CTL, c, nc)) { + v.scanState = ns; + LockSupport.unpark(v.parker); + } + } + } + + /** + * If worker w exists and is active, enqueues and sets status to inactive. + * + * @param w the worker + * @param ss current (non-negative) scanState + */ + private void inactivate(WorkQueue w, int ss) { + int ns = (ss + SS_SEQ) | UNSIGNALLED; + long lc = ns & SP_MASK, nc, c; + if (w != null) { + w.scanState = ns; + do { + nc = lc | (UC_MASK & ((c = ctl) - AC_UNIT)); + w.stackPred = (int)c; + } while (!U.compareAndSwapLong(this, CTL, c, nc)); + } + } + + /** + * Possibly blocks worker w waiting for signal, or returns + * negative status if the worker should terminate. May return + * without status change if multiple stale unparks and/or + * interrupts occur. + * + * @param w the calling worker + * @return negative if w should terminate + */ + private int awaitWork(WorkQueue w) { + int stat = 0; + if (w != null && w.scanState < 0) { + long c = ctl; + if ((int)(c >> AC_SHIFT) + (config & SMASK) <= 0) + stat = timedAwaitWork(w, c); // possibly quiescent + else if ((runState & STOP) != 0) + stat = w.qlock = -1; // pool terminating + else if (w.scanState < 0) { + w.parker = Thread.currentThread(); + if (w.scanState < 0) // recheck after write + LockSupport.park(this); + w.parker = null; + if ((runState & STOP) != 0) + stat = w.qlock = -1; // recheck + else if (w.scanState < 0) + Thread.interrupted(); // clear status + } + } + return stat; + } + + /** + * Possibly triggers shutdown and tries (once) to block worker + * when pool is (or may be) quiescent. Waits up to a duration + * determined by number of workers. On timeout, if ctl has not + * changed, terminates the worker, which will in turn wake up + * another worker to possibly repeat this process. + * + * @param w the calling worker + * @return negative if w should terminate + */ + private int timedAwaitWork(WorkQueue w, long c) { + int stat = 0; + int scale = 1 - (short)(c >>> TC_SHIFT); + long deadline = (((scale <= 0) ? 1 : scale) * IDLE_TIMEOUT_MS + + System.currentTimeMillis()); + if ((runState >= 0 || (stat = tryTerminate(false, false)) > 0) && + w != null && w.scanState < 0) { + int ss; AuxState aux; + w.parker = Thread.currentThread(); + if (w.scanState < 0) + LockSupport.parkUntil(this, deadline); + w.parker = null; + if ((runState & STOP) != 0) + stat = w.qlock = -1; // pool terminating + else if ((ss = w.scanState) < 0 && !Thread.interrupted() && + (int)c == ss && (aux = auxState) != null && ctl == c && + deadline - System.currentTimeMillis() <= TIMEOUT_SLOP_MS) { + aux.lock(); + try { // pre-deregister + WorkQueue[] ws; + int cfg = w.config, idx = cfg & SMASK; + long nc = ((UC_MASK & (c - TC_UNIT)) | + (SP_MASK & w.stackPred)); + if ((runState & STOP) == 0 && + (ws = workQueues) != null && + idx < ws.length && idx >= 0 && ws[idx] == w && + U.compareAndSwapLong(this, CTL, c, nc)) { + ws[idx] = null; + w.config = cfg | UNREGISTERED; + stat = w.qlock = -1; + } + } finally { + aux.unlock(); + } + } + } + return stat; + } + + /** + * If the given worker is a spare with no queued tasks, and there + * are enough existing workers, drops it from ctl counts and sets + * its state to terminated. + * + * @param w the calling worker -- must be a spare + * @return true if dropped (in which case it must not process more tasks) + */ + private boolean tryDropSpare(WorkQueue w) { + if (w != null && w.isEmpty()) { // no local tasks + long c; int sp, wl; WorkQueue[] ws; WorkQueue v; + while ((short)((c = ctl) >> TC_SHIFT) > 0 && + ((sp = (int)c) != 0 || (int)(c >> AC_SHIFT) > 0) && + (ws = workQueues) != null && (wl = ws.length) > 0) { + boolean dropped, canDrop; + if (sp == 0) { // no queued workers + long nc = ((AC_MASK & (c - AC_UNIT)) | + (TC_MASK & (c - TC_UNIT)) | (SP_MASK & c)); + dropped = U.compareAndSwapLong(this, CTL, c, nc); + } + else if ( + (v = ws[(wl - 1) & sp]) == null || v.scanState != sp) + dropped = false; // stale; retry + else { + long nc = v.stackPred & SP_MASK; + if (w == v || w.scanState >= 0) { + canDrop = true; // w unqueued or topmost + nc |= ((AC_MASK & c) | // ensure replacement + (TC_MASK & (c - TC_UNIT))); + } + else { // w may be queued + canDrop = false; // help uncover + nc |= ((AC_MASK & (c + AC_UNIT)) | + (TC_MASK & c)); + } + if (U.compareAndSwapLong(this, CTL, c, nc)) { + v.scanState = sp & ~UNSIGNALLED; + LockSupport.unpark(v.parker); + dropped = canDrop; + } + else + dropped = false; + } + if (dropped) { // pre-deregister + int cfg = w.config, idx = cfg & SMASK; + if (idx >= 0 && idx < ws.length && ws[idx] == w) + ws[idx] = null; + w.config = cfg | UNREGISTERED; + w.qlock = -1; + return true; + } + } + } + return false; + } /** * Top-level runloop for workers, called by ForkJoinWorkerThread.run. */ final void runWorker(WorkQueue w) { - w.growArray(); // allocate queue - int seed = w.hint; // initially holds randomization hint - int r = (seed == 0) ? 1 : seed; // avoid 0 for xorShift - for (ForkJoinTask t;;) { - if ((t = scan(w, r)) != null) - w.runTask(t); - else if (!awaitWork(w, r)) - break; - r ^= r << 13; r ^= r >>> 17; r ^= r << 5; // xorshift + w.growArray(); // allocate queue + int bound = (w.config & SPARE_WORKER) != 0 ? 0 : POLL_LIMIT; + long seed = w.hint * 0xdaba0b6eb09322e3L; // initial random seed + if ((runState & STOP) == 0) { + for (long r = (seed == 0L) ? 1L : seed;;) { // ensure nonzero + if (bound == 0 && tryDropSpare(w)) + break; + // high bits of prev seed for step; current low bits for idx + int step = (int)(r >>> 48) | 1; + r ^= r >>> 12; r ^= r << 25; r ^= r >>> 27; // xorshift + if (scan(w, bound, step, (int)r) < 0 && awaitWork(w) < 0) + break; + } } } + // Scanning for tasks + /** - * Scans for and tries to steal a top-level task. Scans start at a - * random location, randomly moving on apparent contention, - * otherwise continuing linearly until reaching two consecutive - * empty passes over all queues with the same checksum (summing - * each base index of each queue, that moves on each steal), at - * which point the worker tries to inactivate and then re-scans, - * attempting to re-activate (itself or some other worker) if - * finding a task; otherwise returning null to await work. Scans - * otherwise touch as little memory as possible, to reduce - * disruption on other scanning threads. + * Repeatedly scans for and tries to steal and execute (via + * workQueue.runTask) a queued task. Each scan traverses queues in + * pseudorandom permutation. Upon finding a non-empty queue, makes + * at most the given bound attempts to re-poll (fewer if + * contended) on the same queue before returning (impossible + * scanState value) 0 to restart scan. Else returns after at least + * 1 and at most 32 full scans. * * @param w the worker (via its WorkQueue) - * @param r a random seed - * @return a task, or null if none found + * @param bound repoll bound as bitmask (0 if spare) + * @param step (circular) index increment per iteration (must be odd) + * @param r a random seed for origin index + * @return negative if should await signal */ - private ForkJoinTask scan(WorkQueue w, int r) { - WorkQueue[] ws; int m; - if ((ws = workQueues) != null && (m = ws.length - 1) > 0 && w != null) { - int ss = w.scanState; // initially non-negative - for (int origin = r & m, k = origin, oldSum = 0, checkSum = 0;;) { - WorkQueue q; ForkJoinTask[] a; ForkJoinTask t; - int b, n; long c; - if ((q = ws[k]) != null) { - if ((n = (b = q.base) - q.top) < 0 && - (a = q.array) != null) { // non-empty - long i = (((a.length - 1) & b) << ASHIFT) + ABASE; - if ((t = ((ForkJoinTask) - U.getObjectVolatile(a, i))) != null && - q.base == b) { - if (ss >= 0) { - if (U.compareAndSwapObject(a, i, t, null)) { - q.base = b + 1; - if (n < -1) // signal others - signalWork(ws, q); - return t; - } - } - else if (oldSum == 0 && // try to activate - w.scanState < 0) - tryRelease(c = ctl, ws[m & (int)c], AC_UNIT); - } - if (ss < 0) // refresh - ss = w.scanState; - r ^= r << 1; r ^= r >>> 3; r ^= r << 10; - origin = k = r & m; // move and rescan - oldSum = checkSum = 0; - continue; + private int scan(WorkQueue w, int bound, int step, int r) { + int stat = 0, wl; WorkQueue[] ws; + if ((ws = workQueues) != null && w != null && (wl = ws.length) > 0) { + for (int m = wl - 1, + origin = m & r, idx = origin, + npolls = 0, + ss = w.scanState;;) { // negative if inactive + WorkQueue q; ForkJoinTask[] a; int b, al; + if ((q = ws[idx]) != null && (b = q.base) - q.top < 0 && + (a = q.array) != null && (al = a.length) > 0) { + int index = (al - 1) & b; + long offset = ((long)index << ASHIFT) + ABASE; + ForkJoinTask t = (ForkJoinTask) + U.getObjectVolatile(a, offset); + if (t == null) + break; // empty or busy + else if (b++ != q.base) + break; // busy + else if (ss < 0) { + tryReactivate(w, ws, r); + break; // retry upon rescan } - checkSum += b; - } - if ((k = (k + 1) & m) == origin) { // continue until stable - if ((ss >= 0 || (ss == (ss = w.scanState))) && - oldSum == (oldSum = checkSum)) { - if (ss < 0 || w.qlock < 0) // already inactive + else if (!U.compareAndSwapObject(a, offset, t, null)) + break; // contended + else { + q.base = b; + w.currentSteal = t; + if (b != q.top) // propagate signal + signalWork(); + w.runTask(t); + if (++npolls > bound) break; - int ns = ss | INACTIVE; // try to inactivate - long nc = ((SP_MASK & ns) | - (UC_MASK & ((c = ctl) - AC_UNIT))); - w.stackPred = (int)c; // hold prev stack top - U.putInt(w, QSCANSTATE, ns); - if (U.compareAndSwapLong(this, CTL, c, nc)) - ss = ns; - else - w.scanState = ss; // back out } - checkSum = 0; + } + else if (npolls != 0) // rescan + break; + else if ((idx = (idx + step) & m) == origin) { + if (ss < 0) { // await signal + stat = ss; + break; + } + else if (r >= 0) { + inactivate(w, ss); + break; + } + else + r <<= 1; // at most 31 rescans } } } - return null; - } - - /** - * Possibly blocks worker w waiting for a task to steal, or - * returns false if the worker should terminate. If inactivating - * w has caused the pool to become quiescent, checks for pool - * termination, and, so long as this is not the only worker, waits - * for up to a given duration. On timeout, if ctl has not - * changed, terminates the worker, which will in turn wake up - * another worker to possibly repeat this process. - * - * @param w the calling worker - * @param r a random seed (for spins) - * @return false if the worker should terminate - */ - private boolean awaitWork(WorkQueue w, int r) { - if (w == null || w.qlock < 0) // w is terminating - return false; - for (int pred = w.stackPred, spins = SPINS, ss;;) { - if ((ss = w.scanState) >= 0) - break; - else if (spins > 0) { - r ^= r << 6; r ^= r >>> 21; r ^= r << 7; - if (r >= 0 && --spins == 0) { // randomize spins - WorkQueue v; WorkQueue[] ws; int s, j; AtomicLong sc; - if (pred != 0 && (ws = workQueues) != null && - (j = pred & SMASK) < ws.length && - (v = ws[j]) != null && // see if pred parking - (v.parker == null || v.scanState >= 0)) - spins = SPINS; // continue spinning - } - } - else if (w.qlock < 0) // recheck after spins - return false; - else if (!Thread.interrupted()) { - long c, prevctl, parkTime, deadline; - int ac = (int)((c = ctl) >> AC_SHIFT) + (config & SMASK); - if ((ac <= 0 && tryTerminate(false, false)) || - (runState & STOP) != 0) // pool terminating - return false; - if (ac <= 0 && ss == (int)c) { // is last waiter - prevctl = (UC_MASK & (c + AC_UNIT)) | (SP_MASK & pred); - int t = (short)(c >>> TC_SHIFT); // shrink excess spares - if (t > 2 && U.compareAndSwapLong(this, CTL, c, prevctl)) - return false; // else use timed wait - parkTime = IDLE_TIMEOUT * ((t >= 0) ? 1 : 1 - t); - deadline = System.nanoTime() + parkTime - TIMEOUT_SLOP; - } - else - prevctl = parkTime = deadline = 0L; - Thread wt = Thread.currentThread(); - U.putObject(wt, PARKBLOCKER, this); // emulate LockSupport - w.parker = wt; - if (w.scanState < 0 && ctl == c) // recheck before park - U.park(false, parkTime); - U.putOrderedObject(w, QPARKER, null); - U.putObject(wt, PARKBLOCKER, null); - if (w.scanState >= 0) - break; - if (parkTime != 0L && ctl == c && - deadline - System.nanoTime() <= 0L && - U.compareAndSwapLong(this, CTL, c, prevctl)) - return false; // shrink pool - } - } - return true; + return stat; } // Joining tasks @@ -1849,7 +2015,7 @@ * eligible tasks popped from the worker's own queue (via * popCC). Otherwise it scans others, randomly moving on * contention or execution, deciding to give up based on a - * checksum (via return codes frob pollAndExecCC). The maxTasks + * checksum (via return codes from pollAndExecCC). The maxTasks * argument supports external usages; internal calls use zero, * allowing unbounded steps (external calls trap non-positive * values). @@ -1860,15 +2026,17 @@ */ final int helpComplete(WorkQueue w, CountedCompleter task, int maxTasks) { - WorkQueue[] ws; int s = 0, m; - if ((ws = workQueues) != null && (m = ws.length - 1) >= 0 && + WorkQueue[] ws; int s = 0, wl; + if ((ws = workQueues) != null && (wl = ws.length) > 1 && task != null && w != null) { - int mode = w.config; // for popCC - int r = w.hint ^ w.top; // arbitrary seed for origin - int origin = r & m; // first queue to scan - int h = 1; // 1:ran, >1:contended, <0:hash - for (int k = origin, oldSum = 0, checkSum = 0;;) { - CountedCompleter p; WorkQueue q; + for (int m = wl - 1, + mode = w.config, + r = ~mode, // scanning seed + origin = r & m, k = origin, // first queue to scan + step = 3, // first scan step + h = 1, // 1:ran, >1:contended, <0:hash + oldSum = 0, checkSum = 0;;) { + CountedCompleter p; WorkQueue q; int i; if ((s = task.status) < 0) break; if (h == 1 && (p = w.popCC(task, mode)) != null) { @@ -1878,19 +2046,20 @@ origin = k; // reset oldSum = checkSum = 0; } - else { // poll other queues - if ((q = ws[k]) == null) + else { // poll other worker queues + if ((i = k | 1) < 0 || i > m || (q = ws[i]) == null) h = 0; else if ((h = q.pollAndExecCC(task)) < 0) checkSum += h; if (h > 0) { if (h == 1 && maxTasks != 0 && --maxTasks == 0) break; + step = (r >>> 16) | 3; r ^= r << 13; r ^= r >>> 17; r ^= r << 5; // xorshift - origin = k = r & m; // move and restart + k = origin = r & m; // move and restart oldSum = checkSum = 0; } - else if ((k = (k + 1) & m) == origin) { + else if ((k = (k + step) & m) == origin) { if (oldSum == (oldSum = checkSum)) break; checkSum = 0; @@ -1903,7 +2072,7 @@ /** * Tries to locate and execute tasks for a stealer of the given - * task, or in turn one of its stealers, Traces currentSteal -> + * task, or in turn one of its stealers. Traces currentSteal -> * currentJoin links looking for a thread working on a descendant * of the given task and with a non-empty queue to steal back and * execute tasks from. The first call to this method upon a @@ -1915,63 +2084,74 @@ * @param task the task to join */ private void helpStealer(WorkQueue w, ForkJoinTask task) { - WorkQueue[] ws = workQueues; - int oldSum = 0, checkSum, m; - if (ws != null && (m = ws.length - 1) >= 0 && w != null && - task != null) { - do { // restart point - checkSum = 0; // for stability check + if (task != null && w != null) { + ForkJoinTask ps = w.currentSteal; + WorkQueue[] ws; int wl, oldSum = 0; + outer: while (w.tryRemoveAndExec(task) && task.status >= 0 && + (ws = workQueues) != null && (wl = ws.length) > 0) { ForkJoinTask subtask; + int m = wl - 1, checkSum = 0; // for stability check WorkQueue j = w, v; // v is subtask stealer descent: for (subtask = task; subtask.status >= 0; ) { - for (int h = j.hint | 1, k = 0, i; ; k += 2) { - if (k > m) // can't find stealer - break descent; - if ((v = ws[i = (h + k) & m]) != null) { + for (int h = j.hint | 1, k = 0, i;;) { + if ((v = ws[i = (h + (k << 1)) & m]) != null) { if (v.currentSteal == subtask) { j.hint = i; break; } checkSum += v.base; } + if (++k > m) // can't find stealer + break outer; } + for (;;) { // help v or descend - ForkJoinTask[] a; int b; + ForkJoinTask[] a; int b, al; + if (subtask.status < 0) // too late to help + break descent; checkSum += (b = v.base); ForkJoinTask next = v.currentJoin; - if (subtask.status < 0 || j.currentJoin != subtask || - v.currentSteal != subtask) // stale - break descent; - if (b - v.top >= 0 || (a = v.array) == null) { - if ((subtask = next) == null) + ForkJoinTask t = null; + if ((a = v.array) != null && (al = a.length) > 0) { + int index = (al - 1) & b; + long offset = ((long)index << ASHIFT) + ABASE; + t = (ForkJoinTask) + U.getObjectVolatile(a, offset); + if (t != null && b++ == v.base) { + if (j.currentJoin != subtask || + v.currentSteal != subtask || + subtask.status < 0) + break descent; // stale + if (U.compareAndSwapObject(a, offset, t, null)) { + v.base = b; + w.currentSteal = t; + for (int top = w.top;;) { + t.doExec(); // help + w.currentSteal = ps; + if (task.status < 0) + break outer; + if (w.top == top) + break; // run local tasks + if ((t = w.pop()) == null) + break descent; + w.currentSteal = t; + } + } + } + } + if (t == null && b == v.base && b - v.top >= 0) { + if ((subtask = next) == null) { // try to descend + if (next == v.currentJoin && + oldSum == (oldSum = checkSum)) + break outer; break descent; + } j = v; break; } - int i = (((a.length - 1) & b) << ASHIFT) + ABASE; - ForkJoinTask t = ((ForkJoinTask) - U.getObjectVolatile(a, i)); - if (v.base == b) { - if (t == null) // stale - break descent; - if (U.compareAndSwapObject(a, i, t, null)) { - v.base = b + 1; - ForkJoinTask ps = w.currentSteal; - int top = w.top; - do { - U.putOrderedObject(w, QCURRENTSTEAL, t); - t.doExec(); // clear local tasks too - } while (task.status >= 0 && - w.top != top && - (t = w.pop()) != null); - U.putOrderedObject(w, QCURRENTSTEAL, ps); - if (w.base != w.top) - return; // can't further help - } - } } } - } while (task.status >= 0 && oldSum != (oldSum = checkSum)); + } } } @@ -1984,45 +2164,44 @@ * @param w caller */ private boolean tryCompensate(WorkQueue w) { - boolean canBlock; - WorkQueue[] ws; long c; int m, pc, sp; - if (w == null || w.qlock < 0 || // caller terminating - (ws = workQueues) == null || (m = ws.length - 1) <= 0 || - (pc = config & SMASK) == 0) // parallelism disabled + boolean canBlock; int wl; + long c = ctl; + WorkQueue[] ws = workQueues; + int pc = config & SMASK; + int ac = pc + (int)(c >> AC_SHIFT); + int tc = pc + (short)(c >> TC_SHIFT); + if (w == null || w.qlock < 0 || pc == 0 || // terminating or disabled + ws == null || (wl = ws.length) <= 0) canBlock = false; - else if ((sp = (int)(c = ctl)) != 0) // release idle worker - canBlock = tryRelease(c, ws[sp & m], 0L); else { - int ac = (int)(c >> AC_SHIFT) + pc; - int tc = (short)(c >> TC_SHIFT) + pc; - int nbusy = 0; // validate saturation - for (int i = 0; i <= m; ++i) { // two passes of odd indices - WorkQueue v; - if ((v = ws[((i << 1) | 1) & m]) != null) { - if ((v.scanState & SCANNING) != 0) - break; - ++nbusy; + int m = wl - 1, sp; + boolean busy = true; // validate ac + for (int i = 0; i <= m; ++i) { + int k; WorkQueue v; + if ((k = (i << 1) | 1) <= m && k >= 0 && (v = ws[k]) != null && + v.scanState >= 0 && v.currentSteal == null) { + busy = false; + break; } } - if (nbusy != (tc << 1) || ctl != c) - canBlock = false; // unstable or stale + if (!busy || ctl != c) + canBlock = false; // unstable or stale + else if ((sp = (int)c) != 0) // release idle worker + canBlock = tryRelease(c, ws[m & sp], 0L); else if (tc >= pc && ac > 1 && w.isEmpty()) { long nc = ((AC_MASK & (c - AC_UNIT)) | - (~AC_MASK & c)); // uncompensated + (~AC_MASK & c)); // uncompensated canBlock = U.compareAndSwapLong(this, CTL, c, nc); } else if (tc >= MAX_CAP || - (this == common && tc >= pc + commonMaxSpares)) + (this == common && tc >= pc + COMMON_MAX_SPARES)) throw new RejectedExecutionException( "Thread limit exceeded replacing blocked worker"); - else { // similar to tryAddWorker - boolean add = false; int rs; // CAS within lock - long nc = ((AC_MASK & c) | - (TC_MASK & (c + TC_UNIT))); - if (((rs = lockRunState()) & STOP) == 0) - add = U.compareAndSwapLong(this, CTL, c, nc); - unlockRunState(rs, rs & ~RSLOCK); - canBlock = add && createWorker(); // throws on exception + else { // similar to tryAddWorker + boolean isSpare = (tc >= pc); + long nc = (AC_MASK & c) | (TC_MASK & (c + TC_UNIT)); + canBlock = (U.compareAndSwapLong(this, CTL, c, nc) && + createWorker(isSpare)); // throws on exception } } return canBlock; @@ -2038,33 +2217,35 @@ */ final int awaitJoin(WorkQueue w, ForkJoinTask task, long deadline) { int s = 0; - if (task != null && w != null) { + if (w != null) { ForkJoinTask prevJoin = w.currentJoin; - U.putOrderedObject(w, QCURRENTJOIN, task); - CountedCompleter cc = (task instanceof CountedCompleter) ? - (CountedCompleter)task : null; - for (;;) { - if ((s = task.status) < 0) - break; - if (cc != null) - helpComplete(w, cc, 0); - else if (w.base == w.top || w.tryRemoveAndExec(task)) - helpStealer(w, task); - if ((s = task.status) < 0) - break; - long ms, ns; - if (deadline == 0L) - ms = 0L; - else if ((ns = deadline - System.nanoTime()) <= 0L) - break; - else if ((ms = TimeUnit.NANOSECONDS.toMillis(ns)) <= 0L) - ms = 1L; - if (tryCompensate(w)) { - task.internalWait(ms); - U.getAndAddLong(this, CTL, AC_UNIT); + if (task != null && (s = task.status) >= 0) { + w.currentJoin = task; + CountedCompleter cc = (task instanceof CountedCompleter) ? + (CountedCompleter)task : null; + for (;;) { + if (cc != null) + helpComplete(w, cc, 0); + else + helpStealer(w, task); + if ((s = task.status) < 0) + break; + long ms, ns; + if (deadline == 0L) + ms = 0L; + else if ((ns = deadline - System.nanoTime()) <= 0L) + break; + else if ((ms = TimeUnit.NANOSECONDS.toMillis(ns)) <= 0L) + ms = 1L; + if (tryCompensate(w)) { + task.internalWait(ms); + U.getAndAddLong(this, CTL, AC_UNIT); + } + if ((s = task.status) < 0) + break; } + w.currentJoin = prevJoin; } - U.putOrderedObject(w, QCURRENTJOIN, prevJoin); } return s; } @@ -2077,10 +2258,11 @@ * caller if, by the time it tries to use the queue, it is empty. */ private WorkQueue findNonEmptyStealQueue() { - WorkQueue[] ws; int m; // one-shot version of scan loop + WorkQueue[] ws; int wl; // one-shot version of scan loop int r = ThreadLocalRandom.nextSecondarySeed(); - if ((ws = workQueues) != null && (m = ws.length - 1) >= 0) { - for (int origin = r & m, k = origin, oldSum = 0, checkSum = 0;;) { + if ((ws = workQueues) != null && (wl = ws.length) > 0) { + int m = wl - 1, origin = r & m; + for (int k = origin, oldSum = 0, checkSum = 0;;) { WorkQueue q; int b; if ((q = ws[k]) != null) { if ((b = q.base) - q.top < 0) @@ -2105,25 +2287,27 @@ */ final void helpQuiescePool(WorkQueue w) { ForkJoinTask ps = w.currentSteal; // save context + int wc = w.config; for (boolean active = true;;) { - long c; WorkQueue q; ForkJoinTask t; int b; - w.execLocalTasks(); // run locals before each scan - if ((q = findNonEmptyStealQueue()) != null) { + long c; WorkQueue q; ForkJoinTask t; + if (wc >= 0 && (t = w.pop()) != null) { // run locals if LIFO + (w.currentSteal = t).doExec(); + w.currentSteal = ps; + } + else if ((q = findNonEmptyStealQueue()) != null) { if (!active) { // re-establish active count active = true; U.getAndAddLong(this, CTL, AC_UNIT); } - if ((b = q.base) - q.top < 0 && (t = q.pollAt(b)) != null) { - U.putOrderedObject(w, QCURRENTSTEAL, t); - t.doExec(); + if ((t = q.pollAt(q.base)) != null) { + (w.currentSteal = t).doExec(); + w.currentSteal = ps; if (++w.nsteals < 0) w.transferStealCount(this); } } else if (active) { // decrement active count without queuing long nc = (AC_MASK & ((c = ctl) - AC_UNIT)) | (~AC_MASK & c); - if ((int)(nc >> AC_SHIFT) + (config & SMASK) <= 0) - break; // bypass decrement-then-increment if (U.compareAndSwapLong(this, CTL, c, nc)) active = false; } @@ -2131,7 +2315,6 @@ U.compareAndSwapLong(this, CTL, c, c + AC_UNIT)) break; } - U.putOrderedObject(w, QCURRENTSTEAL, ps); } /** @@ -2141,12 +2324,12 @@ */ final ForkJoinTask nextTaskFor(WorkQueue w) { for (ForkJoinTask t;;) { - WorkQueue q; int b; + WorkQueue q; if ((t = w.nextLocalTask()) != null) return t; if ((q = findNonEmptyStealQueue()) == null) return null; - if ((b = q.base) - q.top < 0 && (t = q.pollAt(b)) != null) + if ((t = q.pollAt(q.base)) != null) return t; } } @@ -2195,9 +2378,8 @@ */ static int getSurplusQueuedTaskCount() { Thread t; ForkJoinWorkerThread wt; ForkJoinPool pool; WorkQueue q; - if (((t = Thread.currentThread()) instanceof ForkJoinWorkerThread)) { - int p = (pool = (wt = (ForkJoinWorkerThread)t).pool). - config & SMASK; + if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) { + int p = (pool = (wt = (ForkJoinWorkerThread)t).pool).config & SMASK; int n = (q = wt.workQueue).top - q.base; int a = (int)(pool.ctl >> AC_SHIFT) + p; return n - (a > (p >>>= 1) ? 0 : @@ -2216,212 +2398,165 @@ * * @param now if true, unconditionally terminate, else only * if no work and no active workers - * @param enable if true, enable shutdown when next possible - * @return true if now terminating or terminated + * @param enable if true, terminate when next possible + * @return -1: terminating/terminated, 0: retry if internal caller, else 1 */ - private boolean tryTerminate(boolean now, boolean enable) { - int rs; - if (this == common) // cannot shut down - return false; - if ((rs = runState) >= 0) { - if (!enable) - return false; - rs = lockRunState(); // enter SHUTDOWN phase - unlockRunState(rs, (rs & ~RSLOCK) | SHUTDOWN); + private int tryTerminate(boolean now, boolean enable) { + int rs; // 3 phases: try to set SHUTDOWN, then STOP, then TERMINATED + + while ((rs = runState) >= 0) { + if (!enable || this == common) // cannot shutdown + return 1; + else if (rs == 0) + tryInitialize(false); // ensure initialized + else + U.compareAndSwapInt(this, RUNSTATE, rs, rs | SHUTDOWN); } - if ((rs & STOP) == 0) { + if ((rs & STOP) == 0) { // try to initiate termination if (!now) { // check quiescence for (long oldSum = 0L;;) { // repeat until stable - WorkQueue[] ws; WorkQueue w; int m, b; long c; + WorkQueue[] ws; WorkQueue w; int b; long checkSum = ctl; if ((int)(checkSum >> AC_SHIFT) + (config & SMASK) > 0) - return false; // still active workers - if ((ws = workQueues) == null || (m = ws.length - 1) <= 0) - break; // check queues - for (int i = 0; i <= m; ++i) { - if ((w = ws[i]) != null) { - if ((b = w.base) != w.top || w.scanState >= 0 || - w.currentSteal != null) { - tryRelease(c = ctl, ws[m & (int)c], AC_UNIT); - return false; // arrange for recheck + return 0; // still active workers + if ((ws = workQueues) != null) { + for (int i = 0; i < ws.length; ++i) { + if ((w = ws[i]) != null) { + checkSum += (b = w.base); + if (w.currentSteal != null || b != w.top) + return 0; // retry if internal caller } - checkSum += b; - if ((i & 1) == 0) - w.qlock = -1; // try to disable external } } if (oldSum == (oldSum = checkSum)) break; } } - if ((runState & STOP) == 0) { - rs = lockRunState(); // enter STOP phase - unlockRunState(rs, (rs & ~RSLOCK) | STOP); - } + do {} while (!U.compareAndSwapInt(this, RUNSTATE, + rs = runState, rs | STOP)); } - int pass = 0; // 3 passes to help terminate - for (long oldSum = 0L;;) { // or until done or stable - WorkQueue[] ws; WorkQueue w; ForkJoinWorkerThread wt; int m; + for (long oldSum = 0L;;) { // repeat until stable + WorkQueue[] ws; WorkQueue w; ForkJoinWorkerThread wt; long checkSum = ctl; - if ((short)(checkSum >>> TC_SHIFT) + (config & SMASK) <= 0 || - (ws = workQueues) == null || (m = ws.length - 1) <= 0) { - if ((runState & TERMINATED) == 0) { - rs = lockRunState(); // done - unlockRunState(rs, (rs & ~RSLOCK) | TERMINATED); - synchronized (this) { notifyAll(); } // for awaitTermination - } - break; - } - for (int i = 0; i <= m; ++i) { - if ((w = ws[i]) != null) { - checkSum += w.base; - w.qlock = -1; // try to disable - if (pass > 0) { - w.cancelAll(); // clear queue - if (pass > 1 && (wt = w.owner) != null) { - if (!wt.isInterrupted()) { - try { // unblock join + if ((ws = workQueues) != null) { // help terminate others + for (int i = 0; i < ws.length; ++i) { + if ((w = ws[i]) != null) { + w.cancelAll(); // clear queues + checkSum += w.base; + if (w.qlock >= 0) { + w.qlock = -1; // racy set OK + if ((wt = w.owner) != null) { + try { // unblock join or park wt.interrupt(); } catch (Throwable ignore) { } } - if (w.scanState < 0) - U.unpark(wt); // wake up } } } } - if (checkSum != oldSum) { // unstable - oldSum = checkSum; - pass = 0; - } - else if (pass > 3 && pass > m) // can't further help + if (oldSum == (oldSum = checkSum)) break; - else if (++pass > 1) { // try to dequeue - long c; int j = 0, sp; // bound attempts - while (j++ <= m && (sp = (int)(c = ctl)) != 0) - tryRelease(c, ws[sp & m], AC_UNIT); + } + + if ((short)(ctl >>> TC_SHIFT) + (config & SMASK) <= 0) { + runState = (STARTED | SHUTDOWN | STOP | TERMINATED); // final write + synchronized (this) { + notifyAll(); // for awaitTermination } } - return true; + + return -1; } // External operations /** - * Full version of externalPush, handling uncommon cases, as well - * as performing secondary initialization upon the first - * submission of the first task to the pool. It also detects + * Constructs and tries to install a new external queue, + * failing if the workQueues array already has a queue at + * the given index. + * + * @param index the index of the new queue + */ + private void tryCreateExternalQueue(int index) { + AuxState aux; + if ((aux = auxState) != null && index >= 0) { + WorkQueue q = new WorkQueue(this, null); + q.config = index; + q.scanState = ~UNSIGNALLED; + q.qlock = 1; // lock queue + boolean installed = false; + aux.lock(); + try { // lock pool to install + WorkQueue[] ws; + if ((ws = workQueues) != null && index < ws.length && + ws[index] == null) { + ws[index] = q; // else throw away + installed = true; + } + } finally { + aux.unlock(); + } + if (installed) { + try { + q.growArray(); + } finally { + q.qlock = 0; + } + } + } + } + + /** + * Adds the given task to a submission queue at submitter's + * current queue. Also performs secondary initialization upon the + * first submission of the first task to the pool, and detects * first submission by an external thread and creates a new shared * queue if the one at index if empty or contended. * * @param task the task. Caller must ensure non-null. */ - private void externalSubmit(ForkJoinTask task) { - int r; // initialize caller's probe + final void externalPush(ForkJoinTask task) { + int r; // initialize caller's probe if ((r = ThreadLocalRandom.getProbe()) == 0) { ThreadLocalRandom.localInit(); r = ThreadLocalRandom.getProbe(); } for (;;) { - WorkQueue[] ws; WorkQueue q; int rs, m, k; - boolean move = false; - if ((rs = runState) < 0) { - tryTerminate(false, false); // help terminate - throw new RejectedExecutionException(); - } - else if ((rs & STARTED) == 0 || // initialize - ((ws = workQueues) == null || (m = ws.length - 1) < 0)) { - int ns = 0; - rs = lockRunState(); - try { - if ((rs & STARTED) == 0) { - U.compareAndSwapObject(this, STEALCOUNTER, null, - new AtomicLong()); - // create workQueues array with size a power of two - int p = config & SMASK; // ensure at least 2 slots - int n = (p > 1) ? p - 1 : 1; - n |= n >>> 1; n |= n >>> 2; n |= n >>> 4; - n |= n >>> 8; n |= n >>> 16; n = (n + 1) << 1; - workQueues = new WorkQueue[n]; - ns = STARTED; - } - } finally { - unlockRunState(rs, (rs & ~RSLOCK) | ns); - } + WorkQueue q; int wl, k, stat; + int rs = runState; + WorkQueue[] ws = workQueues; + if (rs <= 0 || ws == null || (wl = ws.length) <= 0) + tryInitialize(true); + else if ((q = ws[k = (wl - 1) & r & SQMASK]) == null) + tryCreateExternalQueue(k); + else if ((stat = q.sharedPush(task)) < 0) + break; + else if (stat == 0) { + signalWork(); + break; } - else if ((q = ws[k = r & m & SQMASK]) != null) { - if (q.qlock == 0 && U.compareAndSwapInt(q, QLOCK, 0, 1)) { - ForkJoinTask[] a = q.array; - int s = q.top; - boolean submitted = false; // initial submission or resizing - try { // locked version of push - if ((a != null && a.length > s + 1 - q.base) || - (a = q.growArray()) != null) { - int j = (((a.length - 1) & s) << ASHIFT) + ABASE; - U.putOrderedObject(a, j, task); - U.putOrderedInt(q, QTOP, s + 1); - submitted = true; - } - } finally { - U.compareAndSwapInt(q, QLOCK, 1, 0); - } - if (submitted) { - signalWork(ws, q); - return; - } - } - move = true; // move on failure - } - else if (((rs = runState) & RSLOCK) == 0) { // create new queue - q = new WorkQueue(this, null); - q.hint = r; - q.config = k | SHARED_QUEUE; - q.scanState = INACTIVE; - rs = lockRunState(); // publish index - if (rs > 0 && (ws = workQueues) != null && - k < ws.length && ws[k] == null) - ws[k] = q; // else terminated - unlockRunState(rs, rs & ~RSLOCK); - } - else - move = true; // move if busy - if (move) + else // move if busy r = ThreadLocalRandom.advanceProbe(r); } } /** - * Tries to add the given task to a submission queue at - * submitter's current queue. Only the (vastly) most common path - * is directly handled in this method, while screening for need - * for externalSubmit. - * - * @param task the task. Caller must ensure non-null. + * Pushes a possibly-external submission. */ - final void externalPush(ForkJoinTask task) { - WorkQueue[] ws; WorkQueue q; int m; - int r = ThreadLocalRandom.getProbe(); - int rs = runState; - if ((ws = workQueues) != null && (m = (ws.length - 1)) >= 0 && - (q = ws[m & r & SQMASK]) != null && r != 0 && rs > 0 && - U.compareAndSwapInt(q, QLOCK, 0, 1)) { - ForkJoinTask[] a; int am, n, s; - if ((a = q.array) != null && - (am = a.length - 1) > (n = (s = q.top) - q.base)) { - int j = ((am & s) << ASHIFT) + ABASE; - U.putOrderedObject(a, j, task); - U.putOrderedInt(q, QTOP, s + 1); - U.putIntVolatile(q, QLOCK, 0); - if (n <= 1) - signalWork(ws, q); - return; - } - U.compareAndSwapInt(q, QLOCK, 1, 0); - } - externalSubmit(task); + private ForkJoinTask externalSubmit(ForkJoinTask task) { + Thread t; ForkJoinWorkerThread w; WorkQueue q; + if (task == null) + throw new NullPointerException(); + if (((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) && + (w = (ForkJoinWorkerThread)t).pool == this && + (q = w.workQueue) != null) + q.push(task); + else + externalPush(task); + return task; } /** @@ -2430,46 +2565,32 @@ static WorkQueue commonSubmitterQueue() { ForkJoinPool p = common; int r = ThreadLocalRandom.getProbe(); - WorkQueue[] ws; int m; + WorkQueue[] ws; int wl; return (p != null && (ws = p.workQueues) != null && - (m = ws.length - 1) >= 0) ? - ws[m & r & SQMASK] : null; + (wl = ws.length) > 0) ? + ws[(wl - 1) & r & SQMASK] : null; } /** - * Performs tryUnpush for an external submitter: Finds queue, - * locks if apparently non-empty, validates upon locking, and - * adjusts top. Each check can fail but rarely does. + * Performs tryUnpush for an external submitter. */ final boolean tryExternalUnpush(ForkJoinTask task) { - WorkQueue[] ws; WorkQueue w; ForkJoinTask[] a; int m, s; int r = ThreadLocalRandom.getProbe(); - if ((ws = workQueues) != null && (m = ws.length - 1) >= 0 && - (w = ws[m & r & SQMASK]) != null && - (a = w.array) != null && (s = w.top) != w.base) { - long j = (((a.length - 1) & (s - 1)) << ASHIFT) + ABASE; - if (U.compareAndSwapInt(w, QLOCK, 0, 1)) { - if (w.top == s && w.array == a && - U.getObject(a, j) == task && - U.compareAndSwapObject(a, j, task, null)) { - U.putOrderedInt(w, QTOP, s - 1); - U.putOrderedInt(w, QLOCK, 0); - return true; - } - U.compareAndSwapInt(w, QLOCK, 1, 0); - } - } - return false; + WorkQueue[] ws; WorkQueue w; int wl; + return ((ws = workQueues) != null && + (wl = ws.length) > 0 && + (w = ws[(wl - 1) & r & SQMASK]) != null && + w.trySharedUnpush(task)); } /** * Performs helpComplete for an external submitter. */ final int externalHelpComplete(CountedCompleter task, int maxTasks) { - WorkQueue[] ws; int n; + WorkQueue[] ws; int wl; int r = ThreadLocalRandom.getProbe(); - return ((ws = workQueues) == null || (n = ws.length) == 0) ? 0 : - helpComplete(ws[(n - 1) & r & SQMASK], task, maxTasks); + return ((ws = workQueues) != null && (wl = ws.length) > 0) ? + helpComplete(ws[(wl - 1) & r & SQMASK], task, maxTasks) : 0; } // Exported methods @@ -2617,7 +2738,7 @@ public T invoke(ForkJoinTask task) { if (task == null) throw new NullPointerException(); - externalPush(task); + externalSubmit(task); return task.join(); } @@ -2630,9 +2751,7 @@ * scheduled for execution */ public void execute(ForkJoinTask task) { - if (task == null) - throw new NullPointerException(); - externalPush(task); + externalSubmit(task); } // AbstractExecutorService methods @@ -2650,7 +2769,7 @@ job = (ForkJoinTask) task; else job = new ForkJoinTask.RunnableExecuteAction(task); - externalPush(job); + externalSubmit(job); } /** @@ -2664,10 +2783,7 @@ * scheduled for execution */ public ForkJoinTask submit(ForkJoinTask task) { - if (task == null) - throw new NullPointerException(); - externalPush(task); - return task; + return externalSubmit(task); } /** @@ -2676,9 +2792,7 @@ * scheduled for execution */ public ForkJoinTask submit(Callable task) { - ForkJoinTask job = new ForkJoinTask.AdaptedCallable(task); - externalPush(job); - return job; + return externalSubmit(new ForkJoinTask.AdaptedCallable(task)); } /** @@ -2687,9 +2801,7 @@ * scheduled for execution */ public ForkJoinTask submit(Runnable task, T result) { - ForkJoinTask job = new ForkJoinTask.AdaptedRunnable(task, result); - externalPush(job); - return job; + return externalSubmit(new ForkJoinTask.AdaptedRunnable(task, result)); } /** @@ -2705,8 +2817,7 @@ job = (ForkJoinTask) task; else job = new ForkJoinTask.AdaptedRunnableAction(task); - externalPush(job); - return job; + return externalSubmit(job); } /** @@ -2719,21 +2830,19 @@ // invocation of multiple tasks is at least as efficient. ArrayList> futures = new ArrayList<>(tasks.size()); - boolean done = false; try { for (Callable t : tasks) { ForkJoinTask f = new ForkJoinTask.AdaptedCallable(t); futures.add(f); - externalPush(f); + externalSubmit(f); } for (int i = 0, size = futures.size(); i < size; i++) ((ForkJoinTask)futures.get(i)).quietlyJoin(); - done = true; return futures; - } finally { - if (!done) - for (int i = 0, size = futures.size(); i < size; i++) - futures.get(i).cancel(false); + } catch (Throwable t) { + for (int i = 0, size = futures.size(); i < size; i++) + futures.get(i).cancel(false); + throw t; } } @@ -2773,7 +2882,7 @@ * @since 1.8 */ public static int getCommonPoolParallelism() { - return commonParallelism; + return COMMON_PARALLELISM; } /** @@ -2857,8 +2966,8 @@ * @return the number of steals */ public long getStealCount() { - AtomicLong sc = stealCounter; - long count = (sc == null) ? 0L : sc.get(); + AuxState sc = auxState; + long count = (sc == null) ? 0L : sc.stealCount; WorkQueue[] ws; WorkQueue w; if ((ws = workQueues) != null) { for (int i = 1; i < ws.length; i += 2) { @@ -2935,10 +3044,11 @@ * @return the next submission, or {@code null} if none */ protected ForkJoinTask pollSubmission() { - WorkQueue[] ws; WorkQueue w; ForkJoinTask t; - if ((ws = workQueues) != null) { - for (int i = 0; i < ws.length; i += 2) { - if ((w = ws[i]) != null && (t = w.poll()) != null) + WorkQueue[] ws; int wl; WorkQueue w; ForkJoinTask t; + int r = ThreadLocalRandom.nextSecondarySeed(); + if ((ws = workQueues) != null && (wl = ws.length) > 0) { + for (int m = wl - 1, i = 0; i < wl; ++i) { + if ((w = ws[(i << 1) & m]) != null && (t = w.poll()) != null) return t; } } @@ -2988,8 +3098,8 @@ public String toString() { // Use a single pass through workQueues to collect counts long qt = 0L, qs = 0L; int rc = 0; - AtomicLong sc = stealCounter; - long st = (sc == null) ? 0L : sc.get(); + AuxState sc = auxState; + long st = (sc == null) ? 0L : sc.stealCount; long c = ctl; WorkQueue[] ws; WorkQueue w; if ((ws = workQueues) != null) { @@ -3171,17 +3281,17 @@ } long startTime = System.nanoTime(); WorkQueue[] ws; - int r = 0, m; + int r = 0, wl; boolean found = true; while (!isQuiescent() && (ws = workQueues) != null && - (m = ws.length - 1) >= 0) { + (wl = ws.length) > 0) { if (!found) { if ((System.nanoTime() - startTime) > nanos) return false; Thread.yield(); // cannot block } found = false; - for (int j = (m + 1) << 2; j >= 0; --j) { + for (int m = wl - 1, j = (m + 1) << 2; j >= 0; --j) { ForkJoinTask t; WorkQueue q; int b, k; if ((k = r++ & m) <= m && k >= 0 && (q = ws[k]) != null && (b = q.base) - q.top < 0) { @@ -3223,7 +3333,7 @@ * *

    For example, here is a ManagedBlocker based on a * ReentrantLock: - *

     {@code
    +     * 
     {@code
          * class ManagedLocker implements ManagedBlocker {
          *   final ReentrantLock lock;
          *   boolean hasLock = false;
    @@ -3240,7 +3350,7 @@
          *
          * 

    Here is a class that possibly blocks waiting for an * item on a given queue: - *

     {@code
    +     * 
     {@code
          * class QueueTaker implements ManagedBlocker {
          *   final BlockingQueue queue;
          *   volatile E item = null;
    @@ -3291,7 +3401,7 @@
          *
          * 

    If not running in a ForkJoinPool, this method is * behaviorally equivalent to - *

     {@code
    +     * 
     {@code
          * while (!blocker.isReleasable())
          *   if (blocker.block())
          *     break;}
    @@ -3342,58 +3452,40 @@ } // Unsafe mechanics - private static final sun.misc.Unsafe U; - private static final int ABASE; - private static final int ASHIFT; + private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe(); private static final long CTL; private static final long RUNSTATE; - private static final long STEALCOUNTER; - private static final long PARKBLOCKER; - private static final long QTOP; - private static final long QLOCK; - private static final long QSCANSTATE; - private static final long QPARKER; - private static final long QCURRENTSTEAL; - private static final long QCURRENTJOIN; + private static final int ABASE; + private static final int ASHIFT; static { - // initialize field offsets for CAS etc try { - U = sun.misc.Unsafe.getUnsafe(); - Class k = ForkJoinPool.class; CTL = U.objectFieldOffset - (k.getDeclaredField("ctl")); + (ForkJoinPool.class.getDeclaredField("ctl")); RUNSTATE = U.objectFieldOffset - (k.getDeclaredField("runState")); - STEALCOUNTER = U.objectFieldOffset - (k.getDeclaredField("stealCounter")); - Class tk = Thread.class; - PARKBLOCKER = U.objectFieldOffset - (tk.getDeclaredField("parkBlocker")); - Class wk = WorkQueue.class; - QTOP = U.objectFieldOffset - (wk.getDeclaredField("top")); - QLOCK = U.objectFieldOffset - (wk.getDeclaredField("qlock")); - QSCANSTATE = U.objectFieldOffset - (wk.getDeclaredField("scanState")); - QPARKER = U.objectFieldOffset - (wk.getDeclaredField("parker")); - QCURRENTSTEAL = U.objectFieldOffset - (wk.getDeclaredField("currentSteal")); - QCURRENTJOIN = U.objectFieldOffset - (wk.getDeclaredField("currentJoin")); - Class ak = ForkJoinTask[].class; - ABASE = U.arrayBaseOffset(ak); - int scale = U.arrayIndexScale(ak); + (ForkJoinPool.class.getDeclaredField("runState")); + ABASE = U.arrayBaseOffset(ForkJoinTask[].class); + int scale = U.arrayIndexScale(ForkJoinTask[].class); if ((scale & (scale - 1)) != 0) - throw new Error("data type scale not a power of two"); + throw new Error("array index scale not a power of two"); ASHIFT = 31 - Integer.numberOfLeadingZeros(scale); - } catch (Exception e) { + } catch (ReflectiveOperationException e) { throw new Error(e); } - commonMaxSpares = DEFAULT_COMMON_MAX_SPARES; + // Reduce the risk of rare disastrous classloading in first call to + // LockSupport.park: https://bugs.openjdk.java.net/browse/JDK-8074773 + Class ensureLoaded = LockSupport.class; + + int commonMaxSpares = DEFAULT_COMMON_MAX_SPARES; + try { + String p = System.getProperty + ("java.util.concurrent.ForkJoinPool.common.maximumSpares"); + if (p != null) + commonMaxSpares = Integer.parseInt(p); + } catch (Exception ignore) {} + COMMON_MAX_SPARES = commonMaxSpares; + defaultForkJoinWorkerThreadFactory = new DefaultForkJoinWorkerThreadFactory(); modifyThreadPermission = new RuntimePermission("modifyThread"); @@ -3401,15 +3493,16 @@ common = java.security.AccessController.doPrivileged (new java.security.PrivilegedAction() { public ForkJoinPool run() { return makeCommonPool(); }}); - int par = common.config & SMASK; // report 1 even if threads disabled - commonParallelism = par > 0 ? par : 1; + + // report 1 even if threads disabled + COMMON_PARALLELISM = Math.max(common.config & SMASK, 1); } /** * Creates and returns the common pool, respecting user settings * specified via system properties. */ - private static ForkJoinPool makeCommonPool() { + static ForkJoinPool makeCommonPool() { int parallelism = -1; ForkJoinWorkerThreadFactory factory = null; UncaughtExceptionHandler handler = null; @@ -3420,8 +3513,6 @@ ("java.util.concurrent.ForkJoinPool.common.threadFactory"); String hp = System.getProperty ("java.util.concurrent.ForkJoinPool.common.exceptionHandler"); - String mp = System.getProperty - ("java.util.concurrent.ForkJoinPool.common.maximumSpares"); if (pp != null) parallelism = Integer.parseInt(pp); if (fp != null) @@ -3430,8 +3521,6 @@ if (hp != null) handler = ((UncaughtExceptionHandler)ClassLoader. getSystemClassLoader().loadClass(hp).newInstance()); - if (mp != null) - commonMaxSpares = Integer.parseInt(mp); } catch (Exception ignore) { } if (factory == null) { @@ -3450,9 +3539,9 @@ } /** - * Factory for innocuous worker threads + * Factory for innocuous worker threads. */ - static final class InnocuousForkJoinWorkerThreadFactory + private static final class InnocuousForkJoinWorkerThreadFactory implements ForkJoinWorkerThreadFactory { /** @@ -3473,9 +3562,8 @@ } public final ForkJoinWorkerThread newThread(ForkJoinPool pool) { - return (ForkJoinWorkerThread.InnocuousForkJoinWorkerThread) - java.security.AccessController.doPrivileged( - new java.security.PrivilegedAction() { + return java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { public ForkJoinWorkerThread run() { return new ForkJoinWorkerThread. InnocuousForkJoinWorkerThread(pool); diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinTask.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinTask.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinTask.java Wed Jul 05 20:54:48 2017 +0200 @@ -36,21 +36,13 @@ package java.util.concurrent; import java.io.Serializable; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.lang.reflect.Constructor; import java.util.Collection; import java.util.List; import java.util.RandomAccess; -import java.lang.ref.WeakReference; -import java.lang.ref.ReferenceQueue; -import java.util.concurrent.Callable; -import java.util.concurrent.CancellationException; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; -import java.util.concurrent.RejectedExecutionException; -import java.util.concurrent.RunnableFuture; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; import java.util.concurrent.locks.ReentrantLock; -import java.lang.reflect.Constructor; /** * Abstract base class for tasks that run within a {@link ForkJoinPool}. @@ -442,7 +434,8 @@ ExceptionNode next; final long thrower; // use id not ref to avoid weak cycles final int hashCode; // store task hashCode before weak ref disappears - ExceptionNode(ForkJoinTask task, Throwable ex, ExceptionNode next) { + ExceptionNode(ForkJoinTask task, Throwable ex, ExceptionNode next, + ReferenceQueue exceptionTableRefQueue) { super(task, exceptionTableRefQueue); this.ex = ex; this.next = next; @@ -468,7 +461,8 @@ int i = h & (t.length - 1); for (ExceptionNode e = t[i]; ; e = e.next) { if (e == null) { - t[i] = new ExceptionNode(this, ex, t[i]); + t[i] = new ExceptionNode(this, ex, t[i], + exceptionTableRefQueue); break; } if (e.get() == this) // already present @@ -561,8 +555,6 @@ * @return the exception, or null if none */ private Throwable getThrowableException() { - if ((status & DONE_MASK) != EXCEPTIONAL) - return null; int h = System.identityHashCode(this); ExceptionNode e; final ReentrantLock lock = exceptionTableLock; @@ -608,7 +600,7 @@ } /** - * Poll stale refs and remove them. Call only while holding lock. + * Polls stale refs and removes them. Call only while holding lock. */ private static void expungeStaleExceptions() { for (Object x; (x = exceptionTableRefQueue.poll()) != null;) { @@ -635,7 +627,7 @@ } /** - * If lock is available, poll stale refs and remove them. + * If lock is available, polls stale refs and removes them. * Called from ForkJoinPool when pools become quiescent. */ static final void helpExpungeStaleExceptions() { @@ -650,21 +642,23 @@ } /** - * A version of "sneaky throw" to relay exceptions + * A version of "sneaky throw" to relay exceptions. */ static void rethrow(Throwable ex) { - if (ex != null) - ForkJoinTask.uncheckedThrow(ex); + ForkJoinTask.uncheckedThrow(ex); } /** * The sneaky part of sneaky throw, relying on generics * limitations to evade compiler complaints about rethrowing - * unchecked exceptions + * unchecked exceptions. */ @SuppressWarnings("unchecked") static - void uncheckedThrow(Throwable t) throws T { - throw (T)t; // rely on vacuous cast + void uncheckedThrow(Throwable t) throws T { + if (t != null) + throw (T)t; // rely on vacuous cast + else + throw new Error("Unknown Exception"); } /** @@ -999,11 +993,10 @@ public final V get() throws InterruptedException, ExecutionException { int s = (Thread.currentThread() instanceof ForkJoinWorkerThread) ? doJoin() : externalInterruptibleAwaitDone(); - Throwable ex; if ((s &= DONE_MASK) == CANCELLED) throw new CancellationException(); - if (s == EXCEPTIONAL && (ex = getThrowableException()) != null) - throw new ExecutionException(ex); + if (s == EXCEPTIONAL) + throw new ExecutionException(getThrowableException()); return getRawResult(); } @@ -1058,13 +1051,11 @@ if (s >= 0) s = status; if ((s &= DONE_MASK) != NORMAL) { - Throwable ex; if (s == CANCELLED) throw new CancellationException(); if (s != EXCEPTIONAL) throw new TimeoutException(); - if ((ex = getThrowableException()) != null) - throw new ExecutionException(ex); + throw new ExecutionException(getThrowableException()); } return getRawResult(); } @@ -1090,10 +1081,10 @@ /** * Possibly executes tasks until the pool hosting the current task - * {@link ForkJoinPool#isQuiescent is quiescent}. This method may - * be of use in designs in which many tasks are forked, but none - * are explicitly joined, instead executing them until all are - * processed. + * {@linkplain ForkJoinPool#isQuiescent is quiescent}. This + * method may be of use in designs in which many tasks are forked, + * but none are explicitly joined, instead executing them until + * all are processed. */ public static void helpQuiesce() { Thread t; @@ -1129,10 +1120,12 @@ } /** - * Returns the pool hosting the current task execution, or null - * if this task is executing outside of any ForkJoinPool. + * Returns the pool hosting the current thread, or {@code null} + * if the current thread is executing outside of any ForkJoinPool. * - * @see #inForkJoinPool + *

    This method returns {@code null} if and only if {@link + * #inForkJoinPool} returns {@code false}. + * * @return the pool, or {@code null} if none */ public static ForkJoinPool getPool() { @@ -1299,6 +1292,23 @@ null; } + /** + * If the current thread is operating in a ForkJoinPool, + * unschedules and returns, without executing, a task externally + * submitted to the pool, if one is available. Availability may be + * transient, so a {@code null} result does not necessarily imply + * quiescence of the pool. This method is designed primarily to + * support extensions, and is unlikely to be useful otherwise. + * + * @return a task, or {@code null} if none are available + * @since 1.9 + */ + protected static ForkJoinTask pollSubmission() { + Thread t; + return ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ? + ((ForkJoinWorkerThread)t).pool.pollSubmission() : null; + } + // tag operations /** @@ -1312,16 +1322,16 @@ } /** - * Atomically sets the tag value for this task. + * Atomically sets the tag value for this task and returns the old value. * - * @param tag the tag value + * @param newValue the new tag value * @return the previous value of the tag * @since 1.8 */ - public final short setForkJoinTaskTag(short tag) { + public final short setForkJoinTaskTag(short newValue) { for (int s;;) { if (U.compareAndSwapInt(this, STATUS, s = status, - (s & ~SMASK) | (tag & SMASK))) + (s & ~SMASK) | (newValue & SMASK))) return (short)s; } } @@ -1334,24 +1344,24 @@ * before processing, otherwise exiting because the node has * already been visited. * - * @param e the expected tag value - * @param tag the new tag value + * @param expect the expected tag value + * @param update the new tag value * @return {@code true} if successful; i.e., the current value was - * equal to e and is now tag. + * equal to {@code expect} and was changed to {@code update}. * @since 1.8 */ - public final boolean compareAndSetForkJoinTaskTag(short e, short tag) { + public final boolean compareAndSetForkJoinTaskTag(short expect, short update) { for (int s;;) { - if ((short)(s = status) != e) + if ((short)(s = status) != expect) return false; if (U.compareAndSwapInt(this, STATUS, s, - (s & ~SMASK) | (tag & SMASK))) + (s & ~SMASK) | (update & SMASK))) return true; } } /** - * Adaptor for Runnables. This implements RunnableFuture + * Adapter for Runnables. This implements RunnableFuture * to be compliant with AbstractExecutorService constraints * when used in ForkJoinPool. */ @@ -1372,7 +1382,7 @@ } /** - * Adaptor for Runnables without results + * Adapter for Runnables without results. */ static final class AdaptedRunnableAction extends ForkJoinTask implements RunnableFuture { @@ -1389,7 +1399,7 @@ } /** - * Adaptor for Runnables in which failure forces worker exception + * Adapter for Runnables in which failure forces worker exception. */ static final class RunnableExecuteAction extends ForkJoinTask { final Runnable runnable; @@ -1407,7 +1417,7 @@ } /** - * Adaptor for Callables + * Adapter for Callables. */ static final class AdaptedCallable extends ForkJoinTask implements RunnableFuture { @@ -1423,8 +1433,6 @@ try { result = callable.call(); return true; - } catch (Error err) { - throw err; } catch (RuntimeException rex) { throw rex; } catch (Exception ex) { @@ -1509,7 +1517,7 @@ } // Unsafe mechanics - private static final sun.misc.Unsafe U; + private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe(); private static final long STATUS; static { @@ -1517,11 +1525,9 @@ exceptionTableRefQueue = new ReferenceQueue(); exceptionTable = new ExceptionNode[EXCEPTION_MAP_CAPACITY]; try { - U = sun.misc.Unsafe.getUnsafe(); - Class k = ForkJoinTask.class; STATUS = U.objectFieldOffset - (k.getDeclaredField("status")); - } catch (Exception e) { + (ForkJoinTask.class.getDeclaredField("status")); + } catch (ReflectiveOperationException e) { throw new Error(e); } } diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinWorkerThread.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinWorkerThread.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinWorkerThread.java Wed Jul 05 20:54:48 2017 +0200 @@ -87,7 +87,7 @@ } /** - * Version for InnocuousForkJoinWorkerThread + * Version for InnocuousForkJoinWorkerThread. */ ForkJoinWorkerThread(ForkJoinPool pool, ThreadGroup threadGroup, AccessControlContext acc) { @@ -179,28 +179,25 @@ } /** - * Non-public hook method for InnocuousForkJoinWorkerThread + * Non-public hook method for InnocuousForkJoinWorkerThread. */ void afterTopLevelExec() { } // Set up to allow setting thread fields in constructor - private static final sun.misc.Unsafe U; + private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe(); private static final long THREADLOCALS; private static final long INHERITABLETHREADLOCALS; private static final long INHERITEDACCESSCONTROLCONTEXT; static { try { - U = sun.misc.Unsafe.getUnsafe(); - Class tk = Thread.class; THREADLOCALS = U.objectFieldOffset - (tk.getDeclaredField("threadLocals")); + (Thread.class.getDeclaredField("threadLocals")); INHERITABLETHREADLOCALS = U.objectFieldOffset - (tk.getDeclaredField("inheritableThreadLocals")); + (Thread.class.getDeclaredField("inheritableThreadLocals")); INHERITEDACCESSCONTROLCONTEXT = U.objectFieldOffset - (tk.getDeclaredField("inheritedAccessControlContext")); - - } catch (Exception e) { + (Thread.class.getDeclaredField("inheritedAccessControlContext")); + } catch (ReflectiveOperationException e) { throw new Error(e); } } @@ -252,10 +249,10 @@ private static ThreadGroup createThreadGroup() { try { sun.misc.Unsafe u = sun.misc.Unsafe.getUnsafe(); - Class tk = Thread.class; - Class gk = ThreadGroup.class; - long tg = u.objectFieldOffset(tk.getDeclaredField("group")); - long gp = u.objectFieldOffset(gk.getDeclaredField("parent")); + long tg = u.objectFieldOffset + (Thread.class.getDeclaredField("group")); + long gp = u.objectFieldOffset + (ThreadGroup.class.getDeclaredField("parent")); ThreadGroup group = (ThreadGroup) u.getObject(Thread.currentThread(), tg); while (group != null) { @@ -265,7 +262,7 @@ "InnocuousForkJoinWorkerThreadGroup"); group = parent; } - } catch (Exception e) { + } catch (ReflectiveOperationException e) { throw new Error(e); } // fall through if null as cannot-happen safeguard diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/Future.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/Future.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/Future.java Wed Jul 05 20:54:48 2017 +0200 @@ -53,6 +53,7 @@ *

    * Sample Usage (Note that the following classes are all * made-up.) + * *

     {@code
      * interface ArchiveSearcher { String search(String target); }
      * class App {
    @@ -75,9 +76,9 @@
      * The {@link FutureTask} class is an implementation of {@code Future} that
      * implements {@code Runnable}, and so may be executed by an {@code Executor}.
      * For example, the above construction with {@code submit} could be replaced by:
    - *  
     {@code
    + * 
     {@code
      * FutureTask future =
    - *   new FutureTask(new Callable() {
    + *   new FutureTask<>(new Callable() {
      *     public String call() {
      *       return searcher.search(target);
      *   }});
    diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/FutureTask.java
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/FutureTask.java	Wed Oct 21 15:15:26 2015 -0700
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/FutureTask.java	Wed Jul 05 20:54:48 2017 +0200
    @@ -34,6 +34,7 @@
      */
     
     package java.util.concurrent;
    +
     import java.util.concurrent.locks.LockSupport;
     
     /**
    @@ -395,7 +396,7 @@
             throws InterruptedException {
             // The code below is very delicate, to achieve these goals:
             // - call nanoTime exactly once for each call to park
    -        // - if nanos <= 0, return promptly without allocation or nanoTime
    +        // - if nanos <= 0L, return promptly without allocation or nanoTime
             // - if nanos == Long.MIN_VALUE, don't underflow
             // - if nanos == Long.MAX_VALUE, and nanoTime is non-monotonic
             //   and we suffer a spurious wakeup, we will do no worse than
    @@ -404,19 +405,20 @@
             WaitNode q = null;
             boolean queued = false;
             for (;;) {
    -            if (Thread.interrupted()) {
    -                removeWaiter(q);
    -                throw new InterruptedException();
    -            }
    -
                 int s = state;
                 if (s > COMPLETING) {
                     if (q != null)
                         q.thread = null;
                     return s;
                 }
    -            else if (s == COMPLETING) // cannot time out yet
    +            else if (s == COMPLETING)
    +                // We may have already promised (via isDone) that we are done
    +                // so never return empty-handed or throw InterruptedException
                     Thread.yield();
    +            else if (Thread.interrupted()) {
    +                removeWaiter(q);
    +                throw new InterruptedException();
    +            }
                 else if (q == null) {
                     if (timed && nanos <= 0L)
                         return s;
    @@ -440,7 +442,9 @@
                         }
                         parkNanos = nanos - elapsed;
                     }
    -                LockSupport.parkNanos(this, parkNanos);
    +                // nanoTime may be slow; recheck before parking
    +                if (state < COMPLETING)
    +                    LockSupport.parkNanos(this, parkNanos);
                 }
                 else
                     LockSupport.park(this);
    @@ -480,20 +484,25 @@
         }
     
         // Unsafe mechanics
    -    private static final sun.misc.Unsafe U;
    +    private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
         private static final long STATE;
         private static final long RUNNER;
         private static final long WAITERS;
         static {
             try {
    -            U = sun.misc.Unsafe.getUnsafe();
    -            Class k = FutureTask.class;
    -            STATE   = U.objectFieldOffset(k.getDeclaredField("state"));
    -            RUNNER  = U.objectFieldOffset(k.getDeclaredField("runner"));
    -            WAITERS = U.objectFieldOffset(k.getDeclaredField("waiters"));
    -        } catch (Exception e) {
    +            STATE = U.objectFieldOffset
    +                (FutureTask.class.getDeclaredField("state"));
    +            RUNNER = U.objectFieldOffset
    +                (FutureTask.class.getDeclaredField("runner"));
    +            WAITERS = U.objectFieldOffset
    +                (FutureTask.class.getDeclaredField("waiters"));
    +        } catch (ReflectiveOperationException e) {
                 throw new Error(e);
             }
    +
    +        // Reduce the risk of rare disastrous classloading in first call to
    +        // LockSupport.park: https://bugs.openjdk.java.net/browse/JDK-8074773
    +        Class ensureLoaded = LockSupport.class;
         }
     
     }
    diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/Helpers.java
    --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/Helpers.java	Wed Jul 05 20:54:48 2017 +0200
    @@ -0,0 +1,118 @@
    +/*
    + * 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.  Oracle designates this
    + * particular file as subject to the "Classpath" exception as provided
    + * by Oracle in the LICENSE file that accompanied this code.
    + *
    + * 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.
    + */
    +
    +/*
    + * This file is available under and governed by the GNU General Public
    + * License version 2 only, as published by the Free Software Foundation.
    + * However, the following notice accompanied the original version of this
    + * file:
    + *
    + * Written by Martin Buchholz with assistance from members of JCP
    + * JSR-166 Expert Group and released to the public domain, as
    + * explained at http://creativecommons.org/publicdomain/zero/1.0/
    + */
    +
    +package java.util.concurrent;
    +
    +import java.util.Collection;
    +
    +/** Shared implementation code for java.util.concurrent. */
    +class Helpers {
    +    private Helpers() {}                // non-instantiable
    +
    +    /**
    +     * An implementation of Collection.toString() suitable for classes
    +     * with locks.  Instead of holding a lock for the entire duration of
    +     * toString(), or acquiring a lock for each call to Iterator.next(),
    +     * we hold the lock only during the call to toArray() (less
    +     * disruptive to other threads accessing the collection) and follows
    +     * the maxim "Never call foreign code while holding a lock".
    +     */
    +    static String collectionToString(Collection c) {
    +        final Object[] a = c.toArray();
    +        final int size = a.length;
    +        if (size == 0)
    +            return "[]";
    +        int charLength = 0;
    +
    +        // Replace every array element with its string representation
    +        for (int i = 0; i < size; i++) {
    +            Object e = a[i];
    +            // Extreme compatibility with AbstractCollection.toString()
    +            String s = (e == c) ? "(this Collection)" : objectToString(e);
    +            a[i] = s;
    +            charLength += s.length();
    +        }
    +
    +        return toString(a, size, charLength);
    +    }
    +
    +    /**
    +     * Like Arrays.toString(), but caller guarantees that size > 0,
    +     * each element with index 0 <= i < size is a non-null String,
    +     * and charLength is the sum of the lengths of the input Strings.
    +     */
    +    static String toString(Object[] a, int size, int charLength) {
    +        // assert a != null;
    +        // assert size > 0;
    +
    +        // Copy each string into a perfectly sized char[]
    +        // Length of [ , , , ] == 2 * size
    +        final char[] chars = new char[charLength + 2 * size];
    +        chars[0] = '[';
    +        int j = 1;
    +        for (int i = 0; i < size; i++) {
    +            if (i > 0) {
    +                chars[j++] = ',';
    +                chars[j++] = ' ';
    +            }
    +            String s = (String) a[i];
    +            int len = s.length();
    +            s.getChars(0, len, chars, j);
    +            j += len;
    +        }
    +        chars[j] = ']';
    +        // assert j == chars.length - 1;
    +        return new String(chars);
    +    }
    +
    +    /** Optimized form of: key + "=" + val */
    +    static String mapEntryToString(Object key, Object val) {
    +        final String k, v;
    +        final int klen, vlen;
    +        final char[] chars =
    +            new char[(klen = (k = objectToString(key)).length()) +
    +                     (vlen = (v = objectToString(val)).length()) + 1];
    +        k.getChars(0, klen, chars, 0);
    +        chars[klen] = '=';
    +        v.getChars(0, vlen, chars, klen + 1);
    +        return new String(chars);
    +    }
    +
    +    private static String objectToString(Object x) {
    +        // Extreme compatibility with StringBuilder.append(null)
    +        String s;
    +        return (x == null || (s = x.toString()) == null) ? "null" : s;
    +    }
    +}
    diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/LinkedBlockingDeque.java
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/LinkedBlockingDeque.java	Wed Oct 21 15:15:26 2015 -0700
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/LinkedBlockingDeque.java	Wed Jul 05 20:54:48 2017 +0200
    @@ -39,10 +39,10 @@
     import java.util.Collection;
     import java.util.Iterator;
     import java.util.NoSuchElementException;
    +import java.util.Spliterator;
    +import java.util.Spliterators;
     import java.util.concurrent.locks.Condition;
     import java.util.concurrent.locks.ReentrantLock;
    -import java.util.Spliterator;
    -import java.util.Spliterators;
     import java.util.function.Consumer;
     
     /**
    @@ -72,7 +72,7 @@
      *
      * @since 1.6
      * @author  Doug Lea
    - * @param  the type of elements held in this collection
    + * @param  the type of elements held in this deque
      */
     public class LinkedBlockingDeque
         extends AbstractQueue
    @@ -412,7 +412,7 @@
             lock.lockInterruptibly();
             try {
                 while (!linkFirst(node)) {
    -                if (nanos <= 0)
    +                if (nanos <= 0L)
                         return false;
                     nanos = notFull.awaitNanos(nanos);
                 }
    @@ -435,7 +435,7 @@
             lock.lockInterruptibly();
             try {
                 while (!linkLast(node)) {
    -                if (nanos <= 0)
    +                if (nanos <= 0L)
                         return false;
                     nanos = notFull.awaitNanos(nanos);
                 }
    @@ -517,7 +517,7 @@
             try {
                 E x;
                 while ( (x = unlinkFirst()) == null) {
    -                if (nanos <= 0)
    +                if (nanos <= 0L)
                         return null;
                     nanos = notEmpty.awaitNanos(nanos);
                 }
    @@ -535,7 +535,7 @@
             try {
                 E x;
                 while ( (x = unlinkLast()) == null) {
    -                if (nanos <= 0)
    +                if (nanos <= 0L)
                         return null;
                     nanos = notEmpty.awaitNanos(nanos);
                 }
    @@ -924,7 +924,7 @@
          * The following code can be used to dump the deque into a newly
          * allocated array of {@code String}:
          *
    -     *  
     {@code String[] y = x.toArray(new String[0]);}
    + *
     {@code String[] y = x.toArray(new String[0]);}
    * * Note that {@code toArray(new Object[0])} is identical in function to * {@code toArray()}. @@ -959,26 +959,7 @@ } public String toString() { - final ReentrantLock lock = this.lock; - lock.lock(); - try { - Node p = first; - if (p == null) - return "[]"; - - StringBuilder sb = new StringBuilder(); - sb.append('['); - for (;;) { - E e = p.item; - sb.append(e == this ? "(this Collection)" : e); - p = p.next; - if (p == null) - return sb.append(']').toString(); - sb.append(',').append(' '); - } - } finally { - lock.unlock(); - } + return Helpers.collectionToString(this); } /** @@ -1032,11 +1013,11 @@ } /** - * Base class for Iterators for LinkedBlockingDeque + * Base class for LinkedBlockingDeque iterators. */ private abstract class AbstractItr implements Iterator { /** - * The next node to return in next() + * The next node to return in next(). */ Node next; @@ -1192,8 +1173,9 @@ if (i > 0) { batch = i; return Spliterators.spliterator - (a, 0, i, Spliterator.ORDERED | Spliterator.NONNULL | - Spliterator.CONCURRENT); + (a, 0, i, (Spliterator.ORDERED | + Spliterator.NONNULL | + Spliterator.CONCURRENT)); } } return null; diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/LinkedBlockingQueue.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/LinkedBlockingQueue.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/LinkedBlockingQueue.java Wed Jul 05 20:54:48 2017 +0200 @@ -35,15 +35,15 @@ package java.util.concurrent; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.locks.Condition; -import java.util.concurrent.locks.ReentrantLock; import java.util.AbstractQueue; import java.util.Collection; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.Spliterator; import java.util.Spliterators; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReentrantLock; import java.util.function.Consumer; /** @@ -75,7 +75,7 @@ * * @since 1.5 * @author Doug Lea - * @param the type of elements held in this collection + * @param the type of elements held in this queue */ public class LinkedBlockingQueue extends AbstractQueue implements BlockingQueue, java.io.Serializable { @@ -117,7 +117,7 @@ */ /** - * Linked list node class + * Linked list node class. */ static class Node { E item; @@ -380,7 +380,7 @@ putLock.lockInterruptibly(); try { while (count.get() == capacity) { - if (nanos <= 0) + if (nanos <= 0L) return false; nanos = notFull.awaitNanos(nanos); } @@ -462,7 +462,7 @@ takeLock.lockInterruptibly(); try { while (count.get() == 0) { - if (nanos <= 0) + if (nanos <= 0L) return null; nanos = notEmpty.awaitNanos(nanos); } @@ -507,11 +507,7 @@ final ReentrantLock takeLock = this.takeLock; takeLock.lock(); try { - Node first = head.next; - if (first == null) - return null; - else - return first.item; + return (count.get() > 0) ? head.next.item : null; } finally { takeLock.unlock(); } @@ -630,7 +626,7 @@ * The following code can be used to dump the queue into a newly * allocated array of {@code String}: * - *
     {@code String[] y = x.toArray(new String[0]);}
    + *
     {@code String[] y = x.toArray(new String[0]);}
    * * Note that {@code toArray(new Object[0])} is identical in function to * {@code toArray()}. @@ -665,25 +661,7 @@ } public String toString() { - fullyLock(); - try { - Node p = head.next; - if (p == null) - return "[]"; - - StringBuilder sb = new StringBuilder(); - sb.append('['); - for (;;) { - E e = p.item; - sb.append(e == this ? "(this Collection)" : e); - p = p.next; - if (p == null) - return sb.append(']').toString(); - sb.append(',').append(' '); - } - } finally { - fullyUnlock(); - } + return Helpers.collectionToString(this); } /** @@ -801,34 +779,26 @@ return current != null; } - /** - * Returns the next live successor of p, or null if no such. - * - * Unlike other traversal methods, iterators need to handle both: - * - dequeued nodes (p.next == p) - * - (possibly multiple) interior removed nodes (p.item == null) - */ - private Node nextNode(Node p) { - for (;;) { - Node s = p.next; - if (s == p) - return head.next; - if (s == null || s.item != null) - return s; - p = s; - } - } - public E next() { fullyLock(); try { if (current == null) throw new NoSuchElementException(); - E x = currentElement; lastRet = current; - current = nextNode(current); - currentElement = (current == null) ? null : current.item; - return x; + E item = null; + // Unlike other traversal methods, iterators must handle both: + // - dequeued nodes (p.next == p) + // - (possibly multiple) interior removed nodes (p.item == null) + for (Node p = current, q;; p = q) { + if ((q = p.next) == p) + q = head.next; + if (q == null || (item = q.item) != null) { + current = q; + E x = currentElement; + currentElement = item; + return x; + } + } } finally { fullyUnlock(); } @@ -901,8 +871,9 @@ if (i > 0) { batch = i; return Spliterators.spliterator - (a, 0, i, Spliterator.ORDERED | Spliterator.NONNULL | - Spliterator.CONCURRENT); + (a, 0, i, (Spliterator.ORDERED | + Spliterator.NONNULL | + Spliterator.CONCURRENT)); } } return null; diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/LinkedTransferQueue.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/LinkedTransferQueue.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/LinkedTransferQueue.java Wed Jul 05 20:54:48 2017 +0200 @@ -36,14 +36,14 @@ package java.util.concurrent; import java.util.AbstractQueue; +import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.Queue; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.LockSupport; import java.util.Spliterator; import java.util.Spliterators; +import java.util.concurrent.locks.LockSupport; import java.util.function.Consumer; /** @@ -83,7 +83,7 @@ * * @since 1.7 * @author Doug Lea - * @param the type of elements held in this collection + * @param the type of elements held in this queue */ public class LinkedTransferQueue extends AbstractQueue implements TransferQueue, java.io.Serializable { @@ -108,7 +108,7 @@ * * A FIFO dual queue may be implemented using a variation of the * Michael & Scott (M&S) lock-free queue algorithm - * (http://www.cs.rochester.edu/u/scott/papers/1996_PODC_queues.pdf). + * (http://www.cs.rochester.edu/~scott/papers/1996_PODC_queues.pdf). * It maintains two pointer fields, "head", pointing to a * (matched) node that in turn points to the first actual * (unmatched) queue node (or null if empty); and "tail" that @@ -215,7 +215,7 @@ * of costly-to-reclaim garbage caused by the sequential "next" * links of nodes starting at old forgotten head nodes: As first * described in detail by Boehm - * (http://portal.acm.org/citation.cfm?doid=503272.503282) if a GC + * (http://portal.acm.org/citation.cfm?doid=503272.503282), if a GC * delays noticing that any arbitrarily old node has become * garbage, all newer dead nodes will also be unreclaimed. * (Similar issues arise in non-GC environments.) To cope with @@ -456,12 +456,12 @@ // CAS methods for fields final boolean casNext(Node cmp, Node val) { - return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val); + return U.compareAndSwapObject(this, NEXT, cmp, val); } final boolean casItem(Object cmp, Object val) { // assert cmp == null || cmp.getClass() != Node.class; - return UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val); + return U.compareAndSwapObject(this, ITEM, cmp, val); } /** @@ -469,7 +469,7 @@ * only be seen after publication via casNext. */ Node(Object item, boolean isData) { - UNSAFE.putObject(this, itemOffset, item); // relaxed write + U.putObject(this, ITEM, item); // relaxed write this.isData = isData; } @@ -478,7 +478,7 @@ * only after CASing head field, so uses relaxed write. */ final void forgetNext() { - UNSAFE.putObject(this, nextOffset, this); + U.putObject(this, NEXT, this); } /** @@ -491,8 +491,8 @@ * else we don't care). */ final void forgetContents() { - UNSAFE.putObject(this, itemOffset, this); - UNSAFE.putObject(this, waiterOffset, null); + U.putObject(this, ITEM, this); + U.putObject(this, WAITER, null); } /** @@ -538,21 +538,19 @@ private static final long serialVersionUID = -3375979862319811754L; // Unsafe mechanics - private static final sun.misc.Unsafe UNSAFE; - private static final long itemOffset; - private static final long nextOffset; - private static final long waiterOffset; + private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe(); + private static final long ITEM; + private static final long NEXT; + private static final long WAITER; static { try { - UNSAFE = sun.misc.Unsafe.getUnsafe(); - Class k = Node.class; - itemOffset = UNSAFE.objectFieldOffset - (k.getDeclaredField("item")); - nextOffset = UNSAFE.objectFieldOffset - (k.getDeclaredField("next")); - waiterOffset = UNSAFE.objectFieldOffset - (k.getDeclaredField("waiter")); - } catch (Exception e) { + ITEM = U.objectFieldOffset + (Node.class.getDeclaredField("item")); + NEXT = U.objectFieldOffset + (Node.class.getDeclaredField("next")); + WAITER = U.objectFieldOffset + (Node.class.getDeclaredField("waiter")); + } catch (ReflectiveOperationException e) { throw new Error(e); } } @@ -569,15 +567,15 @@ // CAS methods for fields private boolean casTail(Node cmp, Node val) { - return UNSAFE.compareAndSwapObject(this, tailOffset, cmp, val); + return U.compareAndSwapObject(this, TAIL, cmp, val); } private boolean casHead(Node cmp, Node val) { - return UNSAFE.compareAndSwapObject(this, headOffset, cmp, val); + return U.compareAndSwapObject(this, HEAD, cmp, val); } private boolean casSweepVotes(int cmp, int val) { - return UNSAFE.compareAndSwapInt(this, sweepVotesOffset, cmp, val); + return U.compareAndSwapInt(this, SWEEPVOTES, cmp, val); } /* @@ -588,12 +586,6 @@ private static final int SYNC = 2; // for transfer, take private static final int TIMED = 3; // for timed poll, tryTransfer - @SuppressWarnings("unchecked") - static E cast(Object item) { - // assert item == null || item.getClass() != Node.class; - return (E) item; - } - /** * Implements all queuing methods. See above for explanation. * @@ -630,7 +622,8 @@ break; // unless slack < 2 } LockSupport.unpark(p.waiter); - return LinkedTransferQueue.cast(item); + @SuppressWarnings("unchecked") E itemE = (E) item; + return itemE; } } Node n = p.next; @@ -708,15 +701,15 @@ if (item != e) { // matched // assert item != s; s.forgetContents(); // avoid garbage - return LinkedTransferQueue.cast(item); + @SuppressWarnings("unchecked") E itemE = (E) item; + return itemE; } - if ((w.isInterrupted() || (timed && nanos <= 0)) && - s.casItem(e, s)) { // cancel - unsplice(pred, s); - return e; + else if (w.isInterrupted() || (timed && nanos <= 0L)) { + unsplice(pred, s); // try to unlink and cancel + if (s.casItem(e, s)) // return normally if lost CAS + return e; } - - if (spins < 0) { // establish spins at/near front + else if (spins < 0) { // establish spins at/near front if ((spins = spinsFor(pred, s.isData)) > 0) randomYields = ThreadLocalRandom.current(); } @@ -768,52 +761,25 @@ } /** - * Returns the first unmatched node of the given mode, or null if - * none. Used by methods isEmpty, hasWaitingConsumer. - */ - private Node firstOfMode(boolean isData) { - for (Node p = head; p != null; p = succ(p)) { - if (!p.isMatched()) - return (p.isData == isData) ? p : null; - } - return null; - } - - /** - * Version of firstOfMode used by Spliterator. Callers must - * recheck if the returned node's item field is null or - * self-linked before using. + * Returns the first unmatched data node, or null if none. + * Callers must recheck if the returned node's item field is null + * or self-linked before using. */ final Node firstDataNode() { - for (Node p = head; p != null;) { - Object item = p.item; - if (p.isData) { - if (item != null && item != p) - return p; + restartFromHead: for (;;) { + for (Node p = head; p != null;) { + Object item = p.item; + if (p.isData) { + if (item != null && item != p) + return p; + } + else if (item == null) + break; + if (p == (p = p.next)) + continue restartFromHead; } - else if (item == null) - break; - if (p == (p = p.next)) - p = head; + return null; } - return null; - } - - /** - * Returns the item in the first unmatched node with isData; or - * null if none. Used by peek. - */ - private E firstDataItem() { - for (Node p = head; p != null; p = succ(p)) { - Object item = p.item; - if (p.isData) { - if (item != null && item != p) - return LinkedTransferQueue.cast(item); - } - else if (item == null) - return null; - } - return null; } /** @@ -821,23 +787,140 @@ * Used by methods size and getWaitingConsumerCount. */ private int countOfMode(boolean data) { - int count = 0; - for (Node p = head; p != null; ) { - if (!p.isMatched()) { - if (p.isData != data) - return 0; - if (++count == Integer.MAX_VALUE) // saturated + restartFromHead: for (;;) { + int count = 0; + for (Node p = head; p != null;) { + if (!p.isMatched()) { + if (p.isData != data) + return 0; + if (++count == Integer.MAX_VALUE) + break; // @see Collection.size() + } + if (p == (p = p.next)) + continue restartFromHead; + } + return count; + } + } + + public String toString() { + String[] a = null; + restartFromHead: for (;;) { + int charLength = 0; + int size = 0; + for (Node p = head; p != null;) { + Object item = p.item; + if (p.isData) { + if (item != null && item != p) { + if (a == null) + a = new String[4]; + else if (size == a.length) + a = Arrays.copyOf(a, 2 * size); + String s = item.toString(); + a[size++] = s; + charLength += s.length(); + } + } else if (item == null) break; + if (p == (p = p.next)) + continue restartFromHead; + } + + if (size == 0) + return "[]"; + + return Helpers.toString(a, size, charLength); + } + } + + private Object[] toArrayInternal(Object[] a) { + Object[] x = a; + restartFromHead: for (;;) { + int size = 0; + for (Node p = head; p != null;) { + Object item = p.item; + if (p.isData) { + if (item != null && item != p) { + if (x == null) + x = new Object[4]; + else if (size == x.length) + x = Arrays.copyOf(x, 2 * (size + 4)); + x[size++] = item; + } + } else if (item == null) + break; + if (p == (p = p.next)) + continue restartFromHead; } - Node n = p.next; - if (n != p) - p = n; - else { - count = 0; - p = head; + if (x == null) + return new Object[0]; + else if (a != null && size <= a.length) { + if (a != x) + System.arraycopy(x, 0, a, 0, size); + if (size < a.length) + a[size] = null; + return a; } + return (size == x.length) ? x : Arrays.copyOf(x, size); } - return count; + } + + /** + * Returns an array containing all of the elements in this queue, in + * proper sequence. + * + *

    The returned array will be "safe" in that no references to it are + * maintained by this queue. (In other words, this method must allocate + * a new array). The caller is thus free to modify the returned array. + * + *

    This method acts as bridge between array-based and collection-based + * APIs. + * + * @return an array containing all of the elements in this queue + */ + public Object[] toArray() { + return toArrayInternal(null); + } + + /** + * Returns an array containing all of the elements in this queue, in + * proper sequence; the runtime type of the returned array is that of + * the specified array. If the queue fits in the specified array, it + * is returned therein. Otherwise, a new array is allocated with the + * runtime type of the specified array and the size of this queue. + * + *

    If this queue fits in the specified array with room to spare + * (i.e., the array has more elements than this queue), the element in + * the array immediately following the end of the queue is set to + * {@code null}. + * + *

    Like the {@link #toArray()} method, this method acts as bridge between + * array-based and collection-based APIs. Further, this method allows + * precise control over the runtime type of the output array, and may, + * under certain circumstances, be used to save allocation costs. + * + *

    Suppose {@code x} is a queue known to contain only strings. + * The following code can be used to dump the queue into a newly + * allocated array of {@code String}: + * + *

     {@code String[] y = x.toArray(new String[0]);}
    + * + * Note that {@code toArray(new Object[0])} is identical in function to + * {@code toArray()}. + * + * @param a the array into which the elements of the queue are to + * be stored, if it is big enough; otherwise, a new array of the + * same runtime type is allocated for this purpose + * @return an array containing all of the elements in this queue + * @throws ArrayStoreException if the runtime type of the specified array + * is not a supertype of the runtime type of every element in + * this queue + * @throws NullPointerException if the specified array is null + */ + @SuppressWarnings("unchecked") + public T[] toArray(T[] a) { + if (a == null) throw new NullPointerException(); + return (T[]) toArrayInternal(a); } final class Itr implements Iterator { @@ -886,7 +969,8 @@ Object item = s.item; if (s.isData) { if (item != null && item != s) { - nextItem = LinkedTransferQueue.cast(item); + @SuppressWarnings("unchecked") E itemE = (E) item; + nextItem = itemE; nextNode = s; return; } @@ -934,23 +1018,19 @@ } /** A customized variant of Spliterators.IteratorSpliterator */ - static final class LTQSpliterator implements Spliterator { + final class LTQSpliterator implements Spliterator { static final int MAX_BATCH = 1 << 25; // max batch array size; - final LinkedTransferQueue queue; - Node current; // current node; null until initialized + Node current; // current node; null until initialized int batch; // batch size for splits boolean exhausted; // true when no more nodes - LTQSpliterator(LinkedTransferQueue queue) { - this.queue = queue; - } + LTQSpliterator() {} public Spliterator trySplit() { Node p; - final LinkedTransferQueue q = this.queue; int b = batch; int n = (b <= 0) ? 1 : (b >= MAX_BATCH) ? MAX_BATCH : b + 1; if (!exhausted && - ((p = current) != null || (p = q.firstDataNode()) != null) && + ((p = current) != null || (p = firstDataNode()) != null) && p.next != null) { Object[] a = new Object[n]; int i = 0; @@ -959,15 +1039,16 @@ if (e != p && (a[i] = e) != null) ++i; if (p == (p = p.next)) - p = q.firstDataNode(); + p = firstDataNode(); } while (p != null && i < n && p.isData); if ((current = p) == null) exhausted = true; if (i > 0) { batch = i; return Spliterators.spliterator - (a, 0, i, Spliterator.ORDERED | Spliterator.NONNULL | - Spliterator.CONCURRENT); + (a, 0, i, (Spliterator.ORDERED | + Spliterator.NONNULL | + Spliterator.CONCURRENT)); } } return null; @@ -977,16 +1058,15 @@ public void forEachRemaining(Consumer action) { Node p; if (action == null) throw new NullPointerException(); - final LinkedTransferQueue q = this.queue; if (!exhausted && - ((p = current) != null || (p = q.firstDataNode()) != null)) { + ((p = current) != null || (p = firstDataNode()) != null)) { exhausted = true; do { Object e = p.item; if (e != null && e != p) action.accept((E)e); if (p == (p = p.next)) - p = q.firstDataNode(); + p = firstDataNode(); } while (p != null && p.isData); } } @@ -995,15 +1075,14 @@ public boolean tryAdvance(Consumer action) { Node p; if (action == null) throw new NullPointerException(); - final LinkedTransferQueue q = this.queue; if (!exhausted && - ((p = current) != null || (p = q.firstDataNode()) != null)) { + ((p = current) != null || (p = firstDataNode()) != null)) { Object e; do { if ((e = p.item) == p) e = null; if (p == (p = p.next)) - p = q.firstDataNode(); + p = firstDataNode(); } while (e == null && p != null && p.isData); if ((current = p) == null) exhausted = true; @@ -1040,7 +1119,7 @@ * @since 1.8 */ public Spliterator spliterator() { - return new LTQSpliterator(this); + return new LTQSpliterator(); } /* -------------- Removal methods -------------- */ @@ -1054,7 +1133,7 @@ * @param s the node to be unspliced */ final void unsplice(Node pred, Node s) { - s.forgetContents(); // forget unneeded fields + s.waiter = null; // disable signals /* * See above for rationale. Briefly: if pred still points to * s, try to unlink s. If s cannot be unlinked, because it is @@ -1332,7 +1411,22 @@ } public E peek() { - return firstDataItem(); + restartFromHead: for (;;) { + for (Node p = head; p != null;) { + Object item = p.item; + if (p.isData) { + if (item != null && item != p) { + @SuppressWarnings("unchecked") E e = (E) item; + return e; + } + } + else if (item == null) + break; + if (p == (p = p.next)) + continue restartFromHead; + } + return null; + } } /** @@ -1341,15 +1435,24 @@ * @return {@code true} if this queue contains no elements */ public boolean isEmpty() { - for (Node p = head; p != null; p = succ(p)) { - if (!p.isMatched()) - return !p.isData; - } - return true; + return firstDataNode() == null; } public boolean hasWaitingConsumer() { - return firstOfMode(false) != null; + restartFromHead: for (;;) { + for (Node p = head; p != null;) { + Object item = p.item; + if (p.isData) { + if (item != null && item != p) + break; + } + else if (item == null) + return true; + if (p == (p = p.next)) + continue restartFromHead; + } + return false; + } } /** @@ -1396,15 +1499,16 @@ * @return {@code true} if this queue contains the specified element */ public boolean contains(Object o) { - if (o == null) return false; - for (Node p = head; p != null; p = succ(p)) { - Object item = p.item; - if (p.isData) { - if (item != null && item != p && o.equals(item)) - return true; + if (o != null) { + for (Node p = head; p != null; p = succ(p)) { + Object item = p.item; + if (p.isData) { + if (item != null && item != p && o.equals(item)) + return true; + } + else if (item == null) + break; } - else if (item == null) - break; } return false; } @@ -1460,22 +1564,24 @@ // Unsafe mechanics - private static final sun.misc.Unsafe UNSAFE; - private static final long headOffset; - private static final long tailOffset; - private static final long sweepVotesOffset; + private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe(); + private static final long HEAD; + private static final long TAIL; + private static final long SWEEPVOTES; static { try { - UNSAFE = sun.misc.Unsafe.getUnsafe(); - Class k = LinkedTransferQueue.class; - headOffset = UNSAFE.objectFieldOffset - (k.getDeclaredField("head")); - tailOffset = UNSAFE.objectFieldOffset - (k.getDeclaredField("tail")); - sweepVotesOffset = UNSAFE.objectFieldOffset - (k.getDeclaredField("sweepVotes")); - } catch (Exception e) { + HEAD = U.objectFieldOffset + (LinkedTransferQueue.class.getDeclaredField("head")); + TAIL = U.objectFieldOffset + (LinkedTransferQueue.class.getDeclaredField("tail")); + SWEEPVOTES = U.objectFieldOffset + (LinkedTransferQueue.class.getDeclaredField("sweepVotes")); + } catch (ReflectiveOperationException e) { throw new Error(e); } + + // Reduce the risk of rare disastrous classloading in first call to + // LockSupport.park: https://bugs.openjdk.java.net/browse/JDK-8074773 + Class ensureLoaded = LockSupport.class; } } diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/Phaser.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/Phaser.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/Phaser.java Wed Jul 05 20:54:48 2017 +0200 @@ -35,8 +35,6 @@ package java.util.concurrent; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.LockSupport; @@ -73,7 +71,7 @@ * *
      * - *
    • Arrival. Methods {@link #arrive} and + *
    • Arrival. Methods {@link #arrive} and * {@link #arriveAndDeregister} record arrival. These methods * do not block, but return an associated arrival phase * number; that is, the phase number of the phaser to which @@ -86,7 +84,7 @@ * flexible than, providing a barrier action to a {@code * CyclicBarrier}. * - *
    • Waiting. Method {@link #awaitAdvance} requires an + *
    • Waiting. Method {@link #awaitAdvance} requires an * argument indicating an arrival phase number, and returns when * the phaser advances to (or is already at) a different phase. * Unlike similar constructions using {@code CyclicBarrier}, @@ -97,9 +95,10 @@ * state of the phaser. If necessary, you can perform any * associated recovery within handlers of those exceptions, * often after invoking {@code forceTermination}. Phasers may - * also be used by tasks executing in a {@link ForkJoinPool}, - * which will ensure sufficient parallelism to execute tasks - * when others are blocked waiting for a phase to advance. + * also be used by tasks executing in a {@link ForkJoinPool}. + * Progress is ensured if the pool's parallelismLevel can + * accommodate the maximum number of simultaneously blocked + * parties. * *
    * @@ -155,7 +154,7 @@ * The typical idiom is for the method setting this up to first * register, then start the actions, then deregister, as in: * - *
     {@code
    + * 
     {@code
      * void runTasks(List tasks) {
      *   final Phaser phaser = new Phaser(1); // "1" to register self
      *   // create and start threads
    @@ -176,7 +175,7 @@
      * 

    One way to cause a set of threads to repeatedly perform actions * for a given number of iterations is to override {@code onAdvance}: * - *

     {@code
    + * 
     {@code
      * void startTasks(List tasks, final int iterations) {
      *   final Phaser phaser = new Phaser() {
      *     protected boolean onAdvance(int phase, int registeredParties) {
    @@ -200,7 +199,7 @@
      *
      * If the main task must later await termination, it
      * may re-register and then execute a similar loop:
    - *  
     {@code
    + * 
     {@code
      *   // ...
      *   phaser.register();
      *   while (!phaser.isTerminated())
    @@ -210,7 +209,7 @@
      * in contexts where you are sure that the phase will never wrap around
      * {@code Integer.MAX_VALUE}. For example:
      *
    - *  
     {@code
    + * 
     {@code
      * void awaitPhase(Phaser phaser, int phase) {
      *   int p = phaser.register(); // assumes caller not already registered
      *   while (p < phase) {
    @@ -230,7 +229,7 @@
      * new Phaser())}, these tasks could then be started, for example by
      * submitting to a pool:
      *
    - *  
     {@code
    + * 
     {@code
      * void build(Task[] tasks, int lo, int hi, Phaser ph) {
      *   if (hi - lo > TASKS_PER_PHASER) {
      *     for (int i = lo; i < hi; i += TASKS_PER_PHASER) {
    @@ -331,7 +330,7 @@
         }
     
         /**
    -     * The parent of this phaser, or null if none
    +     * The parent of this phaser, or null if none.
          */
         private final Phaser parent;
     
    @@ -389,7 +388,7 @@
                 int unarrived = (counts == EMPTY) ? 0 : (counts & UNARRIVED_MASK);
                 if (unarrived <= 0)
                     throw new IllegalStateException(badArrive(s));
    -            if (UNSAFE.compareAndSwapLong(this, stateOffset, s, s-=adjust)) {
    +            if (U.compareAndSwapLong(this, STATE, s, s-=adjust)) {
                     if (unarrived == 1) {
                         long n = s & PARTIES_MASK;  // base of next state
                         int nextUnarrived = (int)n >>> PARTIES_SHIFT;
    @@ -402,13 +401,12 @@
                                 n |= nextUnarrived;
                             int nextPhase = (phase + 1) & MAX_PHASE;
                             n |= (long)nextPhase << PHASE_SHIFT;
    -                        UNSAFE.compareAndSwapLong(this, stateOffset, s, n);
    +                        U.compareAndSwapLong(this, STATE, s, n);
                             releaseWaiters(phase);
                         }
                         else if (nextUnarrived == 0) { // propagate deregistration
                             phase = parent.doArrive(ONE_DEREGISTER);
    -                        UNSAFE.compareAndSwapLong(this, stateOffset,
    -                                                  s, s | EMPTY);
    +                        U.compareAndSwapLong(this, STATE, s, s | EMPTY);
                         }
                         else
                             phase = parent.doArrive(ONE_ARRIVAL);
    @@ -419,7 +417,7 @@
         }
     
         /**
    -     * Implementation of register, bulkRegister
    +     * Implementation of register, bulkRegister.
          *
          * @param registrations number to add to both parties and
          * unarrived fields. Must be greater than zero.
    @@ -443,14 +441,13 @@
                     if (parent == null || reconcileState() == s) {
                         if (unarrived == 0)             // wait out advance
                             root.internalAwaitAdvance(phase, null);
    -                    else if (UNSAFE.compareAndSwapLong(this, stateOffset,
    -                                                       s, s + adjust))
    +                    else if (U.compareAndSwapLong(this, STATE, s, s + adjust))
                             break;
                     }
                 }
                 else if (parent == null) {              // 1st root registration
                     long next = ((long)phase << PHASE_SHIFT) | adjust;
    -                if (UNSAFE.compareAndSwapLong(this, stateOffset, s, next))
    +                if (U.compareAndSwapLong(this, STATE, s, next))
                         break;
                 }
                 else {
    @@ -462,8 +459,8 @@
                             // finish registration whenever parent registration
                             // succeeded, even when racing with termination,
                             // since these are part of the same "transaction".
    -                        while (!UNSAFE.compareAndSwapLong
    -                               (this, stateOffset, s,
    +                        while (!U.compareAndSwapLong
    +                               (this, STATE, s,
                                     ((long)phase << PHASE_SHIFT) | adjust)) {
                                 s = state;
                                 phase = (int)(root.state >>> PHASE_SHIFT);
    @@ -494,8 +491,8 @@
                 // CAS to root phase with current parties, tripping unarrived
                 while ((phase = (int)(root.state >>> PHASE_SHIFT)) !=
                        (int)(s >>> PHASE_SHIFT) &&
    -                   !UNSAFE.compareAndSwapLong
    -                   (this, stateOffset, s,
    +                   !U.compareAndSwapLong
    +                   (this, STATE, s,
                         s = (((long)phase << PHASE_SHIFT) |
                              ((phase < 0) ? (s & COUNTS_MASK) :
                               (((p = (int)s >>> PARTIES_SHIFT) == 0) ? EMPTY :
    @@ -684,8 +681,7 @@
                 int unarrived = (counts == EMPTY) ? 0 : (counts & UNARRIVED_MASK);
                 if (unarrived <= 0)
                     throw new IllegalStateException(badArrive(s));
    -            if (UNSAFE.compareAndSwapLong(this, stateOffset, s,
    -                                          s -= ONE_ARRIVAL)) {
    +            if (U.compareAndSwapLong(this, STATE, s, s -= ONE_ARRIVAL)) {
                     if (unarrived > 1)
                         return root.internalAwaitAdvance(phase, null);
                     if (root != this)
    @@ -700,7 +696,7 @@
                         n |= nextUnarrived;
                     int nextPhase = (phase + 1) & MAX_PHASE;
                     n |= (long)nextPhase << PHASE_SHIFT;
    -                if (!UNSAFE.compareAndSwapLong(this, stateOffset, s, n))
    +                if (!U.compareAndSwapLong(this, STATE, s, n))
                         return (int)(state >>> PHASE_SHIFT); // terminated
                     releaseWaiters(phase);
                     return nextPhase;
    @@ -816,8 +812,7 @@
             final Phaser root = this.root;
             long s;
             while ((s = root.state) >= 0) {
    -            if (UNSAFE.compareAndSwapLong(root, stateOffset,
    -                                          s, s | TERMINATION_BIT)) {
    +            if (U.compareAndSwapLong(root, STATE, s, s | TERMINATION_BIT)) {
                     // signal all threads
                     releaseWaiters(0); // Waiters on evenQ
                     releaseWaiters(1); // Waiters on oddQ
    @@ -956,7 +951,7 @@
         }
     
         /**
    -     * Implementation of toString and string-based error messages
    +     * Implementation of toString and string-based error messages.
          */
         private String stateToString(long s) {
             return super.toString() +
    @@ -1065,7 +1060,7 @@
                 else {
                     try {
                         ForkJoinPool.managedBlock(node);
    -                } catch (InterruptedException ie) {
    +                } catch (InterruptedException cantHappen) {
                         node.wasInterrupted = true;
                     }
                 }
    @@ -1084,7 +1079,7 @@
         }
     
         /**
    -     * Wait nodes for Treiber stack representing wait queue
    +     * Wait nodes for Treiber stack representing wait queue.
          */
         static final class QNode implements ForkJoinPool.ManagedBlocker {
             final Phaser phaser;
    @@ -1121,41 +1116,39 @@
                     thread = null;
                     return true;
                 }
    -            if (timed) {
    -                if (nanos > 0L) {
    -                    nanos = deadline - System.nanoTime();
    -                }
    -                if (nanos <= 0L) {
    -                    thread = null;
    -                    return true;
    -                }
    +            if (timed &&
    +                (nanos <= 0L || (nanos = deadline - System.nanoTime()) <= 0L)) {
    +                thread = null;
    +                return true;
                 }
                 return false;
             }
     
             public boolean block() {
    -            if (isReleasable())
    -                return true;
    -            else if (!timed)
    -                LockSupport.park(this);
    -            else if (nanos > 0L)
    -                LockSupport.parkNanos(this, nanos);
    -            return isReleasable();
    +            while (!isReleasable()) {
    +                if (timed)
    +                    LockSupport.parkNanos(this, nanos);
    +                else
    +                    LockSupport.park(this);
    +            }
    +            return true;
             }
         }
     
         // Unsafe mechanics
     
    -    private static final sun.misc.Unsafe UNSAFE;
    -    private static final long stateOffset;
    +    private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +    private static final long STATE;
         static {
             try {
    -            UNSAFE = sun.misc.Unsafe.getUnsafe();
    -            Class k = Phaser.class;
    -            stateOffset = UNSAFE.objectFieldOffset
    -                (k.getDeclaredField("state"));
    -        } catch (Exception e) {
    +            STATE = U.objectFieldOffset
    +                (Phaser.class.getDeclaredField("state"));
    +        } catch (ReflectiveOperationException e) {
                 throw new Error(e);
             }
    +
    +        // Reduce the risk of rare disastrous classloading in first call to
    +        // LockSupport.park: https://bugs.openjdk.java.net/browse/JDK-8074773
    +        Class ensureLoaded = LockSupport.class;
         }
     }
    diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/PriorityBlockingQueue.java
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/PriorityBlockingQueue.java	Wed Oct 21 15:15:26 2015 -0700
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/PriorityBlockingQueue.java	Wed Jul 05 20:54:48 2017 +0200
    @@ -35,8 +35,6 @@
     
     package java.util.concurrent;
     
    -import java.util.concurrent.locks.Condition;
    -import java.util.concurrent.locks.ReentrantLock;
     import java.util.AbstractQueue;
     import java.util.Arrays;
     import java.util.Collection;
    @@ -47,6 +45,8 @@
     import java.util.Queue;
     import java.util.SortedSet;
     import java.util.Spliterator;
    +import java.util.concurrent.locks.Condition;
    +import java.util.concurrent.locks.ReentrantLock;
     import java.util.function.Consumer;
     
     /**
    @@ -78,7 +78,7 @@
      * tie-breaking to comparable elements. To use it, you would insert a
      * {@code new FIFOEntry(anEntry)} instead of a plain entry object.
      *
    - *  
     {@code
    + * 
     {@code
      * class FIFOEntry>
      *     implements Comparable> {
      *   static final AtomicLong seq = new AtomicLong(0);
    @@ -103,7 +103,7 @@
      *
      * @since 1.5
      * @author Doug Lea
    - * @param  the type of elements held in this collection
    + * @param  the type of elements held in this queue
      */
     @SuppressWarnings("unchecked")
     public class PriorityBlockingQueue extends AbstractQueue
    @@ -161,12 +161,12 @@
         private transient Comparator comparator;
     
         /**
    -     * Lock used for all public operations
    +     * Lock used for all public operations.
          */
         private final ReentrantLock lock;
     
         /**
    -     * Condition for blocking when empty
    +     * Condition for blocking when empty.
          */
         private final Condition notEmpty;
     
    @@ -289,8 +289,7 @@
             lock.unlock(); // must release and then re-acquire main lock
             Object[] newArray = null;
             if (allocationSpinLock == 0 &&
    -            UNSAFE.compareAndSwapInt(this, allocationSpinLockOffset,
    -                                     0, 1)) {
    +            U.compareAndSwapInt(this, ALLOCATIONSPINLOCK, 0, 1)) {
                 try {
                     int newCap = oldCap + ((oldCap < 64) ?
                                            (oldCap + 2) : // grow faster if small
    @@ -672,7 +671,7 @@
         }
     
         /**
    -     * Identity-based version for use in Itr.remove
    +     * Identity-based version for use in Itr.remove.
          */
         void removeEQ(Object o) {
             final ReentrantLock lock = this.lock;
    @@ -708,48 +707,8 @@
             }
         }
     
    -    /**
    -     * Returns an array containing all of the elements in this queue.
    -     * The returned array elements are in no particular order.
    -     *
    -     * 

    The returned array will be "safe" in that no references to it are - * maintained by this queue. (In other words, this method must allocate - * a new array). The caller is thus free to modify the returned array. - * - *

    This method acts as bridge between array-based and collection-based - * APIs. - * - * @return an array containing all of the elements in this queue - */ - public Object[] toArray() { - final ReentrantLock lock = this.lock; - lock.lock(); - try { - return Arrays.copyOf(queue, size); - } finally { - lock.unlock(); - } - } - public String toString() { - final ReentrantLock lock = this.lock; - lock.lock(); - try { - int n = size; - if (n == 0) - return "[]"; - StringBuilder sb = new StringBuilder(); - sb.append('['); - for (int i = 0; i < n; ++i) { - Object e = queue[i]; - sb.append(e == this ? "(this Collection)" : e); - if (i != n - 1) - sb.append(',').append(' '); - } - return sb.append(']').toString(); - } finally { - lock.unlock(); - } + return Helpers.collectionToString(this); } /** @@ -808,6 +767,29 @@ } /** + * Returns an array containing all of the elements in this queue. + * The returned array elements are in no particular order. + * + *

    The returned array will be "safe" in that no references to it are + * maintained by this queue. (In other words, this method must allocate + * a new array). The caller is thus free to modify the returned array. + * + *

    This method acts as bridge between array-based and collection-based + * APIs. + * + * @return an array containing all of the elements in this queue + */ + public Object[] toArray() { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + return Arrays.copyOf(queue, size); + } finally { + lock.unlock(); + } + } + + /** * Returns an array containing all of the elements in this queue; the * runtime type of the returned array is that of the specified array. * The returned array elements are in no particular order. @@ -829,7 +811,7 @@ * The following code can be used to dump the queue into a newly * allocated array of {@code String}: * - *

     {@code String[] y = x.toArray(new String[0]);}
    + *
     {@code String[] y = x.toArray(new String[0]);}
    * * Note that {@code toArray(new Object[0])} is identical in function to * {@code toArray()}. @@ -971,7 +953,7 @@ return hi; } - public Spliterator trySplit() { + public PBQSpliterator trySplit() { int hi = getFence(), lo = index, mid = (lo + hi) >>> 1; return (lo >= mid) ? null : new PBQSpliterator(queue, array, lo, index = mid); @@ -1028,15 +1010,13 @@ } // Unsafe mechanics - private static final sun.misc.Unsafe UNSAFE; - private static final long allocationSpinLockOffset; + private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe(); + private static final long ALLOCATIONSPINLOCK; static { try { - UNSAFE = sun.misc.Unsafe.getUnsafe(); - Class k = PriorityBlockingQueue.class; - allocationSpinLockOffset = UNSAFE.objectFieldOffset - (k.getDeclaredField("allocationSpinLock")); - } catch (Exception e) { + ALLOCATIONSPINLOCK = U.objectFieldOffset + (PriorityBlockingQueue.class.getDeclaredField("allocationSpinLock")); + } catch (ReflectiveOperationException e) { throw new Error(e); } } diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/RecursiveAction.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/RecursiveAction.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/RecursiveAction.java Wed Jul 05 20:54:48 2017 +0200 @@ -45,7 +45,7 @@ *

    Sample Usages. Here is a simple but complete ForkJoin * sort that sorts a given {@code long[]} array: * - *

     {@code
    + * 
     {@code
      * static class SortTask extends RecursiveAction {
      *   final long[] array; final int lo, hi;
      *   SortTask(long[] array, int lo, int hi) {
    @@ -79,7 +79,7 @@
      * SortTask(anArray)} and invoking it in a ForkJoinPool.  As a more
      * concrete simple example, the following task increments each element
      * of an array:
    - *  
     {@code
    + * 
     {@code
      * class IncrementTask extends RecursiveAction {
      *   final long[] array; final int lo, hi;
      *   IncrementTask(long[] array, int lo, int hi) {
    @@ -110,7 +110,7 @@
      * performing leaf actions on unstolen tasks rather than further
      * subdividing.
      *
    - *  
     {@code
    + * 
     {@code
      * double sumOfSquares(ForkJoinPool pool, double[] array) {
      *   int n = array.length;
      *   Applyer a = new Applyer(array, 0, n, null);
    diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/RecursiveTask.java
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/RecursiveTask.java	Wed Oct 21 15:15:26 2015 -0700
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/RecursiveTask.java	Wed Jul 05 20:54:48 2017 +0200
    @@ -40,11 +40,11 @@
      *
      * 

    For a classic example, here is a task computing Fibonacci numbers: * - *

     {@code
    + * 
     {@code
      * class Fibonacci extends RecursiveTask {
      *   final int n;
      *   Fibonacci(int n) { this.n = n; }
    - *   Integer compute() {
    + *   protected Integer compute() {
      *     if (n <= 1)
      *       return n;
      *     Fibonacci f1 = new Fibonacci(n - 1);
    diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/ScheduledExecutorService.java
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/ScheduledExecutorService.java	Wed Oct 21 15:15:26 2015 -0700
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ScheduledExecutorService.java	Wed Jul 05 20:54:48 2017 +0200
    @@ -70,7 +70,7 @@
      * Here is a class with a method that sets up a ScheduledExecutorService
      * to beep every ten seconds for an hour:
      *
    - *  
     {@code
    + * 
     {@code
      * import static java.util.concurrent.TimeUnit.*;
      * class BeeperControl {
      *   private final ScheduledExecutorService scheduler =
    @@ -129,23 +129,37 @@
         /**
          * Creates and executes a periodic action that becomes enabled first
          * after the given initial delay, and subsequently with the given
    -     * period; that is executions will commence after
    -     * {@code initialDelay} then {@code initialDelay+period}, then
    +     * period; that is, executions will commence after
    +     * {@code initialDelay}, then {@code initialDelay + period}, then
          * {@code initialDelay + 2 * period}, and so on.
    -     * If any execution of the task
    -     * encounters an exception, subsequent executions are suppressed.
    -     * Otherwise, the task will only terminate via cancellation or
    -     * termination of the executor.  If any execution of this task
    -     * takes longer than its period, then subsequent executions
    -     * may start late, but will not concurrently execute.
    +     *
    +     * 

    The sequence of task executions continues indefinitely until + * one of the following exceptional completions occur: + *

      + *
    • The task is {@linkplain Future#cancel explicitly cancelled} + * via the returned future. + *
    • The executor terminates, also resulting in task cancellation. + *
    • An execution of the task throws an exception. In this case + * calling {@link Future#get() get} on the returned future will + * throw {@link ExecutionException}. + *
    + * Subsequent executions are suppressed. Subsequent calls to + * {@link Future#isDone isDone()} on the returned future will + * return {@code true}. + * + *

    If any execution of this task takes longer than its period, then + * subsequent executions may start late, but will not concurrently + * execute. * * @param command the task to execute * @param initialDelay the time to delay first execution * @param period the period between successive executions * @param unit the time unit of the initialDelay and period parameters * @return a ScheduledFuture representing pending completion of - * the task, and whose {@code get()} method will throw an - * exception upon cancellation + * the series of repeated tasks. The future's {@link + * Future#get() get()} method will never return normally, + * and will throw an exception upon task cancellation or + * abnormal termination of a task execution. * @throws RejectedExecutionException if the task cannot be * scheduled for execution * @throws NullPointerException if command is null @@ -160,10 +174,21 @@ * Creates and executes a periodic action that becomes enabled first * after the given initial delay, and subsequently with the * given delay between the termination of one execution and the - * commencement of the next. If any execution of the task - * encounters an exception, subsequent executions are suppressed. - * Otherwise, the task will only terminate via cancellation or - * termination of the executor. + * commencement of the next. + * + *

    The sequence of task executions continues indefinitely until + * one of the following exceptional completions occur: + *

      + *
    • The task is {@linkplain Future#cancel explicitly cancelled} + * via the returned future. + *
    • The executor terminates, also resulting in task cancellation. + *
    • An execution of the task throws an exception. In this case + * calling {@link Future#get() get} on the returned future will + * throw {@link ExecutionException}. + *
    + * Subsequent executions are suppressed. Subsequent calls to + * {@link Future#isDone isDone()} on the returned future will + * return {@code true}. * * @param command the task to execute * @param initialDelay the time to delay first execution @@ -171,8 +196,10 @@ * execution and the commencement of the next * @param unit the time unit of the initialDelay and delay parameters * @return a ScheduledFuture representing pending completion of - * the task, and whose {@code get()} method will throw an - * exception upon cancellation + * the series of repeated tasks. The future's {@link + * Future#get() get()} method will never return normally, + * and will throw an exception upon task cancellation or + * abnormal termination of a task execution. * @throws RejectedExecutionException if the task cannot be * scheduled for execution * @throws NullPointerException if command is null diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/ScheduledThreadPoolExecutor.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/ScheduledThreadPoolExecutor.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ScheduledThreadPoolExecutor.java Wed Jul 05 20:54:48 2017 +0200 @@ -34,19 +34,27 @@ */ package java.util.concurrent; + +import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.NANOSECONDS; + +import java.util.AbstractQueue; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; -import java.util.*; /** * A {@link ThreadPoolExecutor} that can additionally schedule - * commands to run after a given delay, or to execute - * periodically. This class is preferable to {@link java.util.Timer} - * when multiple worker threads are needed, or when the additional - * flexibility or capabilities of {@link ThreadPoolExecutor} (which - * this class extends) are required. + * commands to run after a given delay, or to execute periodically. + * This class is preferable to {@link java.util.Timer} when multiple + * worker threads are needed, or when the additional flexibility or + * capabilities of {@link ThreadPoolExecutor} (which this class + * extends) are required. * *

    Delayed tasks execute no sooner than they are enabled, but * without any real-time guarantees about when, after they are @@ -55,20 +63,19 @@ * submission. * *

    When a submitted task is cancelled before it is run, execution - * is suppressed. By default, such a cancelled task is not - * automatically removed from the work queue until its delay - * elapses. While this enables further inspection and monitoring, it - * may also cause unbounded retention of cancelled tasks. To avoid - * this, set {@link #setRemoveOnCancelPolicy} to {@code true}, which - * causes tasks to be immediately removed from the work queue at - * time of cancellation. + * is suppressed. By default, such a cancelled task is not + * automatically removed from the work queue until its delay elapses. + * While this enables further inspection and monitoring, it may also + * cause unbounded retention of cancelled tasks. To avoid this, use + * {@link #setRemoveOnCancelPolicy} to cause tasks to be immediately + * removed from the work queue at time of cancellation. * - *

    Successive executions of a task scheduled via - * {@code scheduleAtFixedRate} or - * {@code scheduleWithFixedDelay} do not overlap. While different - * executions may be performed by different threads, the effects of - * prior executions happen-before + *

    Successive executions of a periodic task scheduled via + * {@link #scheduleAtFixedRate scheduleAtFixedRate} or + * {@link #scheduleWithFixedDelay scheduleWithFixedDelay} + * do not overlap. While different executions may be performed by + * different threads, the effects of prior executions + * happen-before * those of subsequent ones. * *

    While this class inherits from {@link ThreadPoolExecutor}, a few @@ -98,7 +105,7 @@ * {@link FutureTask}. However, this may be modified or replaced using * subclasses of the form: * - *

     {@code
    + * 
     {@code
      * public class CustomScheduledExecutor extends ScheduledThreadPoolExecutor {
      *
      *   static class CustomTask implements RunnableScheduledFuture { ... }
    @@ -160,9 +167,9 @@
         private volatile boolean executeExistingDelayedTasksAfterShutdown = true;
     
         /**
    -     * True if ScheduledFutureTask.cancel should remove from queue
    +     * True if ScheduledFutureTask.cancel should remove from queue.
          */
    -    private volatile boolean removeOnCancel = false;
    +    volatile boolean removeOnCancel;
     
         /**
          * Sequence number to break scheduling ties, and in turn to
    @@ -173,7 +180,7 @@
         /**
          * Returns current nanosecond time.
          */
    -    final long now() {
    +    static final long now() {
             return System.nanoTime();
         }
     
    @@ -184,13 +191,13 @@
             private final long sequenceNumber;
     
             /** The time the task is enabled to execute in nanoTime units */
    -        private long time;
    +        private volatile long time;
     
             /**
    -         * Period in nanoseconds for repeating tasks.  A positive
    -         * value indicates fixed-rate execution.  A negative value
    -         * indicates fixed-delay execution.  A value of 0 indicates a
    -         * non-repeating task.
    +         * Period in nanoseconds for repeating tasks.
    +         * A positive value indicates fixed-rate execution.
    +         * A negative value indicates fixed-delay execution.
    +         * A value of 0 indicates a non-repeating (one-shot) task.
              */
             private final long period;
     
    @@ -205,31 +212,35 @@
             /**
              * Creates a one-shot action with given nanoTime-based trigger time.
              */
    -        ScheduledFutureTask(Runnable r, V result, long ns) {
    +        ScheduledFutureTask(Runnable r, V result, long triggerTime,
    +                            long sequenceNumber) {
                 super(r, result);
    -            this.time = ns;
    +            this.time = triggerTime;
                 this.period = 0;
    -            this.sequenceNumber = sequencer.getAndIncrement();
    +            this.sequenceNumber = sequenceNumber;
             }
     
             /**
    -         * Creates a periodic action with given nano time and period.
    +         * Creates a periodic action with given nanoTime-based initial
    +         * trigger time and period.
              */
    -        ScheduledFutureTask(Runnable r, V result, long ns, long period) {
    +        ScheduledFutureTask(Runnable r, V result, long triggerTime,
    +                            long period, long sequenceNumber) {
                 super(r, result);
    -            this.time = ns;
    +            this.time = triggerTime;
                 this.period = period;
    -            this.sequenceNumber = sequencer.getAndIncrement();
    +            this.sequenceNumber = sequenceNumber;
             }
     
             /**
              * Creates a one-shot action with given nanoTime-based trigger time.
              */
    -        ScheduledFutureTask(Callable callable, long ns) {
    +        ScheduledFutureTask(Callable callable, long triggerTime,
    +                            long sequenceNumber) {
                 super(callable);
    -            this.time = ns;
    +            this.time = triggerTime;
                 this.period = 0;
    -            this.sequenceNumber = sequencer.getAndIncrement();
    +            this.sequenceNumber = sequenceNumber;
             }
     
             public long getDelay(TimeUnit unit) {
    @@ -290,8 +301,8 @@
                 if (!canRunInCurrentRunState(periodic))
                     cancel(false);
                 else if (!periodic)
    -                ScheduledFutureTask.super.run();
    -            else if (ScheduledFutureTask.super.runAndReset()) {
    +                super.run();
    +            else if (super.runAndReset()) {
                     setNextRunTime();
                     reExecutePeriodic(outerTask);
                 }
    @@ -419,6 +430,22 @@
         }
     
         /**
    +     * The default keep-alive time for pool threads.
    +     *
    +     * Normally, this value is unused because all pool threads will be
    +     * core threads, but if a user creates a pool with a corePoolSize
    +     * of zero (against our advice), we keep a thread alive as long as
    +     * there are queued tasks.  If the keep alive time is zero (the
    +     * historic value), we end up hot-spinning in getTask, wasting a
    +     * CPU.  But on the other hand, if we set the value too high, and
    +     * users create a one-shot pool which they don't cleanly shutdown,
    +     * the pool's non-daemon threads will prevent JVM termination.  A
    +     * small but non-zero value (relative to a JVM's lifetime) seems
    +     * best.
    +     */
    +    private static final long DEFAULT_KEEPALIVE_MILLIS = 10L;
    +
    +    /**
          * Creates a new {@code ScheduledThreadPoolExecutor} with the
          * given core pool size.
          *
    @@ -427,7 +454,8 @@
          * @throws IllegalArgumentException if {@code corePoolSize < 0}
          */
         public ScheduledThreadPoolExecutor(int corePoolSize) {
    -        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
    +        super(corePoolSize, Integer.MAX_VALUE,
    +              DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
                   new DelayedWorkQueue());
         }
     
    @@ -444,13 +472,14 @@
          */
         public ScheduledThreadPoolExecutor(int corePoolSize,
                                            ThreadFactory threadFactory) {
    -        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
    +        super(corePoolSize, Integer.MAX_VALUE,
    +              DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
                   new DelayedWorkQueue(), threadFactory);
         }
     
         /**
    -     * Creates a new ScheduledThreadPoolExecutor with the given
    -     * initial parameters.
    +     * Creates a new {@code ScheduledThreadPoolExecutor} with the
    +     * given initial parameters.
          *
          * @param corePoolSize the number of threads to keep in the pool, even
          *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
    @@ -461,13 +490,14 @@
          */
         public ScheduledThreadPoolExecutor(int corePoolSize,
                                            RejectedExecutionHandler handler) {
    -        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
    +        super(corePoolSize, Integer.MAX_VALUE,
    +              DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
                   new DelayedWorkQueue(), handler);
         }
     
         /**
    -     * Creates a new ScheduledThreadPoolExecutor with the given
    -     * initial parameters.
    +     * Creates a new {@code ScheduledThreadPoolExecutor} with the
    +     * given initial parameters.
          *
          * @param corePoolSize the number of threads to keep in the pool, even
          *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
    @@ -482,19 +512,20 @@
         public ScheduledThreadPoolExecutor(int corePoolSize,
                                            ThreadFactory threadFactory,
                                            RejectedExecutionHandler handler) {
    -        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
    +        super(corePoolSize, Integer.MAX_VALUE,
    +              DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
                   new DelayedWorkQueue(), threadFactory, handler);
         }
     
         /**
    -     * Returns the trigger time of a delayed action.
    +     * Returns the nanoTime-based trigger time of a delayed action.
          */
         private long triggerTime(long delay, TimeUnit unit) {
             return triggerTime(unit.toNanos((delay < 0) ? 0 : delay));
         }
     
         /**
    -     * Returns the trigger time of a delayed action.
    +     * Returns the nanoTime-based trigger time of a delayed action.
          */
         long triggerTime(long delay) {
             return now() +
    @@ -527,9 +558,10 @@
                                            TimeUnit unit) {
             if (command == null || unit == null)
                 throw new NullPointerException();
    -        RunnableScheduledFuture t = decorateTask(command,
    +        RunnableScheduledFuture t = decorateTask(command,
                 new ScheduledFutureTask(command, null,
    -                                          triggerTime(delay, unit)));
    +                                          triggerTime(delay, unit),
    +                                          sequencer.getAndIncrement()));
             delayedExecute(t);
             return t;
         }
    @@ -545,7 +577,8 @@
                 throw new NullPointerException();
             RunnableScheduledFuture t = decorateTask(callable,
                 new ScheduledFutureTask(callable,
    -                                       triggerTime(delay, unit)));
    +                                       triggerTime(delay, unit),
    +                                       sequencer.getAndIncrement()));
             delayedExecute(t);
             return t;
         }
    @@ -561,13 +594,14 @@
                                                       TimeUnit unit) {
             if (command == null || unit == null)
                 throw new NullPointerException();
    -        if (period <= 0)
    +        if (period <= 0L)
                 throw new IllegalArgumentException();
             ScheduledFutureTask sft =
                 new ScheduledFutureTask(command,
                                               null,
                                               triggerTime(initialDelay, unit),
    -                                          unit.toNanos(period));
    +                                          unit.toNanos(period),
    +                                          sequencer.getAndIncrement());
             RunnableScheduledFuture t = decorateTask(command, sft);
             sft.outerTask = t;
             delayedExecute(t);
    @@ -585,13 +619,14 @@
                                                          TimeUnit unit) {
             if (command == null || unit == null)
                 throw new NullPointerException();
    -        if (delay <= 0)
    +        if (delay <= 0L)
                 throw new IllegalArgumentException();
             ScheduledFutureTask sft =
                 new ScheduledFutureTask(command,
                                               null,
                                               triggerTime(initialDelay, unit),
    -                                          unit.toNanos(-delay));
    +                                          -unit.toNanos(delay),
    +                                          sequencer.getAndIncrement());
             RunnableScheduledFuture t = decorateTask(command, sft);
             sft.outerTask = t;
             delayedExecute(t);
    @@ -764,7 +799,8 @@
         /**
          * Attempts to stop all actively executing tasks, halts the
          * processing of waiting tasks, and returns a list of the tasks
    -     * that were awaiting execution.
    +     * that were awaiting execution. These tasks are drained (removed)
    +     * from the task queue upon return from this method.
          *
          * 

    This method does not wait for actively executing tasks to * terminate. Use {@link #awaitTermination awaitTermination} to @@ -772,13 +808,15 @@ * *

    There are no guarantees beyond best-effort attempts to stop * processing actively executing tasks. This implementation - * cancels tasks via {@link Thread#interrupt}, so any task that + * interrupts tasks via {@link Thread#interrupt}; any task that * fails to respond to interrupts may never terminate. * * @return list of tasks that never commenced execution. - * Each element of this list is a {@link ScheduledFuture}, - * including those tasks submitted using {@code execute}, - * which are for scheduling purposes used as the basis of a + * Each element of this list is a {@link ScheduledFuture}. + * For tasks submitted via one of the {@code schedule} + * methods, the element will be identical to the returned + * {@code ScheduledFuture}. For tasks submitted using + * {@link #execute execute}, the element will be a * zero-delay {@code ScheduledFuture}. * @throws SecurityException {@inheritDoc} */ @@ -787,13 +825,19 @@ } /** - * Returns the task queue used by this executor. Each element of - * this queue is a {@link ScheduledFuture}, including those - * tasks submitted using {@code execute} which are for scheduling - * purposes used as the basis of a zero-delay - * {@code ScheduledFuture}. Iteration over this queue is - * not guaranteed to traverse tasks in the order in - * which they will execute. + * Returns the task queue used by this executor. Access to the + * task queue is intended primarily for debugging and monitoring. + * This queue may be in active use. Retrieving the task queue + * does not prevent queued tasks from executing. + * + *

    Each element of this queue is a {@link ScheduledFuture}. + * For tasks submitted via one of the {@code schedule} methods, the + * element will be identical to the returned {@code ScheduledFuture}. + * For tasks submitted using {@link #execute execute}, the element + * will be a zero-delay {@code ScheduledFuture}. + * + *

    Iteration over this queue is not guaranteed to traverse + * tasks in the order in which they will execute. * * @return the task queue */ @@ -836,7 +880,7 @@ private RunnableScheduledFuture[] queue = new RunnableScheduledFuture[INITIAL_CAPACITY]; private final ReentrantLock lock = new ReentrantLock(); - private int size = 0; + private int size; /** * Thread designated to wait for the task at the head of the @@ -854,7 +898,7 @@ * signalled. So waiting threads must be prepared to acquire * and lose leadership while waiting. */ - private Thread leader = null; + private Thread leader; /** * Condition signalled when a newer task becomes available at the @@ -1062,10 +1106,9 @@ lock.lock(); try { RunnableScheduledFuture first = queue[0]; - if (first == null || first.getDelay(NANOSECONDS) > 0) - return null; - else - return finishPoll(first); + return (first == null || first.getDelay(NANOSECONDS) > 0) + ? null + : finishPoll(first); } finally { lock.unlock(); } @@ -1081,7 +1124,7 @@ available.await(); else { long delay = first.getDelay(NANOSECONDS); - if (delay <= 0) + if (delay <= 0L) return finishPoll(first); first = null; // don't retain ref while waiting if (leader != null) @@ -1114,15 +1157,15 @@ for (;;) { RunnableScheduledFuture first = queue[0]; if (first == null) { - if (nanos <= 0) + if (nanos <= 0L) return null; else nanos = available.awaitNanos(nanos); } else { long delay = first.getDelay(NANOSECONDS); - if (delay <= 0) + if (delay <= 0L) return finishPoll(first); - if (nanos <= 0) + if (nanos <= 0L) return null; first = null; // don't retain ref while waiting if (nanos < delay || leader != null) @@ -1254,8 +1297,8 @@ */ private class Itr implements Iterator { final RunnableScheduledFuture[] array; - int cursor = 0; // index of next element to return - int lastRet = -1; // index of last element, or -1 if no such + int cursor; // index of next element to return; initially 0 + int lastRet = -1; // index of last element returned; -1 if no such Itr(RunnableScheduledFuture[] array) { this.array = array; diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/Semaphore.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/Semaphore.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/Semaphore.java Wed Jul 05 20:54:48 2017 +0200 @@ -34,6 +34,7 @@ */ package java.util.concurrent; + import java.util.Collection; import java.util.concurrent.locks.AbstractQueuedSynchronizer; @@ -48,7 +49,7 @@ *

    Semaphores are often used to restrict the number of threads than can * access some (physical or logical) resource. For example, here is * a class that uses a semaphore to control access to a pool of items: - *

     {@code
    + * 
     {@code
      * class Pool {
      *   private static final int MAX_AVAILABLE = 100;
      *   private final Semaphore available = new Semaphore(MAX_AVAILABLE, true);
    @@ -114,7 +115,7 @@
      * ownership).  This can be useful in some specialized contexts, such
      * as deadlock recovery.
      *
    - * 

    The constructor for this class optionally accepts a + *

    The constructor for this class optionally accepts a * fairness parameter. When set false, this class makes no * guarantees about the order in which threads acquire permits. In * particular, barging is permitted, that is, a thread @@ -141,8 +142,13 @@ * *

    This class also provides convenience methods to {@link * #acquire(int) acquire} and {@link #release(int) release} multiple - * permits at a time. Beware of the increased risk of indefinite - * postponement when these methods are used without fairness set true. + * permits at a time. These methods are generally more efficient and + * effective than loops. However, they do not establish any preference + * order. For example, if thread A invokes {@code s.acquire(3}) and + * thread B invokes {@code s.acquire(2)}, and two permits become + * available, then there is no guarantee that thread B will obtain + * them unless its acquire came first and Semaphore {@code s} is in + * fair mode. * *

    Memory consistency effects: Actions in a thread prior to calling * a "release" method such as {@code release()} @@ -433,14 +439,16 @@ * *

    Acquires the given number of permits, if they are available, * and returns immediately, reducing the number of available permits - * by the given amount. + * by the given amount. This method has the same effect as the + * loop {@code for (int i = 0; i < permits; ++i) acquire();} except + * that it atomically acquires the permits all at once: * *

    If insufficient permits are available then the current thread becomes * disabled for thread scheduling purposes and lies dormant until * one of two things happens: *

      *
    • Some other thread invokes one of the {@link #release() release} - * methods for this semaphore, the current thread is next to be assigned + * methods for this semaphore and the current thread is next to be assigned * permits and the number of available permits satisfies this request; or *
    • Some other thread {@linkplain Thread#interrupt interrupts} * the current thread. @@ -473,12 +481,14 @@ * *

      Acquires the given number of permits, if they are available, * and returns immediately, reducing the number of available permits - * by the given amount. + * by the given amount. This method has the same effect as the + * loop {@code for (int i = 0; i < permits; ++i) acquireUninterruptibly();} + * except that it atomically acquires the permits all at once: * *

      If insufficient permits are available then the current thread becomes * disabled for thread scheduling purposes and lies dormant until * some other thread invokes one of the {@link #release() release} - * methods for this semaphore, the current thread is next to be assigned + * methods for this semaphore and the current thread is next to be assigned * permits and the number of available permits satisfies this request. * *

      If the current thread is {@linkplain Thread#interrupt interrupted} @@ -540,7 +550,7 @@ * purposes and lies dormant until one of three things happens: *

        *
      • Some other thread invokes one of the {@link #release() release} - * methods for this semaphore, the current thread is next to be assigned + * methods for this semaphore and the current thread is next to be assigned * permits and the number of available permits satisfies this request; or *
      • Some other thread {@linkplain Thread#interrupt interrupts} * the current thread; or @@ -587,7 +597,7 @@ * *

        Releases the given number of permits, increasing the number of * available permits by that amount. - * If any threads are trying to acquire permits, then one + * If any threads are trying to acquire permits, then one thread * is selected and given the permits that were just released. * If the number of available permits satisfies that thread's request * then that thread is (re)enabled for thread scheduling purposes; @@ -671,7 +681,7 @@ * Returns an estimate of the number of threads waiting to acquire. * The value is only an estimate because the number of threads may * change dynamically while this method traverses internal data - * structures. This method is designed for use in monitoring of the + * structures. This method is designed for use in monitoring * system state, not for synchronization control. * * @return the estimated number of threads waiting for this lock diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/SubmissionPublisher.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/SubmissionPublisher.java Wed Jul 05 20:54:48 2017 +0200 @@ -0,0 +1,1632 @@ +/* + * 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +package java.util.concurrent; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.locks.LockSupport; +import java.util.function.BiConsumer; +import java.util.function.BiPredicate; +import java.util.function.Consumer; + +/** + * A {@link Flow.Publisher} that asynchronously issues submitted + * (non-null) items to current subscribers until it is closed. Each + * current subscriber receives newly submitted items in the same order + * unless drops or exceptions are encountered. Using a + * SubmissionPublisher allows item generators to act as compliant reactive-streams + * Publishers relying on drop handling and/or blocking for flow + * control. + * + *

        A SubmissionPublisher uses the {@link Executor} supplied in its + * constructor for delivery to subscribers. The best choice of + * Executor depends on expected usage. If the generator(s) of + * submitted items run in separate threads, and the number of + * subscribers can be estimated, consider using a {@link + * Executors#newFixedThreadPool}. Otherwise consider using the + * default, normally the {@link ForkJoinPool#commonPool}. + * + *

        Buffering allows producers and consumers to transiently operate + * at different rates. Each subscriber uses an independent buffer. + * Buffers are created upon first use and expanded as needed up to the + * given maximum. (The enforced capacity may be rounded up to the + * nearest power of two and/or bounded by the largest value supported + * by this implementation.) Invocations of {@link + * Flow.Subscription#request(long) request} do not directly result in + * buffer expansion, but risk saturation if unfilled requests exceed + * the maximum capacity. The default value of {@link + * Flow#defaultBufferSize()} may provide a useful starting point for + * choosing a capacity based on expected rates, resources, and usages. + * + *

        Publication methods support different policies about what to do + * when buffers are saturated. Method {@link #submit(Object) submit} + * blocks until resources are available. This is simplest, but least + * responsive. The {@code offer} methods may drop items (either + * immediately or with bounded timeout), but provide an opportunity to + * interpose a handler and then retry. + * + *

        If any Subscriber method throws an exception, its subscription + * is cancelled. If a handler is supplied as a constructor argument, + * it is invoked before cancellation upon an exception in method + * {@link Flow.Subscriber#onNext onNext}, but exceptions in methods + * {@link Flow.Subscriber#onSubscribe onSubscribe}, + * {@link Flow.Subscriber#onError(Throwable) onError} and + * {@link Flow.Subscriber#onComplete() onComplete} are not recorded or + * handled before cancellation. If the supplied Executor throws + * {@link RejectedExecutionException} (or any other RuntimeException + * or Error) when attempting to execute a task, or a drop handler + * throws an exception when processing a dropped item, then the + * exception is rethrown. In these cases, not all subscribers will + * have been issued the published item. It is usually good practice to + * {@link #closeExceptionally closeExceptionally} in these cases. + * + *

        Method {@link #consume(Consumer)} simplifies support for a + * common case in which the only action of a subscriber is to request + * and process all items using a supplied function. + * + *

        This class may also serve as a convenient base for subclasses + * that generate items, and use the methods in this class to publish + * them. For example here is a class that periodically publishes the + * items generated from a supplier. (In practice you might add methods + * to independently start and stop generation, to share Executors + * among publishers, and so on, or use a SubmissionPublisher as a + * component rather than a superclass.) + * + *

         {@code
        + * class PeriodicPublisher extends SubmissionPublisher {
        + *   final ScheduledFuture periodicTask;
        + *   final ScheduledExecutorService scheduler;
        + *   PeriodicPublisher(Executor executor, int maxBufferCapacity,
        + *                     Supplier supplier,
        + *                     long period, TimeUnit unit) {
        + *     super(executor, maxBufferCapacity);
        + *     scheduler = new ScheduledThreadPoolExecutor(1);
        + *     periodicTask = scheduler.scheduleAtFixedRate(
        + *       () -> submit(supplier.get()), 0, period, unit);
        + *   }
        + *   public void close() {
        + *     periodicTask.cancel(false);
        + *     scheduler.shutdown();
        + *     super.close();
        + *   }
        + * }}
        + * + *

        Here is an example of a {@link Flow.Processor} implementation. + * It uses single-step requests to its publisher for simplicity of + * illustration. A more adaptive version could monitor flow using the + * lag estimate returned from {@code submit}, along with other utility + * methods. + * + *

         {@code
        + * class TransformProcessor extends SubmissionPublisher
        + *   implements Flow.Processor {
        + *   final Function function;
        + *   Flow.Subscription subscription;
        + *   TransformProcessor(Executor executor, int maxBufferCapacity,
        + *                      Function function) {
        + *     super(executor, maxBufferCapacity);
        + *     this.function = function;
        + *   }
        + *   public void onSubscribe(Flow.Subscription subscription) {
        + *     (this.subscription = subscription).request(1);
        + *   }
        + *   public void onNext(S item) {
        + *     subscription.request(1);
        + *     submit(function.apply(item));
        + *   }
        + *   public void onError(Throwable ex) { closeExceptionally(ex); }
        + *   public void onComplete() { close(); }
        + * }}
        + * + * @param the published item type + * @author Doug Lea + * @since 1.9 + */ +public class SubmissionPublisher implements Flow.Publisher, + AutoCloseable { + /* + * Most mechanics are handled by BufferedSubscription. This class + * mainly tracks subscribers and ensures sequentiality, by using + * built-in synchronization locks across public methods. (Using + * built-in locks works well in the most typical case in which + * only one thread submits items). + */ + + /** The largest possible power of two array size. */ + static final int BUFFER_CAPACITY_LIMIT = 1 << 30; + + /** Round capacity to power of 2, at most limit. */ + static final int roundCapacity(int cap) { + int n = cap - 1; + n |= n >>> 1; + n |= n >>> 2; + n |= n >>> 4; + n |= n >>> 8; + n |= n >>> 16; + return (n <= 0) ? 1 : // at least 1 + (n >= BUFFER_CAPACITY_LIMIT) ? BUFFER_CAPACITY_LIMIT : n + 1; + } + + // default Executor setup; nearly the same as CompletableFuture + + /** + * Default executor -- ForkJoinPool.commonPool() unless it cannot + * support parallelism. + */ + private static final Executor ASYNC_POOL = + (ForkJoinPool.getCommonPoolParallelism() > 1) ? + ForkJoinPool.commonPool() : new ThreadPerTaskExecutor(); + + /** Fallback if ForkJoinPool.commonPool() cannot support parallelism */ + private static final class ThreadPerTaskExecutor implements Executor { + public void execute(Runnable r) { new Thread(r).start(); } + } + + /** + * Clients (BufferedSubscriptions) are maintained in a linked list + * (via their "next" fields). This works well for publish loops. + * It requires O(n) traversal to check for duplicate subscribers, + * but we expect that subscribing is much less common than + * publishing. Unsubscribing occurs only during traversal loops, + * when BufferedSubscription methods return negative values + * signifying that they have been disabled. To reduce + * head-of-line blocking, submit and offer methods first call + * BufferedSubscription.offer on each subscriber, and place + * saturated ones in retries list (using nextRetry field), and + * retry, possibly blocking or dropping. + */ + BufferedSubscription clients; + + /** Run status, updated only within locks */ + volatile boolean closed; + /** If non-null, the exception in closeExceptionally */ + volatile Throwable closedException; + + // Parameters for constructing BufferedSubscriptions + final Executor executor; + final BiConsumer, ? super Throwable> onNextHandler; + final int maxBufferCapacity; + + /** + * Creates a new SubmissionPublisher using the given Executor for + * async delivery to subscribers, with the given maximum buffer size + * for each subscriber, and, if non-null, the given handler invoked + * when any Subscriber throws an exception in method {@link + * Flow.Subscriber#onNext(Object) onNext}. + * + * @param executor the executor to use for async delivery, + * supporting creation of at least one independent thread + * @param maxBufferCapacity the maximum capacity for each + * subscriber's buffer (the enforced capacity may be rounded up to + * the nearest power of two and/or bounded by the largest value + * supported by this implementation; method {@link #getMaxBufferCapacity} + * returns the actual value) + * @param handler if non-null, procedure to invoke upon exception + * thrown in method {@code onNext} + * @throws NullPointerException if executor is null + * @throws IllegalArgumentException if maxBufferCapacity not + * positive + */ + public SubmissionPublisher(Executor executor, int maxBufferCapacity, + BiConsumer, ? super Throwable> handler) { + if (executor == null) + throw new NullPointerException(); + if (maxBufferCapacity <= 0) + throw new IllegalArgumentException("capacity must be positive"); + this.executor = executor; + this.onNextHandler = handler; + this.maxBufferCapacity = roundCapacity(maxBufferCapacity); + } + + /** + * Creates a new SubmissionPublisher using the given Executor for + * async delivery to subscribers, with the given maximum buffer size + * for each subscriber, and no handler for Subscriber exceptions in + * method {@link Flow.Subscriber#onNext(Object) onNext}. + * + * @param executor the executor to use for async delivery, + * supporting creation of at least one independent thread + * @param maxBufferCapacity the maximum capacity for each + * subscriber's buffer (the enforced capacity may be rounded up to + * the nearest power of two and/or bounded by the largest value + * supported by this implementation; method {@link #getMaxBufferCapacity} + * returns the actual value) + * @throws NullPointerException if executor is null + * @throws IllegalArgumentException if maxBufferCapacity not + * positive + */ + public SubmissionPublisher(Executor executor, int maxBufferCapacity) { + this(executor, maxBufferCapacity, null); + } + + /** + * Creates a new SubmissionPublisher using the {@link + * ForkJoinPool#commonPool()} for async delivery to subscribers + * (unless it does not support a parallelism level of at least two, + * in which case, a new Thread is created to run each task), with + * maximum buffer capacity of {@link Flow#defaultBufferSize}, and no + * handler for Subscriber exceptions in method {@link + * Flow.Subscriber#onNext(Object) onNext}. + */ + public SubmissionPublisher() { + this(ASYNC_POOL, Flow.defaultBufferSize(), null); + } + + /** + * Adds the given Subscriber unless already subscribed. If already + * subscribed, the Subscriber's {@link + * Flow.Subscriber#onError(Throwable) onError} method is invoked on + * the existing subscription with an {@link IllegalStateException}. + * Otherwise, upon success, the Subscriber's {@link + * Flow.Subscriber#onSubscribe onSubscribe} method is invoked + * asynchronously with a new {@link Flow.Subscription}. If {@link + * Flow.Subscriber#onSubscribe onSubscribe} throws an exception, the + * subscription is cancelled. Otherwise, if this SubmissionPublisher + * was closed exceptionally, then the subscriber's {@link + * Flow.Subscriber#onError onError} method is invoked with the + * corresponding exception, or if closed without exception, the + * subscriber's {@link Flow.Subscriber#onComplete() onComplete} + * method is invoked. Subscribers may enable receiving items by + * invoking the {@link Flow.Subscription#request(long) request} + * method of the new Subscription, and may unsubscribe by invoking + * its {@link Flow.Subscription#cancel() cancel} method. + * + * @param subscriber the subscriber + * @throws NullPointerException if subscriber is null + */ + public void subscribe(Flow.Subscriber subscriber) { + if (subscriber == null) throw new NullPointerException(); + BufferedSubscription subscription = + new BufferedSubscription(subscriber, executor, + onNextHandler, maxBufferCapacity); + synchronized (this) { + for (BufferedSubscription b = clients, pred = null;;) { + if (b == null) { + Throwable ex; + subscription.onSubscribe(); + if ((ex = closedException) != null) + subscription.onError(ex); + else if (closed) + subscription.onComplete(); + else if (pred == null) + clients = subscription; + else + pred.next = subscription; + break; + } + BufferedSubscription next = b.next; + if (b.isDisabled()) { // remove + b.next = null; // detach + if (pred == null) + clients = next; + else + pred.next = next; + } + else if (subscriber.equals(b.subscriber)) { + b.onError(new IllegalStateException("Duplicate subscribe")); + break; + } + else + pred = b; + b = next; + } + } + } + + /** + * Publishes the given item to each current subscriber by + * asynchronously invoking its {@link Flow.Subscriber#onNext(Object) + * onNext} method, blocking uninterruptibly while resources for any + * subscriber are unavailable. This method returns an estimate of + * the maximum lag (number of items submitted but not yet consumed) + * among all current subscribers. This value is at least one + * (accounting for this submitted item) if there are any + * subscribers, else zero. + * + *

        If the Executor for this publisher throws a + * RejectedExecutionException (or any other RuntimeException or + * Error) when attempting to asynchronously notify subscribers, + * then this exception is rethrown, in which case not all + * subscribers will have been issued this item. + * + * @param item the (non-null) item to publish + * @return the estimated maximum lag among subscribers + * @throws IllegalStateException if closed + * @throws NullPointerException if item is null + * @throws RejectedExecutionException if thrown by Executor + */ + public int submit(T item) { + if (item == null) throw new NullPointerException(); + int lag = 0; + boolean complete; + synchronized (this) { + complete = closed; + BufferedSubscription b = clients; + if (!complete) { + BufferedSubscription pred = null, r = null, rtail = null; + while (b != null) { + BufferedSubscription next = b.next; + int stat = b.offer(item); + if (stat < 0) { // disabled + b.next = null; + if (pred == null) + clients = next; + else + pred.next = next; + } + else { + if (stat > lag) + lag = stat; + else if (stat == 0) { // place on retry list + b.nextRetry = null; + if (rtail == null) + r = b; + else + rtail.nextRetry = b; + rtail = b; + } + pred = b; + } + b = next; + } + while (r != null) { + BufferedSubscription nextRetry = r.nextRetry; + r.nextRetry = null; + int stat = r.submit(item); + if (stat > lag) + lag = stat; + else if (stat < 0 && clients == r) + clients = r.next; // postpone internal unsubscribes + r = nextRetry; + } + } + } + if (complete) + throw new IllegalStateException("Closed"); + else + return lag; + } + + /** + * Publishes the given item, if possible, to each current subscriber + * by asynchronously invoking its {@link + * Flow.Subscriber#onNext(Object) onNext} method. The item may be + * dropped by one or more subscribers if resource limits are + * exceeded, in which case the given handler (if non-null) is + * invoked, and if it returns true, retried once. Other calls to + * methods in this class by other threads are blocked while the + * handler is invoked. Unless recovery is assured, options are + * usually limited to logging the error and/or issuing an {@link + * Flow.Subscriber#onError(Throwable) onError} signal to the + * subscriber. + * + *

        This method returns a status indicator: If negative, it + * represents the (negative) number of drops (failed attempts to + * issue the item to a subscriber). Otherwise it is an estimate of + * the maximum lag (number of items submitted but not yet + * consumed) among all current subscribers. This value is at least + * one (accounting for this submitted item) if there are any + * subscribers, else zero. + * + *

        If the Executor for this publisher throws a + * RejectedExecutionException (or any other RuntimeException or + * Error) when attempting to asynchronously notify subscribers, or + * the drop handler throws an exception when processing a dropped + * item, then this exception is rethrown. + * + * @param item the (non-null) item to publish + * @param onDrop if non-null, the handler invoked upon a drop to a + * subscriber, with arguments of the subscriber and item; if it + * returns true, an offer is re-attempted (once) + * @return if negative, the (negative) number of drops; otherwise + * an estimate of maximum lag + * @throws IllegalStateException if closed + * @throws NullPointerException if item is null + * @throws RejectedExecutionException if thrown by Executor + */ + public int offer(T item, + BiPredicate, ? super T> onDrop) { + return doOffer(0L, item, onDrop); + } + + /** + * Publishes the given item, if possible, to each current subscriber + * by asynchronously invoking its {@link + * Flow.Subscriber#onNext(Object) onNext} method, blocking while + * resources for any subscription are unavailable, up to the + * specified timeout or until the caller thread is interrupted, at + * which point the given handler (if non-null) is invoked, and if it + * returns true, retried once. (The drop handler may distinguish + * timeouts from interrupts by checking whether the current thread + * is interrupted.) Other calls to methods in this class by other + * threads are blocked while the handler is invoked. Unless + * recovery is assured, options are usually limited to logging the + * error and/or issuing an {@link Flow.Subscriber#onError(Throwable) + * onError} signal to the subscriber. + * + *

        This method returns a status indicator: If negative, it + * represents the (negative) number of drops (failed attempts to + * issue the item to a subscriber). Otherwise it is an estimate of + * the maximum lag (number of items submitted but not yet + * consumed) among all current subscribers. This value is at least + * one (accounting for this submitted item) if there are any + * subscribers, else zero. + * + *

        If the Executor for this publisher throws a + * RejectedExecutionException (or any other RuntimeException or + * Error) when attempting to asynchronously notify subscribers, or + * the drop handler throws an exception when processing a dropped + * item, then this exception is rethrown. + * + * @param item the (non-null) item to publish + * @param timeout how long to wait for resources for any subscriber + * before giving up, in units of {@code unit} + * @param unit a {@code TimeUnit} determining how to interpret the + * {@code timeout} parameter + * @param onDrop if non-null, the handler invoked upon a drop to a + * subscriber, with arguments of the subscriber and item; if it + * returns true, an offer is re-attempted (once) + * @return if negative, the (negative) number of drops; otherwise + * an estimate of maximum lag + * @throws IllegalStateException if closed + * @throws NullPointerException if item is null + * @throws RejectedExecutionException if thrown by Executor + */ + public int offer(T item, long timeout, TimeUnit unit, + BiPredicate, ? super T> onDrop) { + return doOffer(unit.toNanos(timeout), item, onDrop); + } + + /** Common implementation for both forms of offer */ + final int doOffer(long nanos, T item, + BiPredicate, ? super T> onDrop) { + if (item == null) throw new NullPointerException(); + int lag = 0, drops = 0; + boolean complete; + synchronized (this) { + complete = closed; + BufferedSubscription b = clients; + if (!complete) { + BufferedSubscription pred = null, r = null, rtail = null; + while (b != null) { + BufferedSubscription next = b.next; + int stat = b.offer(item); + if (stat < 0) { + b.next = null; + if (pred == null) + clients = next; + else + pred.next = next; + } + else { + if (stat > lag) + lag = stat; + else if (stat == 0) { + b.nextRetry = null; + if (rtail == null) + r = b; + else + rtail.nextRetry = b; + rtail = b; + } + else if (stat > lag) + lag = stat; + pred = b; + } + b = next; + } + while (r != null) { + BufferedSubscription nextRetry = r.nextRetry; + r.nextRetry = null; + int stat = (nanos > 0L) ? r.timedOffer(item, nanos) : + r.offer(item); + if (stat == 0 && onDrop != null && + onDrop.test(r.subscriber, item)) + stat = r.offer(item); + if (stat == 0) + ++drops; + else if (stat > lag) + lag = stat; + else if (stat < 0 && clients == r) + clients = r.next; + r = nextRetry; + } + } + } + if (complete) + throw new IllegalStateException("Closed"); + else + return (drops > 0) ? -drops : lag; + } + + /** + * Unless already closed, issues {@link + * Flow.Subscriber#onComplete() onComplete} signals to current + * subscribers, and disallows subsequent attempts to publish. + * Upon return, this method does NOT guarantee that all + * subscribers have yet completed. + */ + public void close() { + if (!closed) { + BufferedSubscription b; + synchronized (this) { + b = clients; + clients = null; + closed = true; + } + while (b != null) { + BufferedSubscription next = b.next; + b.next = null; + b.onComplete(); + b = next; + } + } + } + + /** + * Unless already closed, issues {@link + * Flow.Subscriber#onError(Throwable) onError} signals to current + * subscribers with the given error, and disallows subsequent + * attempts to publish. Future subscribers also receive the given + * error. Upon return, this method does NOT guarantee + * that all subscribers have yet completed. + * + * @param error the {@code onError} argument sent to subscribers + * @throws NullPointerException if error is null + */ + public void closeExceptionally(Throwable error) { + if (error == null) + throw new NullPointerException(); + if (!closed) { + BufferedSubscription b; + synchronized (this) { + b = clients; + clients = null; + closed = true; + closedException = error; + } + while (b != null) { + BufferedSubscription next = b.next; + b.next = null; + b.onError(error); + b = next; + } + } + } + + /** + * Returns true if this publisher is not accepting submissions. + * + * @return true if closed + */ + public boolean isClosed() { + return closed; + } + + /** + * Returns the exception associated with {@link + * #closeExceptionally(Throwable) closeExceptionally}, or null if + * not closed or if closed normally. + * + * @return the exception, or null if none + */ + public Throwable getClosedException() { + return closedException; + } + + /** + * Returns true if this publisher has any subscribers. + * + * @return true if this publisher has any subscribers + */ + public boolean hasSubscribers() { + boolean nonEmpty = false; + if (!closed) { + synchronized (this) { + for (BufferedSubscription b = clients; b != null;) { + BufferedSubscription next = b.next; + if (b.isDisabled()) { + b.next = null; + b = clients = next; + } + else { + nonEmpty = true; + break; + } + } + } + } + return nonEmpty; + } + + /** + * Returns the number of current subscribers. + * + * @return the number of current subscribers + */ + public int getNumberOfSubscribers() { + int count = 0; + if (!closed) { + synchronized (this) { + BufferedSubscription pred = null, next; + for (BufferedSubscription b = clients; b != null; b = next) { + next = b.next; + if (b.isDisabled()) { + b.next = null; + if (pred == null) + clients = next; + else + pred.next = next; + } + else { + pred = b; + ++count; + } + } + } + } + return count; + } + + /** + * Returns the Executor used for asynchronous delivery. + * + * @return the Executor used for asynchronous delivery + */ + public Executor getExecutor() { + return executor; + } + + /** + * Returns the maximum per-subscriber buffer capacity. + * + * @return the maximum per-subscriber buffer capacity + */ + public int getMaxBufferCapacity() { + return maxBufferCapacity; + } + + /** + * Returns a list of current subscribers for monitoring and + * tracking purposes, not for invoking {@link Flow.Subscriber} + * methods on the subscribers. + * + * @return list of current subscribers + */ + public List> getSubscribers() { + ArrayList> subs = new ArrayList<>(); + synchronized (this) { + BufferedSubscription pred = null, next; + for (BufferedSubscription b = clients; b != null; b = next) { + next = b.next; + if (b.isDisabled()) { + b.next = null; + if (pred == null) + clients = next; + else + pred.next = next; + } + else + subs.add(b.subscriber); + } + } + return subs; + } + + /** + * Returns true if the given Subscriber is currently subscribed. + * + * @param subscriber the subscriber + * @return true if currently subscribed + * @throws NullPointerException if subscriber is null + */ + public boolean isSubscribed(Flow.Subscriber subscriber) { + if (subscriber == null) throw new NullPointerException(); + if (!closed) { + synchronized (this) { + BufferedSubscription pred = null, next; + for (BufferedSubscription b = clients; b != null; b = next) { + next = b.next; + if (b.isDisabled()) { + b.next = null; + if (pred == null) + clients = next; + else + pred.next = next; + } + else if (subscriber.equals(b.subscriber)) + return true; + else + pred = b; + } + } + } + return false; + } + + /** + * Returns an estimate of the minimum number of items requested + * (via {@link Flow.Subscription#request(long) request}) but not + * yet produced, among all current subscribers. + * + * @return the estimate, or zero if no subscribers + */ + public long estimateMinimumDemand() { + long min = Long.MAX_VALUE; + boolean nonEmpty = false; + synchronized (this) { + BufferedSubscription pred = null, next; + for (BufferedSubscription b = clients; b != null; b = next) { + int n; long d; + next = b.next; + if ((n = b.estimateLag()) < 0) { + b.next = null; + if (pred == null) + clients = next; + else + pred.next = next; + } + else { + if ((d = b.demand - n) < min) + min = d; + nonEmpty = true; + pred = b; + } + } + } + return nonEmpty ? min : 0; + } + + /** + * Returns an estimate of the maximum number of items produced but + * not yet consumed among all current subscribers. + * + * @return the estimate + */ + public int estimateMaximumLag() { + int max = 0; + synchronized (this) { + BufferedSubscription pred = null, next; + for (BufferedSubscription b = clients; b != null; b = next) { + int n; + next = b.next; + if ((n = b.estimateLag()) < 0) { + b.next = null; + if (pred == null) + clients = next; + else + pred.next = next; + } + else { + if (n > max) + max = n; + pred = b; + } + } + } + return max; + } + + /** + * Processes all published items using the given Consumer function. + * Returns a CompletableFuture that is completed normally when this + * publisher signals {@link Flow.Subscriber#onComplete() + * onComplete}, or completed exceptionally upon any error, or an + * exception is thrown by the Consumer, or the returned + * CompletableFuture is cancelled, in which case no further items + * are processed. + * + * @param consumer the function applied to each onNext item + * @return a CompletableFuture that is completed normally + * when the publisher signals onComplete, and exceptionally + * upon any error or cancellation + * @throws NullPointerException if consumer is null + */ + public CompletableFuture consume(Consumer consumer) { + if (consumer == null) + throw new NullPointerException(); + CompletableFuture status = new CompletableFuture<>(); + subscribe(new ConsumerSubscriber(status, consumer)); + return status; + } + + /** Subscriber for method consume */ + private static final class ConsumerSubscriber + implements Flow.Subscriber { + final CompletableFuture status; + final Consumer consumer; + Flow.Subscription subscription; + ConsumerSubscriber(CompletableFuture status, + Consumer consumer) { + this.status = status; this.consumer = consumer; + } + public final void onSubscribe(Flow.Subscription subscription) { + this.subscription = subscription; + status.whenComplete((v, e) -> subscription.cancel()); + if (!status.isDone()) + subscription.request(Long.MAX_VALUE); + } + public final void onError(Throwable ex) { + status.completeExceptionally(ex); + } + public final void onComplete() { + status.complete(null); + } + public final void onNext(T item) { + try { + consumer.accept(item); + } catch (Throwable ex) { + subscription.cancel(); + status.completeExceptionally(ex); + } + } + } + + /** + * A task for consuming buffer items and signals, created and + * executed whenever they become available. A task consumes as + * many items/signals as possible before terminating, at which + * point another task is created when needed. The dual Runnable + * and ForkJoinTask declaration saves overhead when executed by + * ForkJoinPools, without impacting other kinds of Executors. + */ + @SuppressWarnings("serial") + static final class ConsumerTask extends ForkJoinTask + implements Runnable { + final BufferedSubscription consumer; + ConsumerTask(BufferedSubscription consumer) { + this.consumer = consumer; + } + public final Void getRawResult() { return null; } + public final void setRawResult(Void v) {} + public final boolean exec() { consumer.consume(); return false; } + public final void run() { consumer.consume(); } + } + + /** + * A bounded (ring) buffer with integrated control to start a + * consumer task whenever items are available. The buffer + * algorithm is similar to one used inside ForkJoinPool (see its + * internal documentation for details) specialized for the case of + * at most one concurrent producer and consumer, and power of two + * buffer sizes. This allows methods to operate without locks even + * while supporting resizing, blocking, task-triggering, and + * garbage-free buffers (nulling out elements when consumed), + * although supporting these does impose a bit of overhead + * compared to plain fixed-size ring buffers. + * + * The publisher guarantees a single producer via its lock. We + * ensure in this class that there is at most one consumer. The + * request and cancel methods must be fully thread-safe but are + * coded to exploit the most common case in which they are only + * called by consumers (usually within onNext). + * + * Execution control is managed using the ACTIVE ctl bit. We + * ensure that a task is active when consumable items (and + * usually, SUBSCRIBE, ERROR or COMPLETE signals) are present and + * there is demand (unfilled requests). This is complicated on + * the creation side by the possibility of exceptions when trying + * to execute tasks. These eventually force DISABLED state, but + * sometimes not directly. On the task side, termination (clearing + * ACTIVE) that would otherwise race with producers or request() + * calls uses the CONSUME keep-alive bit to force a recheck. + * + * The ctl field also manages run state. When DISABLED, no further + * updates are possible. Disabling may be preceded by setting + * ERROR or COMPLETE (or both -- ERROR has precedence), in which + * case the associated Subscriber methods are invoked, possibly + * synchronously if there is no active consumer task (including + * cases where execute() failed). The cancel() method is supported + * by treating as ERROR but suppressing onError signal. + * + * Support for blocking also exploits the fact that there is only + * one possible waiter. ManagedBlocker-compatible control fields + * are placed in this class itself rather than in wait-nodes. + * Blocking control relies on the "waiter" field. Producers set + * the field before trying to block, but must then recheck (via + * offer) before parking. Signalling then just unparks and clears + * waiter field. If the producer and consumer are both in the same + * ForkJoinPool, or consumers are running in commonPool, the + * producer attempts to help run consumer tasks that it forked + * before blocking. To avoid potential cycles, only one level of + * helping is currently supported. + * + * This class uses @Contended and heuristic field declaration + * ordering to reduce false-sharing-based memory contention among + * instances of BufferedSubscription, but it does not currently + * attempt to avoid memory contention among buffers. This field + * and element packing can hurt performance especially when each + * publisher has only one client operating at a high rate. + * Addressing this may require allocating substantially more space + * than users expect. + */ + @SuppressWarnings("serial") + @sun.misc.Contended + private static final class BufferedSubscription + implements Flow.Subscription, ForkJoinPool.ManagedBlocker { + // Order-sensitive field declarations + long timeout; // > 0 if timed wait + volatile long demand; // # unfilled requests + int maxCapacity; // reduced on OOME + int putStat; // offer result for ManagedBlocker + int helpDepth; // nested helping depth (at most 1) + volatile int ctl; // atomic run state flags + volatile int head; // next position to take + int tail; // next position to put + Object[] array; // buffer: null if disabled + Flow.Subscriber subscriber; // null if disabled + Executor executor; // null if disabled + BiConsumer, ? super Throwable> onNextHandler; + volatile Throwable pendingError; // holds until onError issued + volatile Thread waiter; // blocked producer thread + T putItem; // for offer within ManagedBlocker + BufferedSubscription next; // used only by publisher + BufferedSubscription nextRetry; // used only by publisher + + // ctl values + static final int ACTIVE = 0x01; // consumer task active + static final int CONSUME = 0x02; // keep-alive for consumer task + static final int DISABLED = 0x04; // final state + static final int ERROR = 0x08; // signal onError then disable + static final int SUBSCRIBE = 0x10; // signal onSubscribe + static final int COMPLETE = 0x20; // signal onComplete when done + + static final long INTERRUPTED = -1L; // timeout vs interrupt sentinel + + /** + * Initial buffer capacity used when maxBufferCapacity is + * greater. Must be a power of two. + */ + static final int DEFAULT_INITIAL_CAP = 32; + + BufferedSubscription(Flow.Subscriber subscriber, + Executor executor, + BiConsumer, + ? super Throwable> onNextHandler, + int maxBufferCapacity) { + this.subscriber = subscriber; + this.executor = executor; + this.onNextHandler = onNextHandler; + this.maxCapacity = maxBufferCapacity; + this.array = new Object[maxBufferCapacity < DEFAULT_INITIAL_CAP ? + (maxBufferCapacity < 2 ? // at least 2 slots + 2 : maxBufferCapacity) : + DEFAULT_INITIAL_CAP]; + } + + final boolean isDisabled() { + return ctl == DISABLED; + } + + /** + * Returns estimated number of buffered items, or -1 if + * disabled. + */ + final int estimateLag() { + int n; + return (ctl == DISABLED) ? -1 : ((n = tail - head) > 0) ? n : 0; + } + + /** + * Tries to add item and start consumer task if necessary. + * @return -1 if disabled, 0 if dropped, else estimated lag + */ + final int offer(T item) { + int h = head, t = tail, cap, size, stat; + Object[] a = array; + if (a != null && (cap = a.length) > 0 && cap >= (size = t + 1 - h)) { + a[(cap - 1) & t] = item; // relaxed writes OK + tail = t + 1; + stat = size; + } + else + stat = growAndAdd(a, item); + return (stat > 0 && + (ctl & (ACTIVE | CONSUME)) != (ACTIVE | CONSUME)) ? + startOnOffer(stat) : stat; + } + + /** + * Tries to create or expand buffer, then adds item if possible. + */ + private int growAndAdd(Object[] a, T item) { + boolean alloc; + int cap, stat; + if ((ctl & (ERROR | DISABLED)) != 0) { + cap = 0; + stat = -1; + alloc = false; + } + else if (a == null || (cap = a.length) <= 0) { + cap = 0; + stat = 1; + alloc = true; + } + else { + U.fullFence(); // recheck + int h = head, t = tail, size = t + 1 - h; + if (cap >= size) { + a[(cap - 1) & t] = item; + tail = t + 1; + stat = size; + alloc = false; + } + else if (cap >= maxCapacity) { + stat = 0; // cannot grow + alloc = false; + } + else { + stat = cap + 1; + alloc = true; + } + } + if (alloc) { + int newCap = (cap > 0) ? cap << 1 : 1; + if (newCap <= cap) + stat = 0; + else { + Object[] newArray = null; + try { + newArray = new Object[newCap]; + } catch (Throwable ex) { // try to cope with OOME + } + if (newArray == null) { + if (cap > 0) + maxCapacity = cap; // avoid continuous failure + stat = 0; + } + else { + array = newArray; + int t = tail; + int newMask = newCap - 1; + if (a != null && cap > 0) { + int mask = cap - 1; + for (int j = head; j != t; ++j) { + long k = ((long)(j & mask) << ASHIFT) + ABASE; + Object x = U.getObjectVolatile(a, k); + if (x != null && // races with consumer + U.compareAndSwapObject(a, k, x, null)) + newArray[j & newMask] = x; + } + } + newArray[t & newMask] = item; + tail = t + 1; + } + } + } + return stat; + } + + /** + * Spins/helps/blocks while offer returns 0. Called only if + * initial offer return 0. + */ + final int submit(T item) { + int stat; Executor e; ForkJoinWorkerThread w; + if ((stat = offer(item)) == 0 && helpDepth == 0 && + ((e = executor) instanceof ForkJoinPool)) { + helpDepth = 1; + Thread thread = Thread.currentThread(); + if ((thread instanceof ForkJoinWorkerThread) && + ((w = (ForkJoinWorkerThread)thread)).getPool() == e) + stat = internalHelpConsume(w.workQueue, item); + else if (e == ForkJoinPool.commonPool()) + stat = externalHelpConsume + (ForkJoinPool.commonSubmitterQueue(), item); + helpDepth = 0; + } + if (stat == 0 && (stat = offer(item)) == 0) { + putItem = item; + timeout = 0L; + try { + ForkJoinPool.managedBlock(this); + } catch (InterruptedException ie) { + timeout = INTERRUPTED; + } + stat = putStat; + if (timeout < 0L) + Thread.currentThread().interrupt(); + } + return stat; + } + + /** + * Tries helping for FJ submitter. + */ + private int internalHelpConsume(ForkJoinPool.WorkQueue w, T item) { + int stat = 0; + if (w != null) { + ForkJoinTask t; + while ((t = w.peek()) != null && (t instanceof ConsumerTask)) { + if ((stat = offer(item)) != 0 || !w.tryUnpush(t)) + break; + ((ConsumerTask)t).consumer.consume(); + } + } + return stat; + } + + /** + * Tries helping for non-FJ submitter. + */ + private int externalHelpConsume(ForkJoinPool.WorkQueue w, T item) { + int stat = 0; + if (w != null) { + ForkJoinTask t; + while ((t = w.peek()) != null && (t instanceof ConsumerTask)) { + if ((stat = offer(item)) != 0 || !w.trySharedUnpush(t)) + break; + ((ConsumerTask)t).consumer.consume(); + } + } + return stat; + } + + /** + * Timeout version; similar to submit. + */ + final int timedOffer(T item, long nanos) { + int stat; Executor e; + if ((stat = offer(item)) == 0 && helpDepth == 0 && + ((e = executor) instanceof ForkJoinPool)) { + Thread thread = Thread.currentThread(); + if (((thread instanceof ForkJoinWorkerThread) && + ((ForkJoinWorkerThread)thread).getPool() == e) || + e == ForkJoinPool.commonPool()) { + helpDepth = 1; + ForkJoinTask t; + long deadline = System.nanoTime() + nanos; + while ((t = ForkJoinTask.peekNextLocalTask()) != null && + (t instanceof ConsumerTask)) { + if ((stat = offer(item)) != 0 || + (nanos = deadline - System.nanoTime()) <= 0L || + !t.tryUnfork()) + break; + ((ConsumerTask)t).consumer.consume(); + } + helpDepth = 0; + } + } + if (stat == 0 && (stat = offer(item)) == 0 && + (timeout = nanos) > 0L) { + putItem = item; + try { + ForkJoinPool.managedBlock(this); + } catch (InterruptedException ie) { + timeout = INTERRUPTED; + } + stat = putStat; + if (timeout < 0L) + Thread.currentThread().interrupt(); + } + return stat; + } + + /** + * Tries to start consumer task after offer. + * @return -1 if now disabled, else argument + */ + private int startOnOffer(int stat) { + for (;;) { + Executor e; int c; + if ((c = ctl) == DISABLED || (e = executor) == null) { + stat = -1; + break; + } + else if ((c & ACTIVE) != 0) { // ensure keep-alive + if ((c & CONSUME) != 0 || + U.compareAndSwapInt(this, CTL, c, + c | CONSUME)) + break; + } + else if (demand == 0L || tail == head) + break; + else if (U.compareAndSwapInt(this, CTL, c, + c | (ACTIVE | CONSUME))) { + try { + e.execute(new ConsumerTask(this)); + break; + } catch (RuntimeException | Error ex) { // back out + do {} while (((c = ctl) & DISABLED) == 0 && + (c & ACTIVE) != 0 && + !U.compareAndSwapInt(this, CTL, c, + c & ~ACTIVE)); + throw ex; + } + } + } + return stat; + } + + private void signalWaiter(Thread w) { + waiter = null; + LockSupport.unpark(w); // release producer + } + + /** + * Nulls out most fields, mainly to avoid garbage retention + * until publisher unsubscribes, but also to help cleanly stop + * upon error by nulling required components. + */ + private void detach() { + Thread w = waiter; + executor = null; + subscriber = null; + pendingError = null; + signalWaiter(w); + } + + /** + * Issues error signal, asynchronously if a task is running, + * else synchronously. + */ + final void onError(Throwable ex) { + for (int c;;) { + if (((c = ctl) & (ERROR | DISABLED)) != 0) + break; + else if ((c & ACTIVE) != 0) { + pendingError = ex; + if (U.compareAndSwapInt(this, CTL, c, c | ERROR)) + break; // cause consumer task to exit + } + else if (U.compareAndSwapInt(this, CTL, c, DISABLED)) { + Flow.Subscriber s = subscriber; + if (s != null && ex != null) { + try { + s.onError(ex); + } catch (Throwable ignore) { + } + } + detach(); + break; + } + } + } + + /** + * Tries to start consumer task upon a signal or request; + * disables on failure. + */ + private void startOrDisable() { + Executor e; + if ((e = executor) != null) { // skip if already disabled + try { + e.execute(new ConsumerTask(this)); + } catch (Throwable ex) { // back out and force signal + for (int c;;) { + if ((c = ctl) == DISABLED || (c & ACTIVE) == 0) + break; + if (U.compareAndSwapInt(this, CTL, c, c & ~ACTIVE)) { + onError(ex); + break; + } + } + } + } + } + + final void onComplete() { + for (int c;;) { + if ((c = ctl) == DISABLED) + break; + if (U.compareAndSwapInt(this, CTL, c, + c | (ACTIVE | CONSUME | COMPLETE))) { + if ((c & ACTIVE) == 0) + startOrDisable(); + break; + } + } + } + + final void onSubscribe() { + for (int c;;) { + if ((c = ctl) == DISABLED) + break; + if (U.compareAndSwapInt(this, CTL, c, + c | (ACTIVE | CONSUME | SUBSCRIBE))) { + if ((c & ACTIVE) == 0) + startOrDisable(); + break; + } + } + } + + /** + * Causes consumer task to exit if active (without reporting + * onError unless there is already a pending error), and + * disables. + */ + public void cancel() { + for (int c;;) { + if ((c = ctl) == DISABLED) + break; + else if ((c & ACTIVE) != 0) { + if (U.compareAndSwapInt(this, CTL, c, + c | (CONSUME | ERROR))) + break; + } + else if (U.compareAndSwapInt(this, CTL, c, DISABLED)) { + detach(); + break; + } + } + } + + /** + * Adds to demand and possibly starts task. + */ + public void request(long n) { + if (n > 0L) { + for (;;) { + long prev = demand, d; + if ((d = prev + n) < prev) // saturate + d = Long.MAX_VALUE; + if (U.compareAndSwapLong(this, DEMAND, prev, d)) { + for (int c, h;;) { + if ((c = ctl) == DISABLED) + break; + else if ((c & ACTIVE) != 0) { + if ((c & CONSUME) != 0 || + U.compareAndSwapInt(this, CTL, c, + c | CONSUME)) + break; + } + else if ((h = head) != tail) { + if (U.compareAndSwapInt(this, CTL, c, + c | (ACTIVE|CONSUME))) { + startOrDisable(); + break; + } + } + else if (head == h && tail == h) + break; // else stale + if (demand == 0L) + break; + } + break; + } + } + } + else if (n < 0L) + onError(new IllegalArgumentException( + "negative subscription request")); + } + + public final boolean isReleasable() { // for ManagedBlocker + T item = putItem; + if (item != null) { + if ((putStat = offer(item)) == 0) + return false; + putItem = null; + } + return true; + } + + public final boolean block() { // for ManagedBlocker + T item = putItem; + if (item != null) { + putItem = null; + long nanos = timeout; + long deadline = (nanos > 0L) ? System.nanoTime() + nanos : 0L; + while ((putStat = offer(item)) == 0) { + if (Thread.interrupted()) { + timeout = INTERRUPTED; + if (nanos > 0L) + break; + } + else if (nanos > 0L && + (nanos = deadline - System.nanoTime()) <= 0L) + break; + else if (waiter == null) + waiter = Thread.currentThread(); + else { + if (nanos > 0L) + LockSupport.parkNanos(this, nanos); + else + LockSupport.park(this); + waiter = null; + } + } + } + waiter = null; + return true; + } + + /** + * Consumer loop, called from ConsumerTask, or indirectly + * when helping during submit. + */ + final void consume() { + Flow.Subscriber s; + int h = head; + if ((s = subscriber) != null) { // else disabled + for (;;) { + long d = demand; + int c; Object[] a; int n; long i; Object x; Thread w; + if (((c = ctl) & (ERROR | SUBSCRIBE | DISABLED)) != 0) { + if (!checkControl(s, c)) + break; + } + else if ((a = array) == null || h == tail || + (n = a.length) == 0 || + (x = U.getObjectVolatile + (a, (i = ((long)((n - 1) & h) << ASHIFT) + ABASE))) + == null) { + if (!checkEmpty(s, c)) + break; + } + else if (d == 0L) { + if (!checkDemand(c)) + break; + } + else if (((c & CONSUME) != 0 || + U.compareAndSwapInt(this, CTL, c, c | CONSUME)) && + U.compareAndSwapObject(a, i, x, null)) { + U.putOrderedInt(this, HEAD, ++h); + U.getAndAddLong(this, DEMAND, -1L); + if ((w = waiter) != null) + signalWaiter(w); + try { + @SuppressWarnings("unchecked") T y = (T) x; + s.onNext(y); + } catch (Throwable ex) { + handleOnNext(s, ex); + } + } + } + } + } + + /** + * Responds to control events in consume(). + */ + private boolean checkControl(Flow.Subscriber s, int c) { + boolean stat = true; + if ((c & ERROR) != 0) { + Throwable ex = pendingError; + ctl = DISABLED; // no need for CAS + if (ex != null) { // null if errorless cancel + try { + if (s != null) + s.onError(ex); + } catch (Throwable ignore) { + } + } + } + else if ((c & SUBSCRIBE) != 0) { + if (U.compareAndSwapInt(this, CTL, c, c & ~SUBSCRIBE)) { + try { + if (s != null) + s.onSubscribe(this); + } catch (Throwable ex) { + onError(ex); + } + } + } + else { + detach(); + stat = false; + } + return stat; + } + + /** + * Responds to apparent emptiness in consume(). + */ + private boolean checkEmpty(Flow.Subscriber s, int c) { + boolean stat = true; + if (head == tail) { + if ((c & CONSUME) != 0) + U.compareAndSwapInt(this, CTL, c, c & ~CONSUME); + else if ((c & COMPLETE) != 0) { + if (U.compareAndSwapInt(this, CTL, c, DISABLED)) { + try { + if (s != null) + s.onComplete(); + } catch (Throwable ignore) { + } + } + } + else if (U.compareAndSwapInt(this, CTL, c, c & ~ACTIVE)) + stat = false; + } + return stat; + } + + /** + * Responds to apparent zero demand in consume(). + */ + private boolean checkDemand(int c) { + boolean stat = true; + if (demand == 0L) { + if ((c & CONSUME) != 0) + U.compareAndSwapInt(this, CTL, c, c & ~CONSUME); + else if (U.compareAndSwapInt(this, CTL, c, c & ~ACTIVE)) + stat = false; + } + return stat; + } + + /** + * Processes exception in Subscriber.onNext. + */ + private void handleOnNext(Flow.Subscriber s, Throwable ex) { + BiConsumer, ? super Throwable> h; + if ((h = onNextHandler) != null) { + try { + h.accept(s, ex); + } catch (Throwable ignore) { + } + } + onError(ex); + } + + // Unsafe mechanics + private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe(); + private static final long CTL; + private static final long TAIL; + private static final long HEAD; + private static final long DEMAND; + private static final int ABASE; + private static final int ASHIFT; + + static { + try { + CTL = U.objectFieldOffset + (BufferedSubscription.class.getDeclaredField("ctl")); + TAIL = U.objectFieldOffset + (BufferedSubscription.class.getDeclaredField("tail")); + HEAD = U.objectFieldOffset + (BufferedSubscription.class.getDeclaredField("head")); + DEMAND = U.objectFieldOffset + (BufferedSubscription.class.getDeclaredField("demand")); + + ABASE = U.arrayBaseOffset(Object[].class); + int scale = U.arrayIndexScale(Object[].class); + if ((scale & (scale - 1)) != 0) + throw new Error("data type scale not a power of two"); + ASHIFT = 31 - Integer.numberOfLeadingZeros(scale); + } catch (ReflectiveOperationException e) { + throw new Error(e); + } + + // Reduce the risk of rare disastrous classloading in first call to + // LockSupport.park: https://bugs.openjdk.java.net/browse/JDK-8074773 + Class ensureLoaded = LockSupport.class; + } + } +} diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/SynchronousQueue.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/SynchronousQueue.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/SynchronousQueue.java Wed Jul 05 20:54:48 2017 +0200 @@ -35,11 +35,15 @@ */ package java.util.concurrent; + +import java.util.AbstractQueue; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.Spliterator; +import java.util.Spliterators; import java.util.concurrent.locks.LockSupport; import java.util.concurrent.locks.ReentrantLock; -import java.util.*; -import java.util.Spliterator; -import java.util.Spliterators; /** * A {@linkplain BlockingQueue blocking queue} in which each insert @@ -79,7 +83,7 @@ * * @since 1.5 * @author Doug Lea and Bill Scherer and Michael Scott - * @param the type of elements held in this collection + * @param the type of elements held in this queue */ public class SynchronousQueue extends AbstractQueue implements BlockingQueue, java.io.Serializable { @@ -182,9 +186,6 @@ abstract E transfer(E e, boolean timed, long nanos); } - /** The number of CPUs, for spin control */ - static final int NCPUS = Runtime.getRuntime().availableProcessors(); - /** * The number of times to spin before blocking in timed waits. * The value is empirically derived -- it works well across a @@ -192,20 +193,21 @@ * seems not to vary with number of CPUs (beyond 2) so is just * a constant. */ - static final int maxTimedSpins = (NCPUS < 2) ? 0 : 32; + static final int MAX_TIMED_SPINS = + (Runtime.getRuntime().availableProcessors() < 2) ? 0 : 32; /** * The number of times to spin before blocking in untimed waits. * This is greater than timed value because untimed waits spin * faster since they don't need to check times on each spin. */ - static final int maxUntimedSpins = maxTimedSpins * 16; + static final int MAX_UNTIMED_SPINS = MAX_TIMED_SPINS * 16; /** * The number of nanoseconds for which it is faster to spin * rather than to use timed park. A rough estimate suffices. */ - static final long spinForTimeoutThreshold = 1000L; + static final long SPIN_FOR_TIMEOUT_THRESHOLD = 1000L; /** Dual stack */ static final class TransferStack extends Transferer { @@ -245,7 +247,7 @@ boolean casNext(SNode cmp, SNode val) { return cmp == next && - UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val); + U.compareAndSwapObject(this, NEXT, cmp, val); } /** @@ -258,7 +260,7 @@ */ boolean tryMatch(SNode s) { if (match == null && - UNSAFE.compareAndSwapObject(this, matchOffset, null, s)) { + U.compareAndSwapObject(this, MATCH, null, s)) { Thread w = waiter; if (w != null) { // waiters need at most one unpark waiter = null; @@ -273,7 +275,7 @@ * Tries to cancel a wait by matching node to itself. */ void tryCancel() { - UNSAFE.compareAndSwapObject(this, matchOffset, null, this); + U.compareAndSwapObject(this, MATCH, null, this); } boolean isCancelled() { @@ -281,19 +283,17 @@ } // Unsafe mechanics - private static final sun.misc.Unsafe UNSAFE; - private static final long matchOffset; - private static final long nextOffset; + private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe(); + private static final long MATCH; + private static final long NEXT; static { try { - UNSAFE = sun.misc.Unsafe.getUnsafe(); - Class k = SNode.class; - matchOffset = UNSAFE.objectFieldOffset - (k.getDeclaredField("match")); - nextOffset = UNSAFE.objectFieldOffset - (k.getDeclaredField("next")); - } catch (Exception e) { + MATCH = U.objectFieldOffset + (SNode.class.getDeclaredField("match")); + NEXT = U.objectFieldOffset + (SNode.class.getDeclaredField("next")); + } catch (ReflectiveOperationException e) { throw new Error(e); } } @@ -304,7 +304,7 @@ boolean casHead(SNode h, SNode nh) { return h == head && - UNSAFE.compareAndSwapObject(this, headOffset, h, nh); + U.compareAndSwapObject(this, HEAD, h, nh); } /** @@ -353,7 +353,7 @@ for (;;) { SNode h = head; if (h == null || h.mode == mode) { // empty or same-mode - if (timed && nanos <= 0) { // can't wait + if (timed && nanos <= 0L) { // can't wait if (h != null && h.isCancelled()) casHead(h, h.next); // pop cancelled node else @@ -435,8 +435,9 @@ */ final long deadline = timed ? System.nanoTime() + nanos : 0L; Thread w = Thread.currentThread(); - int spins = (shouldSpin(s) ? - (timed ? maxTimedSpins : maxUntimedSpins) : 0); + int spins = shouldSpin(s) + ? (timed ? MAX_TIMED_SPINS : MAX_UNTIMED_SPINS) + : 0; for (;;) { if (w.isInterrupted()) s.tryCancel(); @@ -451,12 +452,12 @@ } } if (spins > 0) - spins = shouldSpin(s) ? (spins-1) : 0; + spins = shouldSpin(s) ? (spins - 1) : 0; else if (s.waiter == null) s.waiter = w; // establish waiter so can park next iter else if (!timed) LockSupport.park(this); - else if (nanos > spinForTimeoutThreshold) + else if (nanos > SPIN_FOR_TIMEOUT_THRESHOLD) LockSupport.parkNanos(this, nanos); } } @@ -508,15 +509,13 @@ } // Unsafe mechanics - private static final sun.misc.Unsafe UNSAFE; - private static final long headOffset; + private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe(); + private static final long HEAD; static { try { - UNSAFE = sun.misc.Unsafe.getUnsafe(); - Class k = TransferStack.class; - headOffset = UNSAFE.objectFieldOffset - (k.getDeclaredField("head")); - } catch (Exception e) { + HEAD = U.objectFieldOffset + (TransferStack.class.getDeclaredField("head")); + } catch (ReflectiveOperationException e) { throw new Error(e); } } @@ -547,19 +546,19 @@ boolean casNext(QNode cmp, QNode val) { return next == cmp && - UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val); + U.compareAndSwapObject(this, NEXT, cmp, val); } boolean casItem(Object cmp, Object val) { return item == cmp && - UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val); + U.compareAndSwapObject(this, ITEM, cmp, val); } /** * Tries to cancel by CAS'ing ref to this as item. */ void tryCancel(Object cmp) { - UNSAFE.compareAndSwapObject(this, itemOffset, cmp, this); + U.compareAndSwapObject(this, ITEM, cmp, this); } boolean isCancelled() { @@ -576,19 +575,17 @@ } // Unsafe mechanics - private static final sun.misc.Unsafe UNSAFE; - private static final long itemOffset; - private static final long nextOffset; + private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe(); + private static final long ITEM; + private static final long NEXT; static { try { - UNSAFE = sun.misc.Unsafe.getUnsafe(); - Class k = QNode.class; - itemOffset = UNSAFE.objectFieldOffset - (k.getDeclaredField("item")); - nextOffset = UNSAFE.objectFieldOffset - (k.getDeclaredField("next")); - } catch (Exception e) { + ITEM = U.objectFieldOffset + (QNode.class.getDeclaredField("item")); + NEXT = U.objectFieldOffset + (QNode.class.getDeclaredField("next")); + } catch (ReflectiveOperationException e) { throw new Error(e); } } @@ -617,7 +614,7 @@ */ void advanceHead(QNode h, QNode nh) { if (h == head && - UNSAFE.compareAndSwapObject(this, headOffset, h, nh)) + U.compareAndSwapObject(this, HEAD, h, nh)) h.next = h; // forget old next } @@ -626,7 +623,7 @@ */ void advanceTail(QNode t, QNode nt) { if (tail == t) - UNSAFE.compareAndSwapObject(this, tailOffset, t, nt); + U.compareAndSwapObject(this, TAIL, t, nt); } /** @@ -634,7 +631,7 @@ */ boolean casCleanMe(QNode cmp, QNode val) { return cleanMe == cmp && - UNSAFE.compareAndSwapObject(this, cleanMeOffset, cmp, val); + U.compareAndSwapObject(this, CLEANME, cmp, val); } /** @@ -684,7 +681,7 @@ advanceTail(t, tn); continue; } - if (timed && nanos <= 0) // can't wait + if (timed && nanos <= 0L) // can't wait return null; if (s == null) s = new QNode(e, isData); @@ -739,8 +736,9 @@ /* Same idea as TransferStack.awaitFulfill */ final long deadline = timed ? System.nanoTime() + nanos : 0L; Thread w = Thread.currentThread(); - int spins = ((head.next == s) ? - (timed ? maxTimedSpins : maxUntimedSpins) : 0); + int spins = (head.next == s) + ? (timed ? MAX_TIMED_SPINS : MAX_UNTIMED_SPINS) + : 0; for (;;) { if (w.isInterrupted()) s.tryCancel(e); @@ -760,7 +758,7 @@ s.waiter = w; else if (!timed) LockSupport.park(this); - else if (nanos > spinForTimeoutThreshold) + else if (nanos > SPIN_FOR_TIMEOUT_THRESHOLD) LockSupport.parkNanos(this, nanos); } } @@ -819,21 +817,19 @@ } } - private static final sun.misc.Unsafe UNSAFE; - private static final long headOffset; - private static final long tailOffset; - private static final long cleanMeOffset; + private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe(); + private static final long HEAD; + private static final long TAIL; + private static final long CLEANME; static { try { - UNSAFE = sun.misc.Unsafe.getUnsafe(); - Class k = TransferQueue.class; - headOffset = UNSAFE.objectFieldOffset - (k.getDeclaredField("head")); - tailOffset = UNSAFE.objectFieldOffset - (k.getDeclaredField("tail")); - cleanMeOffset = UNSAFE.objectFieldOffset - (k.getDeclaredField("cleanMe")); - } catch (Exception e) { + HEAD = U.objectFieldOffset + (TransferQueue.class.getDeclaredField("head")); + TAIL = U.objectFieldOffset + (TransferQueue.class.getDeclaredField("tail")); + CLEANME = U.objectFieldOffset + (TransferQueue.class.getDeclaredField("cleanMe")); + } catch (ReflectiveOperationException e) { throw new Error(e); } } @@ -1088,7 +1084,7 @@ } /** - * Sets the zeroeth element of the specified array to {@code null} + * Sets the zeroth element of the specified array to {@code null} * (if the array has non-zero length) and returns it. * * @param a the array @@ -1102,6 +1098,14 @@ } /** + * Always returns {@code "[]"}. + * @return {@code "[]"} + */ + public String toString() { + return "[]"; + } + + /** * @throws UnsupportedOperationException {@inheritDoc} * @throws ClassCastException {@inheritDoc} * @throws NullPointerException {@inheritDoc} @@ -1196,17 +1200,9 @@ transferer = new TransferStack(); } - // Unsafe mechanics - static long objectFieldOffset(sun.misc.Unsafe UNSAFE, - String field, Class klazz) { - try { - return UNSAFE.objectFieldOffset(klazz.getDeclaredField(field)); - } catch (NoSuchFieldException e) { - // Convert Exception to corresponding Error - NoSuchFieldError error = new NoSuchFieldError(field); - error.initCause(e); - throw error; - } + static { + // Reduce the risk of rare disastrous classloading in first call to + // LockSupport.park: https://bugs.openjdk.java.net/browse/JDK-8074773 + Class ensureLoaded = LockSupport.class; } - } diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/ThreadFactory.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/ThreadFactory.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ThreadFactory.java Wed Jul 05 20:54:48 2017 +0200 @@ -42,7 +42,7 @@ * *

        * The simplest implementation of this interface is just: - *

         {@code
        + * 
         {@code
          * class SimpleThreadFactory implements ThreadFactory {
          *   public Thread newThread(Runnable r) {
          *     return new Thread(r);
        diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/ThreadLocalRandom.java
        --- a/jdk/src/java.base/share/classes/java/util/concurrent/ThreadLocalRandom.java	Wed Oct 21 15:15:26 2015 -0700
        +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ThreadLocalRandom.java	Wed Jul 05 20:54:48 2017 +0200
        @@ -126,8 +126,7 @@
              */
         
             /** Generates per-thread initialization/probe field */
        -    private static final AtomicInteger probeGenerator =
        -        new AtomicInteger();
        +    private static final AtomicInteger probeGenerator = new AtomicInteger();
         
             /**
              * The next seed for default constructors.
        @@ -150,17 +149,17 @@
             }
         
             /**
        -     * The seed increment
        +     * The seed increment.
              */
             private static final long GAMMA = 0x9e3779b97f4a7c15L;
         
             /**
        -     * The increment for generating probe values
        +     * The increment for generating probe values.
              */
             private static final int PROBE_INCREMENT = 0x9e3779b9;
         
             /**
        -     * The increment of seeder per new instance
        +     * The increment of seeder per new instance.
              */
             private static final long SEEDER_INCREMENT = 0xbb67ae8584caa73bL;
         
        @@ -170,7 +169,7 @@
         
             /** Rarely-used holder for the second of a pair of Gaussians */
             private static final ThreadLocal nextLocalGaussian =
        -        new ThreadLocal();
        +        new ThreadLocal<>();
         
             private static long mix64(long z) {
                 z = (z ^ (z >>> 33)) * 0xff51afd7ed558ccdL;
        @@ -209,8 +208,8 @@
                 int probe = (p == 0) ? 1 : p; // skip 0
                 long seed = mix64(seeder.getAndAdd(SEEDER_INCREMENT));
                 Thread t = Thread.currentThread();
        -        UNSAFE.putLong(t, SEED, seed);
        -        UNSAFE.putInt(t, PROBE, probe);
        +        U.putLong(t, SEED, seed);
        +        U.putInt(t, PROBE, probe);
             }
         
             /**
        @@ -219,7 +218,7 @@
              * @return the current thread's {@code ThreadLocalRandom}
              */
             public static ThreadLocalRandom current() {
        -        if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0)
        +        if (U.getInt(Thread.currentThread(), PROBE) == 0)
                     localInit();
                 return instance;
             }
        @@ -238,8 +237,8 @@
         
             final long nextSeed() {
                 Thread t; long r; // read and update per-thread seed
        -        UNSAFE.putLong(t = Thread.currentThread(), SEED,
        -                       r = UNSAFE.getLong(t, SEED) + GAMMA);
        +        U.putLong(t = Thread.currentThread(), SEED,
        +                  r = U.getLong(t, SEED) + GAMMA);
                 return r;
             }
         
        @@ -249,9 +248,9 @@
             }
         
             // IllegalArgumentException messages
        -    static final String BadBound = "bound must be positive";
        -    static final String BadRange = "bound must be greater than origin";
        -    static final String BadSize  = "size must be non-negative";
        +    static final String BAD_BOUND = "bound must be positive";
        +    static final String BAD_RANGE = "bound must be greater than origin";
        +    static final String BAD_SIZE  = "size must be non-negative";
         
             /**
              * The form of nextLong used by LongStream Spliterators.  If
        @@ -349,7 +348,7 @@
              */
             public int nextInt(int bound) {
                 if (bound <= 0)
        -            throw new IllegalArgumentException(BadBound);
        +            throw new IllegalArgumentException(BAD_BOUND);
                 int r = mix32(nextSeed());
                 int m = bound - 1;
                 if ((bound & m) == 0) // power of two
        @@ -376,7 +375,7 @@
              */
             public int nextInt(int origin, int bound) {
                 if (origin >= bound)
        -            throw new IllegalArgumentException(BadRange);
        +            throw new IllegalArgumentException(BAD_RANGE);
                 return internalNextInt(origin, bound);
             }
         
        @@ -400,7 +399,7 @@
              */
             public long nextLong(long bound) {
                 if (bound <= 0)
        -            throw new IllegalArgumentException(BadBound);
        +            throw new IllegalArgumentException(BAD_BOUND);
                 long r = mix64(nextSeed());
                 long m = bound - 1;
                 if ((bound & m) == 0L) // power of two
        @@ -427,7 +426,7 @@
              */
             public long nextLong(long origin, long bound) {
                 if (origin >= bound)
        -            throw new IllegalArgumentException(BadRange);
        +            throw new IllegalArgumentException(BAD_RANGE);
                 return internalNextLong(origin, bound);
             }
         
        @@ -453,9 +452,9 @@
              */
             public double nextDouble(double bound) {
                 if (!(bound > 0.0))
        -            throw new IllegalArgumentException(BadBound);
        +            throw new IllegalArgumentException(BAD_BOUND);
                 double result = (mix64(nextSeed()) >>> 11) * DOUBLE_UNIT * bound;
        -        return (result < bound) ?  result : // correct for rounding
        +        return (result < bound) ? result : // correct for rounding
                     Double.longBitsToDouble(Double.doubleToLongBits(bound) - 1);
             }
         
        @@ -472,7 +471,7 @@
              */
             public double nextDouble(double origin, double bound) {
                 if (!(origin < bound))
        -            throw new IllegalArgumentException(BadRange);
        +            throw new IllegalArgumentException(BAD_RANGE);
                 return internalNextDouble(origin, bound);
             }
         
        @@ -529,7 +528,7 @@
              */
             public IntStream ints(long streamSize) {
                 if (streamSize < 0L)
        -            throw new IllegalArgumentException(BadSize);
        +            throw new IllegalArgumentException(BAD_SIZE);
                 return StreamSupport.intStream
                     (new RandomIntsSpliterator
                      (0L, streamSize, Integer.MAX_VALUE, 0),
        @@ -571,9 +570,9 @@
             public IntStream ints(long streamSize, int randomNumberOrigin,
                                   int randomNumberBound) {
                 if (streamSize < 0L)
        -            throw new IllegalArgumentException(BadSize);
        +            throw new IllegalArgumentException(BAD_SIZE);
                 if (randomNumberOrigin >= randomNumberBound)
        -            throw new IllegalArgumentException(BadRange);
        +            throw new IllegalArgumentException(BAD_RANGE);
                 return StreamSupport.intStream
                     (new RandomIntsSpliterator
                      (0L, streamSize, randomNumberOrigin, randomNumberBound),
        @@ -598,7 +597,7 @@
              */
             public IntStream ints(int randomNumberOrigin, int randomNumberBound) {
                 if (randomNumberOrigin >= randomNumberBound)
        -            throw new IllegalArgumentException(BadRange);
        +            throw new IllegalArgumentException(BAD_RANGE);
                 return StreamSupport.intStream
                     (new RandomIntsSpliterator
                      (0L, Long.MAX_VALUE, randomNumberOrigin, randomNumberBound),
        @@ -617,7 +616,7 @@
              */
             public LongStream longs(long streamSize) {
                 if (streamSize < 0L)
        -            throw new IllegalArgumentException(BadSize);
        +            throw new IllegalArgumentException(BAD_SIZE);
                 return StreamSupport.longStream
                     (new RandomLongsSpliterator
                      (0L, streamSize, Long.MAX_VALUE, 0L),
        @@ -659,9 +658,9 @@
             public LongStream longs(long streamSize, long randomNumberOrigin,
                                     long randomNumberBound) {
                 if (streamSize < 0L)
        -            throw new IllegalArgumentException(BadSize);
        +            throw new IllegalArgumentException(BAD_SIZE);
                 if (randomNumberOrigin >= randomNumberBound)
        -            throw new IllegalArgumentException(BadRange);
        +            throw new IllegalArgumentException(BAD_RANGE);
                 return StreamSupport.longStream
                     (new RandomLongsSpliterator
                      (0L, streamSize, randomNumberOrigin, randomNumberBound),
        @@ -686,7 +685,7 @@
              */
             public LongStream longs(long randomNumberOrigin, long randomNumberBound) {
                 if (randomNumberOrigin >= randomNumberBound)
        -            throw new IllegalArgumentException(BadRange);
        +            throw new IllegalArgumentException(BAD_RANGE);
                 return StreamSupport.longStream
                     (new RandomLongsSpliterator
                      (0L, Long.MAX_VALUE, randomNumberOrigin, randomNumberBound),
        @@ -706,7 +705,7 @@
              */
             public DoubleStream doubles(long streamSize) {
                 if (streamSize < 0L)
        -            throw new IllegalArgumentException(BadSize);
        +            throw new IllegalArgumentException(BAD_SIZE);
                 return StreamSupport.doubleStream
                     (new RandomDoublesSpliterator
                      (0L, streamSize, Double.MAX_VALUE, 0.0),
        @@ -750,9 +749,9 @@
             public DoubleStream doubles(long streamSize, double randomNumberOrigin,
                                         double randomNumberBound) {
                 if (streamSize < 0L)
        -            throw new IllegalArgumentException(BadSize);
        +            throw new IllegalArgumentException(BAD_SIZE);
                 if (!(randomNumberOrigin < randomNumberBound))
        -            throw new IllegalArgumentException(BadRange);
        +            throw new IllegalArgumentException(BAD_RANGE);
                 return StreamSupport.doubleStream
                     (new RandomDoublesSpliterator
                      (0L, streamSize, randomNumberOrigin, randomNumberBound),
        @@ -777,7 +776,7 @@
              */
             public DoubleStream doubles(double randomNumberOrigin, double randomNumberBound) {
                 if (!(randomNumberOrigin < randomNumberBound))
        -            throw new IllegalArgumentException(BadRange);
        +            throw new IllegalArgumentException(BAD_RANGE);
                 return StreamSupport.doubleStream
                     (new RandomDoublesSpliterator
                      (0L, Long.MAX_VALUE, randomNumberOrigin, randomNumberBound),
        @@ -792,7 +791,8 @@
              * approach. The long and double versions of this class are
              * identical except for types.
              */
        -    static final class RandomIntsSpliterator implements Spliterator.OfInt {
        +    private static final class RandomIntsSpliterator
        +            implements Spliterator.OfInt {
                 long index;
                 final long fence;
                 final int origin;
        @@ -846,7 +846,8 @@
             /**
              * Spliterator for long streams.
              */
        -    static final class RandomLongsSpliterator implements Spliterator.OfLong {
        +    private static final class RandomLongsSpliterator
        +            implements Spliterator.OfLong {
                 long index;
                 final long fence;
                 final long origin;
        @@ -901,7 +902,8 @@
             /**
              * Spliterator for double streams.
              */
        -    static final class RandomDoublesSpliterator implements Spliterator.OfDouble {
        +    private static final class RandomDoublesSpliterator
        +            implements Spliterator.OfDouble {
                 long index;
                 final long fence;
                 final double origin;
        @@ -978,7 +980,7 @@
              * can be used to force initialization on zero return.
              */
             static final int getProbe() {
        -        return UNSAFE.getInt(Thread.currentThread(), PROBE);
        +        return U.getInt(Thread.currentThread(), PROBE);
             }
         
             /**
        @@ -989,7 +991,7 @@
                 probe ^= probe << 13;   // xorshift
                 probe ^= probe >>> 17;
                 probe ^= probe << 5;
        -        UNSAFE.putInt(Thread.currentThread(), PROBE, probe);
        +        U.putInt(Thread.currentThread(), PROBE, probe);
                 return probe;
             }
         
        @@ -999,17 +1001,14 @@
             static final int nextSecondarySeed() {
                 int r;
                 Thread t = Thread.currentThread();
        -        if ((r = UNSAFE.getInt(t, SECONDARY)) != 0) {
        +        if ((r = U.getInt(t, SECONDARY)) != 0) {
                     r ^= r << 13;   // xorshift
                     r ^= r >>> 17;
                     r ^= r << 5;
                 }
        -        else {
        -            localInit();
        -            if ((r = (int)UNSAFE.getLong(t, SEED)) == 0)
        -                r = 1; // avoid zero
        -        }
        -        UNSAFE.putInt(t, SECONDARY, r);
        +        else if ((r = mix32(seeder.getAndAdd(SEEDER_INCREMENT))) == 0)
        +            r = 1; // avoid zero
        +        U.putInt(t, SECONDARY, r);
                 return r;
             }
         
        @@ -1024,8 +1023,8 @@
              *              always true
              */
             private static final ObjectStreamField[] serialPersistentFields = {
        -            new ObjectStreamField("rnd", long.class),
        -            new ObjectStreamField("initialized", boolean.class),
        +        new ObjectStreamField("rnd", long.class),
        +        new ObjectStreamField("initialized", boolean.class),
             };
         
             /**
        @@ -1037,7 +1036,7 @@
                 throws java.io.IOException {
         
                 java.io.ObjectOutputStream.PutField fields = s.putFields();
        -        fields.put("rnd", UNSAFE.getLong(Thread.currentThread(), SEED));
        +        fields.put("rnd", U.getLong(Thread.currentThread(), SEED));
                 fields.put("initialized", true);
                 s.writeFields();
             }
        @@ -1051,21 +1050,19 @@
             }
         
             // Unsafe mechanics
        -    private static final sun.misc.Unsafe UNSAFE;
        +    private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
             private static final long SEED;
             private static final long PROBE;
             private static final long SECONDARY;
             static {
                 try {
        -            UNSAFE = sun.misc.Unsafe.getUnsafe();
        -            Class tk = Thread.class;
        -            SEED = UNSAFE.objectFieldOffset
        -                (tk.getDeclaredField("threadLocalRandomSeed"));
        -            PROBE = UNSAFE.objectFieldOffset
        -                (tk.getDeclaredField("threadLocalRandomProbe"));
        -            SECONDARY = UNSAFE.objectFieldOffset
        -                (tk.getDeclaredField("threadLocalRandomSecondarySeed"));
        -        } catch (Exception e) {
        +            SEED = U.objectFieldOffset
        +                (Thread.class.getDeclaredField("threadLocalRandomSeed"));
        +            PROBE = U.objectFieldOffset
        +                (Thread.class.getDeclaredField("threadLocalRandomProbe"));
        +            SECONDARY = U.objectFieldOffset
        +                (Thread.class.getDeclaredField("threadLocalRandomSecondarySeed"));
        +        } catch (ReflectiveOperationException e) {
                     throw new Error(e);
                 }
             }
        diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/ThreadPoolExecutor.java
        --- a/jdk/src/java.base/share/classes/java/util/concurrent/ThreadPoolExecutor.java	Wed Oct 21 15:15:26 2015 -0700
        +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ThreadPoolExecutor.java	Wed Jul 05 20:54:48 2017 +0200
        @@ -34,11 +34,16 @@
          */
         
         package java.util.concurrent;
        +
        +import java.util.ArrayList;
        +import java.util.ConcurrentModificationException;
        +import java.util.HashSet;
        +import java.util.Iterator;
        +import java.util.List;
        +import java.util.concurrent.atomic.AtomicInteger;
         import java.util.concurrent.locks.AbstractQueuedSynchronizer;
         import java.util.concurrent.locks.Condition;
         import java.util.concurrent.locks.ReentrantLock;
        -import java.util.concurrent.atomic.AtomicInteger;
        -import java.util.*;
         
         /**
          * An {@link ExecutorService} that executes each submitted task using
        @@ -69,7 +74,8 @@
          *
          * 
        Core and maximum pool sizes
        * - *
        A {@code ThreadPoolExecutor} will automatically adjust the + *
        + * A {@code ThreadPoolExecutor} will automatically adjust the * pool size (see {@link #getPoolSize}) * according to the bounds set by * corePoolSize (see {@link #getCorePoolSize}) and @@ -91,7 +97,8 @@ * *
        On-demand construction
        * - *
        By default, even core threads are initially created and + *
        + * By default, even core threads are initially created and * started only when new tasks arrive, but this can be overridden * dynamically using method {@link #prestartCoreThread} or {@link * #prestartAllCoreThreads}. You probably want to prestart threads if @@ -99,7 +106,8 @@ * *
        Creating new threads
        * - *
        New threads are created using a {@link ThreadFactory}. If not + *
        + * New threads are created using a {@link ThreadFactory}. If not * otherwise specified, a {@link Executors#defaultThreadFactory} is * used, that creates threads to all be in the same {@link * ThreadGroup} and with the same {@code NORM_PRIORITY} priority and @@ -116,7 +124,8 @@ * *
        Keep-alive times
        * - *
        If the pool currently has more than corePoolSize threads, + *
        + * If the pool currently has more than corePoolSize threads, * excess threads will be terminated if they have been idle for more * than the keepAliveTime (see {@link #getKeepAliveTime(TimeUnit)}). * This provides a means of reducing resource consumption when the @@ -126,36 +135,37 @@ * TimeUnit)}. Using a value of {@code Long.MAX_VALUE} {@link * TimeUnit#NANOSECONDS} effectively disables idle threads from ever * terminating prior to shut down. By default, the keep-alive policy - * applies only when there are more than corePoolSize threads. But + * applies only when there are more than corePoolSize threads, but * method {@link #allowCoreThreadTimeOut(boolean)} can be used to * apply this time-out policy to core threads as well, so long as the * keepAliveTime value is non-zero.
        * *
        Queuing
        * - *
        Any {@link BlockingQueue} may be used to transfer and hold + *
        + * Any {@link BlockingQueue} may be used to transfer and hold * submitted tasks. The use of this queue interacts with pool sizing: * *
          * - *
        • If fewer than corePoolSize threads are running, the Executor + *
        • If fewer than corePoolSize threads are running, the Executor * always prefers adding a new thread - * rather than queuing.
        • + * rather than queuing. * - *
        • If corePoolSize or more threads are running, the Executor + *
        • If corePoolSize or more threads are running, the Executor * always prefers queuing a request rather than adding a new - * thread.
        • + * thread. * - *
        • If a request cannot be queued, a new thread is created unless + *
        • If a request cannot be queued, a new thread is created unless * this would exceed maximumPoolSize, in which case, the task will be - * rejected.
        • + * rejected. * *
        * * There are three general strategies for queuing: *
          * - *
        1. Direct handoffs. A good default choice for a work + *
        2. Direct handoffs. A good default choice for a work * queue is a {@link SynchronousQueue} that hands off tasks to threads * without otherwise holding them. Here, an attempt to queue a task * will fail if no threads are immediately available to run it, so a @@ -164,7 +174,7 @@ * Direct handoffs generally require unbounded maximumPoolSizes to * avoid rejection of new submitted tasks. This in turn admits the * possibility of unbounded thread growth when commands continue to - * arrive on average faster than they can be processed.
        3. + * arrive on average faster than they can be processed. * *
        4. Unbounded queues. Using an unbounded queue (for * example a {@link LinkedBlockingQueue} without a predefined @@ -177,7 +187,7 @@ * While this style of queuing can be useful in smoothing out * transient bursts of requests, it admits the possibility of * unbounded work queue growth when commands continue to arrive on - * average faster than they can be processed.
        5. + * average faster than they can be processed. * *
        6. Bounded queues. A bounded queue (for example, an * {@link ArrayBlockingQueue}) helps prevent resource exhaustion when @@ -190,7 +200,7 @@ * time for more threads than you otherwise allow. Use of small queues * generally requires larger pool sizes, which keeps CPUs busier but * may encounter unacceptable scheduling overhead, which also - * decreases throughput.
        7. + * decreases throughput. * *
        * @@ -198,7 +208,8 @@ * *
        Rejected tasks
        * - *
        New tasks submitted in method {@link #execute(Runnable)} will be + *
        + * New tasks submitted in method {@link #execute(Runnable)} will be * rejected when the Executor has been shut down, and also when * the Executor uses finite bounds for both maximum threads and work queue * capacity, and is saturated. In either case, the {@code execute} method @@ -209,22 +220,22 @@ * *
          * - *
        1. In the default {@link ThreadPoolExecutor.AbortPolicy}, the + *
        2. In the default {@link ThreadPoolExecutor.AbortPolicy}, the * handler throws a runtime {@link RejectedExecutionException} upon - * rejection.
        3. + * rejection. * - *
        4. In {@link ThreadPoolExecutor.CallerRunsPolicy}, the thread + *
        5. In {@link ThreadPoolExecutor.CallerRunsPolicy}, the thread * that invokes {@code execute} itself runs the task. This provides a * simple feedback control mechanism that will slow down the rate that - * new tasks are submitted.
        6. + * new tasks are submitted. * - *
        7. In {@link ThreadPoolExecutor.DiscardPolicy}, a task that - * cannot be executed is simply dropped.
        8. + *
        9. In {@link ThreadPoolExecutor.DiscardPolicy}, a task that + * cannot be executed is simply dropped. * *
        10. In {@link ThreadPoolExecutor.DiscardOldestPolicy}, if the * executor is not shut down, the task at the head of the work queue * is dropped, and then execution is retried (which can fail again, - * causing this to be repeated.)
        11. + * causing this to be repeated.) * *
        * @@ -235,7 +246,8 @@ * *
        Hook methods
        * - *
        This class provides {@code protected} overridable + *
        + * This class provides {@code protected} overridable * {@link #beforeExecute(Thread, Runnable)} and * {@link #afterExecute(Runnable, Throwable)} methods that are called * before and after execution of each task. These can be used to @@ -245,12 +257,14 @@ * any special processing that needs to be done once the Executor has * fully terminated. * - *

        If hook or callback methods throw exceptions, internal worker - * threads may in turn fail and abruptly terminate.

        + *

        If hook, callback, or BlockingQueue methods throw exceptions, + * internal worker threads may in turn fail, abruptly terminate, and + * possibly be replaced. * *

        Queue maintenance
        * - *
        Method {@link #getQueue()} allows access to the work queue + *
        + * Method {@link #getQueue()} allows access to the work queue * for purposes of monitoring and debugging. Use of this method for * any other purpose is strongly discouraged. Two supplied methods, * {@link #remove(Runnable)} and {@link #purge} are available to @@ -259,7 +273,8 @@ * *
        Finalization
        * - *
        A pool that is no longer referenced in a program AND + *
        + * A pool that is no longer referenced in a program AND * has no remaining threads will be {@code shutdown} automatically. If * you would like to ensure that unreferenced pools are reclaimed even * if users forget to call {@link #shutdown}, then you must arrange @@ -273,7 +288,7 @@ * override one or more of the protected hook methods. For example, * here is a subclass that adds a simple pause/resume feature: * - *
         {@code
        + * 
         {@code
          * class PausableThreadPoolExecutor extends ThreadPoolExecutor {
          *   private boolean isPaused;
          *   private ReentrantLock pauseLock = new ReentrantLock();
        @@ -462,10 +477,10 @@
              * Set containing all worker threads in pool. Accessed only when
              * holding mainLock.
              */
        -    private final HashSet workers = new HashSet();
        +    private final HashSet workers = new HashSet<>();
         
             /**
        -     * Wait condition to support awaitTermination
        +     * Wait condition to support awaitTermination.
              */
             private final Condition termination = mainLock.newCondition();
         
        @@ -541,7 +556,7 @@
             private volatile int maximumPoolSize;
         
             /**
        -     * The default rejected execution handler
        +     * The default rejected execution handler.
              */
             private static final RejectedExecutionHandler defaultHandler =
                 new AbortPolicy();
        @@ -612,7 +627,7 @@
                     this.thread = getThreadFactory().newThread(this);
                 }
         
        -        /** Delegates main run loop to outer runWorker  */
        +        /** Delegates main run loop to outer runWorker. */
                 public void run() {
                     runWorker(this);
                 }
        @@ -668,6 +683,7 @@
              *        (but not TIDYING or TERMINATED -- use tryTerminate for that)
              */
             private void advanceRunState(int targetState) {
        +        // assert targetState == SHUTDOWN || targetState == STOP;
                 for (;;) {
                     int c = ctl.get();
                     if (runStateAtLeast(c, targetState) ||
        @@ -850,7 +866,7 @@
              */
             private List drainQueue() {
                 BlockingQueue q = workQueue;
        -        ArrayList taskList = new ArrayList();
        +        ArrayList taskList = new ArrayList<>();
                 q.drainTo(taskList);
                 if (!q.isEmpty()) {
                     for (Runnable r : q.toArray(new Runnable[0])) {
        @@ -1406,7 +1422,7 @@
              *
              * 

        There are no guarantees beyond best-effort attempts to stop * processing actively executing tasks. This implementation - * cancels tasks via {@link Thread#interrupt}, so any task that + * interrupts tasks via {@link Thread#interrupt}; any task that * fails to respond to interrupts may never terminate. * * @throws SecurityException {@inheritDoc} @@ -1457,13 +1473,12 @@ final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { - for (;;) { - if (runStateAtLeast(ctl.get(), TERMINATED)) - return true; - if (nanos <= 0) + while (!runStateAtLeast(ctl.get(), TERMINATED)) { + if (nanos <= 0L) return false; nanos = termination.awaitNanos(nanos); } + return true; } finally { mainLock.unlock(); } @@ -1680,11 +1695,13 @@ } /** - * Sets the time limit for which threads may remain idle before - * being terminated. If there are more than the core number of - * threads currently in the pool, after waiting this amount of - * time without processing a task, excess threads will be - * terminated. This overrides any value set in the constructor. + * Sets the thread keep-alive time, which is the amount of time + * that threads may remain idle before being terminated. + * Threads that wait this amount of time without processing a + * task will be terminated if there are more than the core + * number of threads currently in the pool, or if this pool + * {@linkplain #allowsCoreThreadTimeOut() allows core thread timeout}. + * This overrides any value set in the constructor. * * @param time the time to wait. A time value of zero will cause * excess threads to terminate immediately after executing tasks. @@ -1707,8 +1724,11 @@ /** * Returns the thread keep-alive time, which is the amount of time - * that threads in excess of the core pool size may remain - * idle before being terminated. + * that threads may remain idle before being terminated. + * Threads that wait this amount of time without processing a + * task will be terminated if there are more than the core + * number of threads currently in the pool, or if this pool + * {@linkplain #allowsCoreThreadTimeOut() allows core thread timeout}. * * @param unit the desired time unit of the result * @return the time limit @@ -1739,8 +1759,8 @@ * *

        This method may be useful as one part of a cancellation * scheme. It may fail to remove tasks that have been converted - * into other forms before being placed on the internal queue. For - * example, a task entered using {@code submit} might be + * into other forms before being placed on the internal queue. + * For example, a task entered using {@code submit} might be * converted into a form that maintains {@code Future} status. * However, in such cases, method {@link #purge} may be used to * remove those Futures that have been cancelled. @@ -1912,11 +1932,12 @@ mainLock.unlock(); } int c = ctl.get(); - String rs = (runStateLessThan(c, SHUTDOWN) ? "Running" : - (runStateAtLeast(c, TERMINATED) ? "Terminated" : - "Shutting down")); + String runState = + runStateLessThan(c, SHUTDOWN) ? "Running" : + runStateAtLeast(c, TERMINATED) ? "Terminated" : + "Shutting down"; return super.toString() + - "[" + rs + + "[" + runState + ", pool size = " + nworkers + ", active threads = " + nactive + ", queued tasks = " + workQueue.size() + @@ -1963,20 +1984,23 @@ * as in this sample subclass that prints either the direct cause * or the underlying exception if a task has been aborted: * - *

         {@code
        +     * 
         {@code
              * class ExtendedExecutor extends ThreadPoolExecutor {
              *   // ...
              *   protected void afterExecute(Runnable r, Throwable t) {
              *     super.afterExecute(r, t);
        -     *     if (t == null && r instanceof Future) {
        +     *     if (t == null
        +     *         && r instanceof Future
        +     *         && ((Future)r).isDone()) {
              *       try {
              *         Object result = ((Future) r).get();
              *       } catch (CancellationException ce) {
        -     *           t = ce;
        +     *         t = ce;
              *       } catch (ExecutionException ee) {
        -     *           t = ee.getCause();
        +     *         t = ee.getCause();
              *       } catch (InterruptedException ie) {
        -     *           Thread.currentThread().interrupt(); // ignore/reset
        +     *         // ignore/reset
        +     *         Thread.currentThread().interrupt();
              *       }
              *     }
              *     if (t != null)
        diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/TimeUnit.java
        --- a/jdk/src/java.base/share/classes/java/util/concurrent/TimeUnit.java	Wed Oct 21 15:15:26 2015 -0700
        +++ b/jdk/src/java.base/share/classes/java/util/concurrent/TimeUnit.java	Wed Jul 05 20:54:48 2017 +0200
        @@ -52,12 +52,12 @@
          * the following code will timeout in 50 milliseconds if the {@link
          * java.util.concurrent.locks.Lock lock} is not available:
          *
        - *  
         {@code
        + * 
         {@code
          * Lock lock = ...;
          * if (lock.tryLock(50L, TimeUnit.MILLISECONDS)) ...}
        * * while this code will timeout in 50 seconds: - *
         {@code
        + * 
         {@code
          * Lock lock = ...;
          * if (lock.tryLock(50L, TimeUnit.SECONDS)) ...}
        * @@ -70,7 +70,7 @@ */ public enum TimeUnit { /** - * Time unit representing one thousandth of a microsecond + * Time unit representing one thousandth of a microsecond. */ NANOSECONDS { public long toNanos(long d) { return d; } @@ -85,7 +85,7 @@ }, /** - * Time unit representing one thousandth of a millisecond + * Time unit representing one thousandth of a millisecond. */ MICROSECONDS { public long toNanos(long d) { return x(d, C1/C0, MAX/(C1/C0)); } @@ -100,7 +100,7 @@ }, /** - * Time unit representing one thousandth of a second + * Time unit representing one thousandth of a second. */ MILLISECONDS { public long toNanos(long d) { return x(d, C2/C0, MAX/(C2/C0)); } @@ -115,7 +115,7 @@ }, /** - * Time unit representing one second + * Time unit representing one second. */ SECONDS { public long toNanos(long d) { return x(d, C3/C0, MAX/(C3/C0)); } @@ -130,7 +130,7 @@ }, /** - * Time unit representing sixty seconds + * Time unit representing sixty seconds. * @since 1.6 */ MINUTES { @@ -146,7 +146,7 @@ }, /** - * Time unit representing sixty minutes + * Time unit representing sixty minutes. * @since 1.6 */ HOURS { @@ -162,7 +162,7 @@ }, /** - * Time unit representing twenty four hours + * Time unit representing twenty four hours. * @since 1.6 */ DAYS { @@ -193,7 +193,7 @@ * This has a short name to make above code more readable. */ static long x(long d, long m, long over) { - if (d > over) return Long.MAX_VALUE; + if (d > +over) return Long.MAX_VALUE; if (d < -over) return Long.MIN_VALUE; return d * m; } @@ -329,7 +329,7 @@ * method (see {@link BlockingQueue#poll BlockingQueue.poll}) * using: * - *
         {@code
        +     * 
         {@code
              * public synchronized Object poll(long timeout, TimeUnit unit)
              *     throws InterruptedException {
              *   while (empty) {
        diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/TransferQueue.java
        --- a/jdk/src/java.base/share/classes/java/util/concurrent/TransferQueue.java	Wed Oct 21 15:15:26 2015 -0700
        +++ b/jdk/src/java.base/share/classes/java/util/concurrent/TransferQueue.java	Wed Jul 05 20:54:48 2017 +0200
        @@ -63,7 +63,7 @@
          *
          * @since 1.7
          * @author Doug Lea
        - * @param  the type of elements held in this collection
        + * @param  the type of elements held in this queue
          */
         public interface TransferQueue extends BlockingQueue {
             /**
        diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceArray.java
        --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceArray.java	Wed Oct 21 15:15:26 2015 -0700
        +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceArray.java	Wed Jul 05 20:54:48 2017 +0200
        @@ -53,9 +53,9 @@
             private static final long serialVersionUID = -6209656149925076980L;
         
             private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
        +    private static final long ARRAY;
             private static final int ABASE;
             private static final int ASHIFT;
        -    private static final long ARRAY;
             private final Object[] array; // must have exact type Object[]
         
             static {
        diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java
        --- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java	Wed Oct 21 15:15:26 2015 -0700
        +++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java	Wed Jul 05 20:54:48 2017 +0200
        @@ -34,11 +34,12 @@
          */
         
         package java.util.concurrent.locks;
        -import java.util.concurrent.TimeUnit;
        +
         import java.util.ArrayList;
         import java.util.Collection;
         import java.util.Date;
        -import sun.misc.Unsafe;
        +import java.util.concurrent.TimeUnit;
        +import java.util.concurrent.locks.AbstractQueuedSynchronizer.Node;
         
         /**
          * A version of {@link AbstractQueuedSynchronizer} in
        @@ -77,221 +78,6 @@
             protected AbstractQueuedLongSynchronizer() { }
         
             /**
        -     * Wait queue node class.
        -     *
        -     * 

        The wait queue is a variant of a "CLH" (Craig, Landin, and - * Hagersten) lock queue. CLH locks are normally used for - * spinlocks. We instead use them for blocking synchronizers, but - * use the same basic tactic of holding some of the control - * information about a thread in the predecessor of its node. A - * "status" field in each node keeps track of whether a thread - * should block. A node is signalled when its predecessor - * releases. Each node of the queue otherwise serves as a - * specific-notification-style monitor holding a single waiting - * thread. The status field does NOT control whether threads are - * granted locks etc though. A thread may try to acquire if it is - * first in the queue. But being first does not guarantee success; - * it only gives the right to contend. So the currently released - * contender thread may need to rewait. - * - *

        To enqueue into a CLH lock, you atomically splice it in as new - * tail. To dequeue, you just set the head field. - *

        -     *      +------+  prev +-----+       +-----+
        -     * head |      | <---- |     | <---- |     |  tail
        -     *      +------+       +-----+       +-----+
        -     * 
        - * - *

        Insertion into a CLH queue requires only a single atomic - * operation on "tail", so there is a simple atomic point of - * demarcation from unqueued to queued. Similarly, dequeuing - * involves only updating the "head". However, it takes a bit - * more work for nodes to determine who their successors are, - * in part to deal with possible cancellation due to timeouts - * and interrupts. - * - *

        The "prev" links (not used in original CLH locks), are mainly - * needed to handle cancellation. If a node is cancelled, its - * successor is (normally) relinked to a non-cancelled - * predecessor. For explanation of similar mechanics in the case - * of spin locks, see the papers by Scott and Scherer at - * http://www.cs.rochester.edu/u/scott/synchronization/ - * - *

        We also use "next" links to implement blocking mechanics. - * The thread id for each node is kept in its own node, so a - * predecessor signals the next node to wake up by traversing - * next link to determine which thread it is. Determination of - * successor must avoid races with newly queued nodes to set - * the "next" fields of their predecessors. This is solved - * when necessary by checking backwards from the atomically - * updated "tail" when a node's successor appears to be null. - * (Or, said differently, the next-links are an optimization - * so that we don't usually need a backward scan.) - * - *

        Cancellation introduces some conservatism to the basic - * algorithms. Since we must poll for cancellation of other - * nodes, we can miss noticing whether a cancelled node is - * ahead or behind us. This is dealt with by always unparking - * successors upon cancellation, allowing them to stabilize on - * a new predecessor, unless we can identify an uncancelled - * predecessor who will carry this responsibility. - * - *

        CLH queues need a dummy header node to get started. But - * we don't create them on construction, because it would be wasted - * effort if there is never contention. Instead, the node - * is constructed and head and tail pointers are set upon first - * contention. - * - *

        Threads waiting on Conditions use the same nodes, but - * use an additional link. Conditions only need to link nodes - * in simple (non-concurrent) linked queues because they are - * only accessed when exclusively held. Upon await, a node is - * inserted into a condition queue. Upon signal, the node is - * transferred to the main queue. A special value of status - * field is used to mark which queue a node is on. - * - *

        Thanks go to Dave Dice, Mark Moir, Victor Luchangco, Bill - * Scherer and Michael Scott, along with members of JSR-166 - * expert group, for helpful ideas, discussions, and critiques - * on the design of this class. - */ - static final class Node { - /** Marker to indicate a node is waiting in shared mode */ - static final Node SHARED = new Node(); - /** Marker to indicate a node is waiting in exclusive mode */ - static final Node EXCLUSIVE = null; - - /** waitStatus value to indicate thread has cancelled */ - static final int CANCELLED = 1; - /** waitStatus value to indicate successor's thread needs unparking */ - static final int SIGNAL = -1; - /** waitStatus value to indicate thread is waiting on condition */ - static final int CONDITION = -2; - /** - * waitStatus value to indicate the next acquireShared should - * unconditionally propagate - */ - static final int PROPAGATE = -3; - - /** - * Status field, taking on only the values: - * SIGNAL: The successor of this node is (or will soon be) - * blocked (via park), so the current node must - * unpark its successor when it releases or - * cancels. To avoid races, acquire methods must - * first indicate they need a signal, - * then retry the atomic acquire, and then, - * on failure, block. - * CANCELLED: This node is cancelled due to timeout or interrupt. - * Nodes never leave this state. In particular, - * a thread with cancelled node never again blocks. - * CONDITION: This node is currently on a condition queue. - * It will not be used as a sync queue node - * until transferred, at which time the status - * will be set to 0. (Use of this value here has - * nothing to do with the other uses of the - * field, but simplifies mechanics.) - * PROPAGATE: A releaseShared should be propagated to other - * nodes. This is set (for head node only) in - * doReleaseShared to ensure propagation - * continues, even if other operations have - * since intervened. - * 0: None of the above - * - * The values are arranged numerically to simplify use. - * Non-negative values mean that a node doesn't need to - * signal. So, most code doesn't need to check for particular - * values, just for sign. - * - * The field is initialized to 0 for normal sync nodes, and - * CONDITION for condition nodes. It is modified using CAS - * (or when possible, unconditional volatile writes). - */ - volatile int waitStatus; - - /** - * Link to predecessor node that current node/thread relies on - * for checking waitStatus. Assigned during enqueuing, and nulled - * out (for sake of GC) only upon dequeuing. Also, upon - * cancellation of a predecessor, we short-circuit while - * finding a non-cancelled one, which will always exist - * because the head node is never cancelled: A node becomes - * head only as a result of successful acquire. A - * cancelled thread never succeeds in acquiring, and a thread only - * cancels itself, not any other node. - */ - volatile Node prev; - - /** - * Link to the successor node that the current node/thread - * unparks upon release. Assigned during enqueuing, adjusted - * when bypassing cancelled predecessors, and nulled out (for - * sake of GC) when dequeued. The enq operation does not - * assign next field of a predecessor until after attachment, - * so seeing a null next field does not necessarily mean that - * node is at end of queue. However, if a next field appears - * to be null, we can scan prev's from the tail to - * double-check. The next field of cancelled nodes is set to - * point to the node itself instead of null, to make life - * easier for isOnSyncQueue. - */ - volatile Node next; - - /** - * The thread that enqueued this node. Initialized on - * construction and nulled out after use. - */ - volatile Thread thread; - - /** - * Link to next node waiting on condition, or the special - * value SHARED. Because condition queues are accessed only - * when holding in exclusive mode, we just need a simple - * linked queue to hold nodes while they are waiting on - * conditions. They are then transferred to the queue to - * re-acquire. And because conditions can only be exclusive, - * we save a field by using special value to indicate shared - * mode. - */ - Node nextWaiter; - - /** - * Returns true if node is waiting in shared mode. - */ - final boolean isShared() { - return nextWaiter == SHARED; - } - - /** - * Returns previous node, or throws NullPointerException if null. - * Use when predecessor cannot be null. The null check could - * be elided, but is present to help the VM. - * - * @return the predecessor of this node - */ - final Node predecessor() throws NullPointerException { - Node p = prev; - if (p == null) - throw new NullPointerException(); - else - return p; - } - - Node() { // Used to establish initial head or SHARED marker - } - - Node(Thread thread, Node mode) { // Used by addWaiter - this.nextWaiter = mode; - this.thread = thread; - } - - Node(Thread thread, int waitStatus) { // Used by Condition - this.waitStatus = waitStatus; - this.thread = thread; - } - } - - /** * Head of the wait queue, lazily initialized. Except for * initialization, it is modified only via method setHead. Note: * If head exists, its waitStatus is guaranteed not to be @@ -325,7 +111,9 @@ * @param newState the new state value */ protected final void setState(long newState) { - state = newState; + // Use putLongVolatile instead of ordinary volatile store when + // using compareAndSwapLong, for sake of some 32bit systems. + U.putLongVolatile(this, STATE, newState); } /** @@ -340,8 +128,7 @@ * value was not equal to the expected value. */ protected final boolean compareAndSetState(long expect, long update) { - // See below for intrinsics setup to support this - return unsafe.compareAndSwapLong(this, stateOffset, expect, update); + return U.compareAndSwapLong(this, STATE, expect, update); } // Queuing utilities @@ -351,25 +138,24 @@ * rather than to use timed park. A rough estimate suffices * to improve responsiveness with very short timeouts. */ - static final long spinForTimeoutThreshold = 1000L; + static final long SPIN_FOR_TIMEOUT_THRESHOLD = 1000L; /** * Inserts node into queue, initializing if necessary. See picture above. * @param node the node to insert * @return node's predecessor */ - private Node enq(final Node node) { + private Node enq(Node node) { for (;;) { - Node t = tail; - if (t == null) { // Must initialize - if (compareAndSetHead(new Node())) - tail = head; + Node oldTail = tail; + if (oldTail != null) { + U.putObject(node, Node.PREV, oldTail); + if (compareAndSetTail(oldTail, node)) { + oldTail.next = node; + return oldTail; + } } else { - node.prev = t; - if (compareAndSetTail(t, node)) { - t.next = node; - return t; - } + initializeSyncQueue(); } } } @@ -381,18 +167,20 @@ * @return the new node */ private Node addWaiter(Node mode) { - Node node = new Node(Thread.currentThread(), mode); - // Try the fast path of enq; backup to full enq on failure - Node pred = tail; - if (pred != null) { - node.prev = pred; - if (compareAndSetTail(pred, node)) { - pred.next = node; - return node; + Node node = new Node(mode); + + for (;;) { + Node oldTail = tail; + if (oldTail != null) { + U.putObject(node, Node.PREV, oldTail); + if (compareAndSetTail(oldTail, node)) { + oldTail.next = node; + return node; + } + } else { + initializeSyncQueue(); } } - enq(node); - return node; } /** @@ -421,7 +209,7 @@ */ int ws = node.waitStatus; if (ws < 0) - compareAndSetWaitStatus(node, ws, 0); + node.compareAndSetWaitStatus(ws, 0); /* * Thread to unpark is held in successor, which is normally @@ -432,9 +220,9 @@ Node s = node.next; if (s == null || s.waitStatus > 0) { s = null; - for (Node t = tail; t != null && t != node; t = t.prev) - if (t.waitStatus <= 0) - s = t; + for (Node p = tail; p != node && p != null; p = p.prev) + if (p.waitStatus <= 0) + s = p; } if (s != null) LockSupport.unpark(s.thread); @@ -462,12 +250,12 @@ if (h != null && h != tail) { int ws = h.waitStatus; if (ws == Node.SIGNAL) { - if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) + if (!h.compareAndSetWaitStatus(Node.SIGNAL, 0)) continue; // loop to recheck cases unparkSuccessor(h); } else if (ws == 0 && - !compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) + !h.compareAndSetWaitStatus(0, Node.PROPAGATE)) continue; // loop on failed CAS } if (h == head) // loop if head changed @@ -541,18 +329,18 @@ // If we are the tail, remove ourselves. if (node == tail && compareAndSetTail(node, pred)) { - compareAndSetNext(pred, predNext, null); + pred.compareAndSetNext(predNext, null); } else { // If successor needs signal, try to set pred's next-link // so it will get one. Otherwise wake it up to propagate. int ws; if (pred != head && ((ws = pred.waitStatus) == Node.SIGNAL || - (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) && + (ws <= 0 && pred.compareAndSetWaitStatus(ws, Node.SIGNAL))) && pred.thread != null) { Node next = node.next; if (next != null && next.waitStatus <= 0) - compareAndSetNext(pred, predNext, next); + pred.compareAndSetNext(predNext, next); } else { unparkSuccessor(node); } @@ -593,7 +381,7 @@ * need a signal, but don't park yet. Caller will need to * retry to make sure it cannot acquire before parking. */ - compareAndSetWaitStatus(pred, ws, Node.SIGNAL); + pred.compareAndSetWaitStatus(ws, Node.SIGNAL); } return false; } @@ -606,7 +394,7 @@ } /** - * Convenience method to park and then check if interrupted + * Convenience method to park and then check if interrupted. * * @return {@code true} if interrupted */ @@ -633,7 +421,6 @@ * @return {@code true} if interrupted while waiting */ final boolean acquireQueued(final Node node, long arg) { - boolean failed = true; try { boolean interrupted = false; for (;;) { @@ -641,16 +428,15 @@ if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC - failed = false; return interrupted; } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } - } finally { - if (failed) - cancelAcquire(node); + } catch (Throwable t) { + cancelAcquire(node); + throw t; } } @@ -661,23 +447,21 @@ private void doAcquireInterruptibly(long arg) throws InterruptedException { final Node node = addWaiter(Node.EXCLUSIVE); - boolean failed = true; try { for (;;) { final Node p = node.predecessor(); if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC - failed = false; return; } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) throw new InterruptedException(); } - } finally { - if (failed) - cancelAcquire(node); + } catch (Throwable t) { + cancelAcquire(node); + throw t; } } @@ -694,28 +478,28 @@ return false; final long deadline = System.nanoTime() + nanosTimeout; final Node node = addWaiter(Node.EXCLUSIVE); - boolean failed = true; try { for (;;) { final Node p = node.predecessor(); if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC - failed = false; return true; } nanosTimeout = deadline - System.nanoTime(); - if (nanosTimeout <= 0L) + if (nanosTimeout <= 0L) { + cancelAcquire(node); return false; + } if (shouldParkAfterFailedAcquire(p, node) && - nanosTimeout > spinForTimeoutThreshold) + nanosTimeout > SPIN_FOR_TIMEOUT_THRESHOLD) LockSupport.parkNanos(this, nanosTimeout); if (Thread.interrupted()) throw new InterruptedException(); } - } finally { - if (failed) - cancelAcquire(node); + } catch (Throwable t) { + cancelAcquire(node); + throw t; } } @@ -725,7 +509,6 @@ */ private void doAcquireShared(long arg) { final Node node = addWaiter(Node.SHARED); - boolean failed = true; try { boolean interrupted = false; for (;;) { @@ -737,7 +520,6 @@ p.next = null; // help GC if (interrupted) selfInterrupt(); - failed = false; return; } } @@ -745,9 +527,9 @@ parkAndCheckInterrupt()) interrupted = true; } - } finally { - if (failed) - cancelAcquire(node); + } catch (Throwable t) { + cancelAcquire(node); + throw t; } } @@ -758,7 +540,6 @@ private void doAcquireSharedInterruptibly(long arg) throws InterruptedException { final Node node = addWaiter(Node.SHARED); - boolean failed = true; try { for (;;) { final Node p = node.predecessor(); @@ -767,7 +548,6 @@ if (r >= 0) { setHeadAndPropagate(node, r); p.next = null; // help GC - failed = false; return; } } @@ -775,9 +555,9 @@ parkAndCheckInterrupt()) throw new InterruptedException(); } - } finally { - if (failed) - cancelAcquire(node); + } catch (Throwable t) { + cancelAcquire(node); + throw t; } } @@ -794,7 +574,6 @@ return false; final long deadline = System.nanoTime() + nanosTimeout; final Node node = addWaiter(Node.SHARED); - boolean failed = true; try { for (;;) { final Node p = node.predecessor(); @@ -803,22 +582,23 @@ if (r >= 0) { setHeadAndPropagate(node, r); p.next = null; // help GC - failed = false; return true; } } nanosTimeout = deadline - System.nanoTime(); - if (nanosTimeout <= 0L) + if (nanosTimeout <= 0L) { + cancelAcquire(node); return false; + } if (shouldParkAfterFailedAcquire(p, node) && - nanosTimeout > spinForTimeoutThreshold) + nanosTimeout > SPIN_FOR_TIMEOUT_THRESHOLD) LockSupport.parkNanos(this, nanosTimeout); if (Thread.interrupted()) throw new InterruptedException(); } - } finally { - if (failed) - cancelAcquire(node); + } catch (Throwable t) { + cancelAcquire(node); + throw t; } } @@ -1170,7 +950,7 @@ } /** - * Version of getFirstQueuedThread called when fastpath fails + * Version of getFirstQueuedThread called when fastpath fails. */ private Thread fullGetFirstQueuedThread() { /* @@ -1250,7 +1030,7 @@ * *

        An invocation of this method is equivalent to (but may be * more efficient than): - *

         {@code
        +     * 
         {@code
              * getFirstQueuedThread() != Thread.currentThread() &&
              * hasQueuedThreads()}
        * @@ -1270,7 +1050,7 @@ * tryAcquire} method for a fair, reentrant, exclusive mode * synchronizer might look like this: * - *
         {@code
        +     * 
         {@code
              * protected boolean tryAcquire(int arg) {
              *   if (isHeldExclusively()) {
              *     // A reentrant acquire; increment hold count
        @@ -1306,8 +1086,7 @@
              * acquire.  The value is only an estimate because the number of
              * threads may change dynamically while this method traverses
              * internal data structures.  This method is designed for use in
        -     * monitoring system state, not for synchronization
        -     * control.
        +     * monitoring system state, not for synchronization control.
              *
              * @return the estimated number of threads waiting to acquire
              */
        @@ -1332,7 +1111,7 @@
              * @return the collection of threads
              */
             public final Collection getQueuedThreads() {
        -        ArrayList list = new ArrayList();
        +        ArrayList list = new ArrayList<>();
                 for (Node p = tail; p != null; p = p.prev) {
                     Thread t = p.thread;
                     if (t != null)
        @@ -1350,7 +1129,7 @@
              * @return the collection of threads
              */
             public final Collection getExclusiveQueuedThreads() {
        -        ArrayList list = new ArrayList();
        +        ArrayList list = new ArrayList<>();
                 for (Node p = tail; p != null; p = p.prev) {
                     if (!p.isShared()) {
                         Thread t = p.thread;
        @@ -1370,7 +1149,7 @@
              * @return the collection of threads
              */
             public final Collection getSharedQueuedThreads() {
        -        ArrayList list = new ArrayList();
        +        ArrayList list = new ArrayList<>();
                 for (Node p = tail; p != null; p = p.prev) {
                     if (p.isShared()) {
                         Thread t = p.thread;
        @@ -1391,10 +1170,9 @@
              * @return a string identifying this synchronizer, as well as its state
              */
             public String toString() {
        -        long s = getState();
        -        String q  = hasQueuedThreads() ? "non" : "";
        -        return super.toString() +
        -            "[State = " + s + ", " + q + "empty queue]";
        +        return super.toString()
        +            + "[State = " + getState() + ", "
        +            + (hasQueuedThreads() ? "non" : "") + "empty queue]";
             }
         
         
        @@ -1428,13 +1206,15 @@
              * @return true if present
              */
             private boolean findNodeFromTail(Node node) {
        -        Node t = tail;
        -        for (;;) {
        -            if (t == node)
        +        // We check for node first, since it's likely to be at or near tail.
        +        // tail is known to be non-null, so we could re-order to "save"
        +        // one null check, but we leave it this way to help the VM.
        +        for (Node p = tail;;) {
        +            if (p == node)
                         return true;
        -            if (t == null)
        +            if (p == null)
                         return false;
        -            t = t.prev;
        +            p = p.prev;
                 }
             }
         
        @@ -1449,7 +1229,7 @@
                 /*
                  * If cannot change waitStatus, the node has been cancelled.
                  */
        -        if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
        +        if (!node.compareAndSetWaitStatus(Node.CONDITION, 0))
                     return false;
         
                 /*
        @@ -1460,7 +1240,7 @@
                  */
                 Node p = enq(node);
                 int ws = p.waitStatus;
        -        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
        +        if (ws > 0 || !p.compareAndSetWaitStatus(ws, Node.SIGNAL))
                     LockSupport.unpark(node.thread);
                 return true;
             }
        @@ -1473,7 +1253,7 @@
              * @return true if cancelled before the node was signalled
              */
             final boolean transferAfterCancelledWait(Node node) {
        -        if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
        +        if (node.compareAndSetWaitStatus(Node.CONDITION, 0)) {
                     enq(node);
                     return true;
                 }
        @@ -1495,18 +1275,14 @@
              * @return previous sync state
              */
             final long fullyRelease(Node node) {
        -        boolean failed = true;
                 try {
                     long savedState = getState();
        -            if (release(savedState)) {
        -                failed = false;
        +            if (release(savedState))
                         return savedState;
        -            } else {
        -                throw new IllegalMonitorStateException();
        -            }
        -        } finally {
        -            if (failed)
        -                node.waitStatus = Node.CANCELLED;
        +            throw new IllegalMonitorStateException();
        +        } catch (Throwable t) {
        +            node.waitStatus = Node.CANCELLED;
        +            throw t;
                 }
             }
         
        @@ -1551,8 +1327,8 @@
              * given condition associated with this synchronizer. Note that
              * because timeouts and interrupts may occur at any time, the
              * estimate serves only as an upper bound on the actual number of
        -     * waiters.  This method is designed for use in monitoring of the
        -     * system state, not for synchronization control.
        +     * waiters.  This method is designed for use in monitoring system
        +     * state, not for synchronization control.
              *
              * @param condition the condition
              * @return the estimated number of waiting threads
        @@ -1632,7 +1408,9 @@
                         unlinkCancelledWaiters();
                         t = lastWaiter;
                     }
        -            Node node = new Node(Thread.currentThread(), Node.CONDITION);
        +
        +            Node node = new Node(Node.CONDITION);
        +
                     if (t == null)
                         firstWaiter = node;
                     else
        @@ -1740,12 +1518,12 @@
                 /**
                  * Implements uninterruptible condition wait.
                  * 
          - *
        1. Save lock state returned by {@link #getState}. - *
        2. Invoke {@link #release} with saved state as argument, - * throwing IllegalMonitorStateException if it fails. - *
        3. Block until signalled. - *
        4. Reacquire by invoking specialized version of - * {@link #acquire} with saved state as argument. + *
        5. Save lock state returned by {@link #getState}. + *
        6. Invoke {@link #release} with saved state as argument, + * throwing IllegalMonitorStateException if it fails. + *
        7. Block until signalled. + *
        8. Reacquire by invoking specialized version of + * {@link #acquire} with saved state as argument. *
        */ public final void awaitUninterruptibly() { @@ -1799,14 +1577,14 @@ /** * Implements interruptible condition wait. *
          - *
        1. If current thread is interrupted, throw InterruptedException. - *
        2. Save lock state returned by {@link #getState}. - *
        3. Invoke {@link #release} with saved state as argument, - * throwing IllegalMonitorStateException if it fails. - *
        4. Block until signalled or interrupted. - *
        5. Reacquire by invoking specialized version of - * {@link #acquire} with saved state as argument. - *
        6. If interrupted while blocked in step 4, throw InterruptedException. + *
        7. If current thread is interrupted, throw InterruptedException. + *
        8. Save lock state returned by {@link #getState}. + *
        9. Invoke {@link #release} with saved state as argument, + * throwing IllegalMonitorStateException if it fails. + *
        10. Block until signalled or interrupted. + *
        11. Reacquire by invoking specialized version of + * {@link #acquire} with saved state as argument. + *
        12. If interrupted while blocked in step 4, throw InterruptedException. *
        */ public final void await() throws InterruptedException { @@ -1831,30 +1609,33 @@ /** * Implements timed condition wait. *
          - *
        1. If current thread is interrupted, throw InterruptedException. - *
        2. Save lock state returned by {@link #getState}. - *
        3. Invoke {@link #release} with saved state as argument, - * throwing IllegalMonitorStateException if it fails. - *
        4. Block until signalled, interrupted, or timed out. - *
        5. Reacquire by invoking specialized version of - * {@link #acquire} with saved state as argument. - *
        6. If interrupted while blocked in step 4, throw InterruptedException. + *
        7. If current thread is interrupted, throw InterruptedException. + *
        8. Save lock state returned by {@link #getState}. + *
        9. Invoke {@link #release} with saved state as argument, + * throwing IllegalMonitorStateException if it fails. + *
        10. Block until signalled, interrupted, or timed out. + *
        11. Reacquire by invoking specialized version of + * {@link #acquire} with saved state as argument. + *
        12. If interrupted while blocked in step 4, throw InterruptedException. *
        */ public final long awaitNanos(long nanosTimeout) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); + // We don't check for nanosTimeout <= 0L here, to allow + // awaitNanos(0) as a way to "yield the lock". + final long deadline = System.nanoTime() + nanosTimeout; + long initialNanos = nanosTimeout; Node node = addConditionWaiter(); long savedState = fullyRelease(node); - final long deadline = System.nanoTime() + nanosTimeout; int interruptMode = 0; while (!isOnSyncQueue(node)) { if (nanosTimeout <= 0L) { transferAfterCancelledWait(node); break; } - if (nanosTimeout >= spinForTimeoutThreshold) + if (nanosTimeout >= SPIN_FOR_TIMEOUT_THRESHOLD) LockSupport.parkNanos(this, nanosTimeout); if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) break; @@ -1866,21 +1647,22 @@ unlinkCancelledWaiters(); if (interruptMode != 0) reportInterruptAfterWait(interruptMode); - return deadline - System.nanoTime(); + long remaining = deadline - System.nanoTime(); // avoid overflow + return (remaining <= initialNanos) ? remaining : Long.MIN_VALUE; } /** * Implements absolute timed condition wait. *
          - *
        1. If current thread is interrupted, throw InterruptedException. - *
        2. Save lock state returned by {@link #getState}. - *
        3. Invoke {@link #release} with saved state as argument, - * throwing IllegalMonitorStateException if it fails. - *
        4. Block until signalled, interrupted, or timed out. - *
        5. Reacquire by invoking specialized version of - * {@link #acquire} with saved state as argument. - *
        6. If interrupted while blocked in step 4, throw InterruptedException. - *
        7. If timed out while blocked in step 4, return false, else true. + *
        8. If current thread is interrupted, throw InterruptedException. + *
        9. Save lock state returned by {@link #getState}. + *
        10. Invoke {@link #release} with saved state as argument, + * throwing IllegalMonitorStateException if it fails. + *
        11. Block until signalled, interrupted, or timed out. + *
        12. Reacquire by invoking specialized version of + * {@link #acquire} with saved state as argument. + *
        13. If interrupted while blocked in step 4, throw InterruptedException. + *
        14. If timed out while blocked in step 4, return false, else true. *
        */ public final boolean awaitUntil(Date deadline) @@ -1893,7 +1675,7 @@ boolean timedout = false; int interruptMode = 0; while (!isOnSyncQueue(node)) { - if (System.currentTimeMillis() > abstime) { + if (System.currentTimeMillis() >= abstime) { timedout = transferAfterCancelledWait(node); break; } @@ -1913,15 +1695,15 @@ /** * Implements timed condition wait. *
          - *
        1. If current thread is interrupted, throw InterruptedException. - *
        2. Save lock state returned by {@link #getState}. - *
        3. Invoke {@link #release} with saved state as argument, - * throwing IllegalMonitorStateException if it fails. - *
        4. Block until signalled, interrupted, or timed out. - *
        5. Reacquire by invoking specialized version of - * {@link #acquire} with saved state as argument. - *
        6. If interrupted while blocked in step 4, throw InterruptedException. - *
        7. If timed out while blocked in step 4, return false, else true. + *
        8. If current thread is interrupted, throw InterruptedException. + *
        9. Save lock state returned by {@link #getState}. + *
        10. Invoke {@link #release} with saved state as argument, + * throwing IllegalMonitorStateException if it fails. + *
        11. Block until signalled, interrupted, or timed out. + *
        12. Reacquire by invoking specialized version of + * {@link #acquire} with saved state as argument. + *
        13. If interrupted while blocked in step 4, throw InterruptedException. + *
        14. If timed out while blocked in step 4, return false, else true. *
        */ public final boolean await(long time, TimeUnit unit) @@ -1929,9 +1711,11 @@ long nanosTimeout = unit.toNanos(time); if (Thread.interrupted()) throw new InterruptedException(); + // We don't check for nanosTimeout <= 0L here, to allow + // await(0, unit) as a way to "yield the lock". + final long deadline = System.nanoTime() + nanosTimeout; Node node = addConditionWaiter(); long savedState = fullyRelease(node); - final long deadline = System.nanoTime() + nanosTimeout; boolean timedout = false; int interruptMode = 0; while (!isOnSyncQueue(node)) { @@ -1939,7 +1723,7 @@ timedout = transferAfterCancelledWait(node); break; } - if (nanosTimeout >= spinForTimeoutThreshold) + if (nanosTimeout >= SPIN_FOR_TIMEOUT_THRESHOLD) LockSupport.parkNanos(this, nanosTimeout); if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) break; @@ -2016,7 +1800,7 @@ protected final Collection getWaitingThreads() { if (!isHeldExclusively()) throw new IllegalMonitorStateException(); - ArrayList list = new ArrayList(); + ArrayList list = new ArrayList<>(); for (Node w = firstWaiter; w != null; w = w.nextWaiter) { if (w.waitStatus == Node.CONDITION) { Thread t = w.thread; @@ -2037,59 +1821,40 @@ * are at it, we do the same for other CASable fields (which could * otherwise be done with atomic field updaters). */ - private static final Unsafe unsafe = Unsafe.getUnsafe(); - private static final long stateOffset; - private static final long headOffset; - private static final long tailOffset; - private static final long waitStatusOffset; - private static final long nextOffset; + private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe(); + private static final long STATE; + private static final long HEAD; + private static final long TAIL; static { try { - stateOffset = unsafe.objectFieldOffset + STATE = U.objectFieldOffset (AbstractQueuedLongSynchronizer.class.getDeclaredField("state")); - headOffset = unsafe.objectFieldOffset + HEAD = U.objectFieldOffset (AbstractQueuedLongSynchronizer.class.getDeclaredField("head")); - tailOffset = unsafe.objectFieldOffset + TAIL = U.objectFieldOffset (AbstractQueuedLongSynchronizer.class.getDeclaredField("tail")); - waitStatusOffset = unsafe.objectFieldOffset - (Node.class.getDeclaredField("waitStatus")); - nextOffset = unsafe.objectFieldOffset - (Node.class.getDeclaredField("next")); + } catch (ReflectiveOperationException e) { + throw new Error(e); + } - } catch (Exception ex) { throw new Error(ex); } + // Reduce the risk of rare disastrous classloading in first call to + // LockSupport.park: https://bugs.openjdk.java.net/browse/JDK-8074773 + Class ensureLoaded = LockSupport.class; } /** - * CAS head field. Used only by enq. + * Initializes head and tail fields on first contention. */ - private final boolean compareAndSetHead(Node update) { - return unsafe.compareAndSwapObject(this, headOffset, null, update); - } - - /** - * CAS tail field. Used only by enq. - */ - private final boolean compareAndSetTail(Node expect, Node update) { - return unsafe.compareAndSwapObject(this, tailOffset, expect, update); + private final void initializeSyncQueue() { + if (U.compareAndSwapObject(this, HEAD, null, new Node())) + tail = head; } /** - * CAS waitStatus field of a node. + * CASes tail field. */ - private static final boolean compareAndSetWaitStatus(Node node, - int expect, - int update) { - return unsafe.compareAndSwapInt(node, waitStatusOffset, - expect, update); - } - - /** - * CAS next field of a node. - */ - private static final boolean compareAndSetNext(Node node, - Node expect, - Node update) { - return unsafe.compareAndSwapObject(node, nextOffset, expect, update); + private final boolean compareAndSetTail(Node expect, Node update) { + return U.compareAndSwapObject(this, TAIL, expect, update); } } diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java Wed Jul 05 20:54:48 2017 +0200 @@ -34,11 +34,11 @@ */ package java.util.concurrent.locks; -import java.util.concurrent.TimeUnit; + import java.util.ArrayList; import java.util.Collection; import java.util.Date; -import sun.misc.Unsafe; +import java.util.concurrent.TimeUnit; /** * Provides a framework for implementing blocking locks and related @@ -110,11 +110,11 @@ * #setState} and/or {@link #compareAndSetState}: * *
          - *
        • {@link #tryAcquire} - *
        • {@link #tryRelease} - *
        • {@link #tryAcquireShared} - *
        • {@link #tryReleaseShared} - *
        • {@link #isHeldExclusively} + *
        • {@link #tryAcquire} + *
        • {@link #tryRelease} + *
        • {@link #tryAcquireShared} + *
        • {@link #tryReleaseShared} + *
        • {@link #isHeldExclusively} *
        * * Each of these methods by default throws {@link @@ -195,7 +195,7 @@ * It also supports conditions and exposes * one of the instrumentation methods: * - *
         {@code
        + * 
         {@code
          * class Mutex implements Lock, java.io.Serializable {
          *
          *   // Our internal helper class
        @@ -259,7 +259,7 @@
          * fire. Because a latch is non-exclusive, it uses the {@code shared}
          * acquire and release methods.
          *
        - *  
         {@code
        + * 
         {@code
          * class BooleanLatch {
          *
          *   private static class Sync extends AbstractQueuedSynchronizer {
        @@ -383,15 +383,15 @@
                 /** Marker to indicate a node is waiting in exclusive mode */
                 static final Node EXCLUSIVE = null;
         
        -        /** waitStatus value to indicate thread has cancelled */
        +        /** waitStatus value to indicate thread has cancelled. */
                 static final int CANCELLED =  1;
        -        /** waitStatus value to indicate successor's thread needs unparking */
        +        /** waitStatus value to indicate successor's thread needs unparking. */
                 static final int SIGNAL    = -1;
        -        /** waitStatus value to indicate thread is waiting on condition */
        +        /** waitStatus value to indicate thread is waiting on condition. */
                 static final int CONDITION = -2;
                 /**
                  * waitStatus value to indicate the next acquireShared should
        -         * unconditionally propagate
        +         * unconditionally propagate.
                  */
                 static final int PROPAGATE = -3;
         
        @@ -499,17 +499,49 @@
                         return p;
                 }
         
        -        Node() {    // Used to establish initial head or SHARED marker
        +        /** Establishes initial head or SHARED marker. */
        +        Node() {}
        +
        +        /** Constructor used by addWaiter. */
        +        Node(Node nextWaiter) {
        +            this.nextWaiter = nextWaiter;
        +            U.putObject(this, THREAD, Thread.currentThread());
        +        }
        +
        +        /** Constructor used by addConditionWaiter. */
        +        Node(int waitStatus) {
        +            U.putInt(this, WAITSTATUS, waitStatus);
        +            U.putObject(this, THREAD, Thread.currentThread());
        +        }
        +
        +        /** CASes waitStatus field. */
        +        final boolean compareAndSetWaitStatus(int expect, int update) {
        +            return U.compareAndSwapInt(this, WAITSTATUS, expect, update);
                 }
         
        -        Node(Thread thread, Node mode) {     // Used by addWaiter
        -            this.nextWaiter = mode;
        -            this.thread = thread;
        +        /** CASes next field. */
        +        final boolean compareAndSetNext(Node expect, Node update) {
        +            return U.compareAndSwapObject(this, NEXT, expect, update);
                 }
         
        -        Node(Thread thread, int waitStatus) { // Used by Condition
        -            this.waitStatus = waitStatus;
        -            this.thread = thread;
        +        private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
        +        private static final long NEXT;
        +        static final long PREV;
        +        private static final long THREAD;
        +        private static final long WAITSTATUS;
        +        static {
        +            try {
        +                NEXT = U.objectFieldOffset
        +                    (Node.class.getDeclaredField("next"));
        +                PREV = U.objectFieldOffset
        +                    (Node.class.getDeclaredField("prev"));
        +                THREAD = U.objectFieldOffset
        +                    (Node.class.getDeclaredField("thread"));
        +                WAITSTATUS = U.objectFieldOffset
        +                    (Node.class.getDeclaredField("waitStatus"));
        +            } catch (ReflectiveOperationException e) {
        +                throw new Error(e);
        +            }
                 }
             }
         
        @@ -562,8 +594,7 @@
              *         value was not equal to the expected value.
              */
             protected final boolean compareAndSetState(int expect, int update) {
        -        // See below for intrinsics setup to support this
        -        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
        +        return U.compareAndSwapInt(this, STATE, expect, update);
             }
         
             // Queuing utilities
        @@ -573,25 +604,24 @@
              * rather than to use timed park. A rough estimate suffices
              * to improve responsiveness with very short timeouts.
              */
        -    static final long spinForTimeoutThreshold = 1000L;
        +    static final long SPIN_FOR_TIMEOUT_THRESHOLD = 1000L;
         
             /**
              * Inserts node into queue, initializing if necessary. See picture above.
              * @param node the node to insert
              * @return node's predecessor
              */
        -    private Node enq(final Node node) {
        +    private Node enq(Node node) {
                 for (;;) {
        -            Node t = tail;
        -            if (t == null) { // Must initialize
        -                if (compareAndSetHead(new Node()))
        -                    tail = head;
        +            Node oldTail = tail;
        +            if (oldTail != null) {
        +                U.putObject(node, Node.PREV, oldTail);
        +                if (compareAndSetTail(oldTail, node)) {
        +                    oldTail.next = node;
        +                    return oldTail;
        +                }
                     } else {
        -                node.prev = t;
        -                if (compareAndSetTail(t, node)) {
        -                    t.next = node;
        -                    return t;
        -                }
        +                initializeSyncQueue();
                     }
                 }
             }
        @@ -603,18 +633,20 @@
              * @return the new node
              */
             private Node addWaiter(Node mode) {
        -        Node node = new Node(Thread.currentThread(), mode);
        -        // Try the fast path of enq; backup to full enq on failure
        -        Node pred = tail;
        -        if (pred != null) {
        -            node.prev = pred;
        -            if (compareAndSetTail(pred, node)) {
        -                pred.next = node;
        -                return node;
        +        Node node = new Node(mode);
        +
        +        for (;;) {
        +            Node oldTail = tail;
        +            if (oldTail != null) {
        +                U.putObject(node, Node.PREV, oldTail);
        +                if (compareAndSetTail(oldTail, node)) {
        +                    oldTail.next = node;
        +                    return node;
        +                }
        +            } else {
        +                initializeSyncQueue();
                     }
                 }
        -        enq(node);
        -        return node;
             }
         
             /**
        @@ -643,7 +675,7 @@
                  */
                 int ws = node.waitStatus;
                 if (ws < 0)
        -            compareAndSetWaitStatus(node, ws, 0);
        +            node.compareAndSetWaitStatus(ws, 0);
         
                 /*
                  * Thread to unpark is held in successor, which is normally
        @@ -654,9 +686,9 @@
                 Node s = node.next;
                 if (s == null || s.waitStatus > 0) {
                     s = null;
        -            for (Node t = tail; t != null && t != node; t = t.prev)
        -                if (t.waitStatus <= 0)
        -                    s = t;
        +            for (Node p = tail; p != node && p != null; p = p.prev)
        +                if (p.waitStatus <= 0)
        +                    s = p;
                 }
                 if (s != null)
                     LockSupport.unpark(s.thread);
        @@ -684,12 +716,12 @@
                     if (h != null && h != tail) {
                         int ws = h.waitStatus;
                         if (ws == Node.SIGNAL) {
        -                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
        +                    if (!h.compareAndSetWaitStatus(Node.SIGNAL, 0))
                                 continue;            // loop to recheck cases
                             unparkSuccessor(h);
                         }
                         else if (ws == 0 &&
        -                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
        +                         !h.compareAndSetWaitStatus(0, Node.PROPAGATE))
                             continue;                // loop on failed CAS
                     }
                     if (h == head)                   // loop if head changed
        @@ -763,18 +795,18 @@
         
                 // If we are the tail, remove ourselves.
                 if (node == tail && compareAndSetTail(node, pred)) {
        -            compareAndSetNext(pred, predNext, null);
        +            pred.compareAndSetNext(predNext, null);
                 } else {
                     // If successor needs signal, try to set pred's next-link
                     // so it will get one. Otherwise wake it up to propagate.
                     int ws;
                     if (pred != head &&
                         ((ws = pred.waitStatus) == Node.SIGNAL ||
        -                 (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
        +                 (ws <= 0 && pred.compareAndSetWaitStatus(ws, Node.SIGNAL))) &&
                         pred.thread != null) {
                         Node next = node.next;
                         if (next != null && next.waitStatus <= 0)
        -                    compareAndSetNext(pred, predNext, next);
        +                    pred.compareAndSetNext(predNext, next);
                     } else {
                         unparkSuccessor(node);
                     }
        @@ -815,7 +847,7 @@
                      * need a signal, but don't park yet.  Caller will need to
                      * retry to make sure it cannot acquire before parking.
                      */
        -            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        +            pred.compareAndSetWaitStatus(ws, Node.SIGNAL);
                 }
                 return false;
             }
        @@ -828,7 +860,7 @@
             }
         
             /**
        -     * Convenience method to park and then check if interrupted
        +     * Convenience method to park and then check if interrupted.
              *
              * @return {@code true} if interrupted
              */
        @@ -855,7 +887,6 @@
              * @return {@code true} if interrupted while waiting
              */
             final boolean acquireQueued(final Node node, int arg) {
        -        boolean failed = true;
                 try {
                     boolean interrupted = false;
                     for (;;) {
        @@ -863,16 +894,15 @@
                         if (p == head && tryAcquire(arg)) {
                             setHead(node);
                             p.next = null; // help GC
        -                    failed = false;
                             return interrupted;
                         }
                         if (shouldParkAfterFailedAcquire(p, node) &&
                             parkAndCheckInterrupt())
                             interrupted = true;
                     }
        -        } finally {
        -            if (failed)
        -                cancelAcquire(node);
        +        } catch (Throwable t) {
        +            cancelAcquire(node);
        +            throw t;
                 }
             }
         
        @@ -883,23 +913,21 @@
             private void doAcquireInterruptibly(int arg)
                 throws InterruptedException {
                 final Node node = addWaiter(Node.EXCLUSIVE);
        -        boolean failed = true;
                 try {
                     for (;;) {
                         final Node p = node.predecessor();
                         if (p == head && tryAcquire(arg)) {
                             setHead(node);
                             p.next = null; // help GC
        -                    failed = false;
                             return;
                         }
                         if (shouldParkAfterFailedAcquire(p, node) &&
                             parkAndCheckInterrupt())
                             throw new InterruptedException();
                     }
        -        } finally {
        -            if (failed)
        -                cancelAcquire(node);
        +        } catch (Throwable t) {
        +            cancelAcquire(node);
        +            throw t;
                 }
             }
         
        @@ -916,28 +944,28 @@
                     return false;
                 final long deadline = System.nanoTime() + nanosTimeout;
                 final Node node = addWaiter(Node.EXCLUSIVE);
        -        boolean failed = true;
                 try {
                     for (;;) {
                         final Node p = node.predecessor();
                         if (p == head && tryAcquire(arg)) {
                             setHead(node);
                             p.next = null; // help GC
        -                    failed = false;
                             return true;
                         }
                         nanosTimeout = deadline - System.nanoTime();
        -                if (nanosTimeout <= 0L)
        +                if (nanosTimeout <= 0L) {
        +                    cancelAcquire(node);
                             return false;
        +                }
                         if (shouldParkAfterFailedAcquire(p, node) &&
        -                    nanosTimeout > spinForTimeoutThreshold)
        +                    nanosTimeout > SPIN_FOR_TIMEOUT_THRESHOLD)
                             LockSupport.parkNanos(this, nanosTimeout);
                         if (Thread.interrupted())
                             throw new InterruptedException();
                     }
        -        } finally {
        -            if (failed)
        -                cancelAcquire(node);
        +        } catch (Throwable t) {
        +            cancelAcquire(node);
        +            throw t;
                 }
             }
         
        @@ -947,7 +975,6 @@
              */
             private void doAcquireShared(int arg) {
                 final Node node = addWaiter(Node.SHARED);
        -        boolean failed = true;
                 try {
                     boolean interrupted = false;
                     for (;;) {
        @@ -959,7 +986,6 @@
                                 p.next = null; // help GC
                                 if (interrupted)
                                     selfInterrupt();
        -                        failed = false;
                                 return;
                             }
                         }
        @@ -967,9 +993,9 @@
                             parkAndCheckInterrupt())
                             interrupted = true;
                     }
        -        } finally {
        -            if (failed)
        -                cancelAcquire(node);
        +        } catch (Throwable t) {
        +            cancelAcquire(node);
        +            throw t;
                 }
             }
         
        @@ -980,7 +1006,6 @@
             private void doAcquireSharedInterruptibly(int arg)
                 throws InterruptedException {
                 final Node node = addWaiter(Node.SHARED);
        -        boolean failed = true;
                 try {
                     for (;;) {
                         final Node p = node.predecessor();
        @@ -989,7 +1014,6 @@
                             if (r >= 0) {
                                 setHeadAndPropagate(node, r);
                                 p.next = null; // help GC
        -                        failed = false;
                                 return;
                             }
                         }
        @@ -997,9 +1021,9 @@
                             parkAndCheckInterrupt())
                             throw new InterruptedException();
                     }
        -        } finally {
        -            if (failed)
        -                cancelAcquire(node);
        +        } catch (Throwable t) {
        +            cancelAcquire(node);
        +            throw t;
                 }
             }
         
        @@ -1016,7 +1040,6 @@
                     return false;
                 final long deadline = System.nanoTime() + nanosTimeout;
                 final Node node = addWaiter(Node.SHARED);
        -        boolean failed = true;
                 try {
                     for (;;) {
                         final Node p = node.predecessor();
        @@ -1025,22 +1048,23 @@
                             if (r >= 0) {
                                 setHeadAndPropagate(node, r);
                                 p.next = null; // help GC
        -                        failed = false;
                                 return true;
                             }
                         }
                         nanosTimeout = deadline - System.nanoTime();
        -                if (nanosTimeout <= 0L)
        +                if (nanosTimeout <= 0L) {
        +                    cancelAcquire(node);
                             return false;
        +                }
                         if (shouldParkAfterFailedAcquire(p, node) &&
        -                    nanosTimeout > spinForTimeoutThreshold)
        +                    nanosTimeout > SPIN_FOR_TIMEOUT_THRESHOLD)
                             LockSupport.parkNanos(this, nanosTimeout);
                         if (Thread.interrupted())
                             throw new InterruptedException();
                     }
        -        } finally {
        -            if (failed)
        -                cancelAcquire(node);
        +        } catch (Throwable t) {
        +            cancelAcquire(node);
        +            throw t;
                 }
             }
         
        @@ -1392,7 +1416,7 @@
             }
         
             /**
        -     * Version of getFirstQueuedThread called when fastpath fails
        +     * Version of getFirstQueuedThread called when fastpath fails.
              */
             private Thread fullGetFirstQueuedThread() {
                 /*
        @@ -1472,7 +1496,7 @@
              *
              * 

        An invocation of this method is equivalent to (but may be * more efficient than): - *

         {@code
        +     * 
         {@code
              * getFirstQueuedThread() != Thread.currentThread() &&
              * hasQueuedThreads()}
        * @@ -1492,7 +1516,7 @@ * tryAcquire} method for a fair, reentrant, exclusive mode * synchronizer might look like this: * - *
         {@code
        +     * 
         {@code
              * protected boolean tryAcquire(int arg) {
              *   if (isHeldExclusively()) {
              *     // A reentrant acquire; increment hold count
        @@ -1528,8 +1552,7 @@
              * acquire.  The value is only an estimate because the number of
              * threads may change dynamically while this method traverses
              * internal data structures.  This method is designed for use in
        -     * monitoring system state, not for synchronization
        -     * control.
        +     * monitoring system state, not for synchronization control.
              *
              * @return the estimated number of threads waiting to acquire
              */
        @@ -1554,7 +1577,7 @@
              * @return the collection of threads
              */
             public final Collection getQueuedThreads() {
        -        ArrayList list = new ArrayList();
        +        ArrayList list = new ArrayList<>();
                 for (Node p = tail; p != null; p = p.prev) {
                     Thread t = p.thread;
                     if (t != null)
        @@ -1572,7 +1595,7 @@
              * @return the collection of threads
              */
             public final Collection getExclusiveQueuedThreads() {
        -        ArrayList list = new ArrayList();
        +        ArrayList list = new ArrayList<>();
                 for (Node p = tail; p != null; p = p.prev) {
                     if (!p.isShared()) {
                         Thread t = p.thread;
        @@ -1592,7 +1615,7 @@
              * @return the collection of threads
              */
             public final Collection getSharedQueuedThreads() {
        -        ArrayList list = new ArrayList();
        +        ArrayList list = new ArrayList<>();
                 for (Node p = tail; p != null; p = p.prev) {
                     if (p.isShared()) {
                         Thread t = p.thread;
        @@ -1613,10 +1636,9 @@
              * @return a string identifying this synchronizer, as well as its state
              */
             public String toString() {
        -        int s = getState();
        -        String q  = hasQueuedThreads() ? "non" : "";
        -        return super.toString() +
        -            "[State = " + s + ", " + q + "empty queue]";
        +        return super.toString()
        +            + "[State = " + getState() + ", "
        +            + (hasQueuedThreads() ? "non" : "") + "empty queue]";
             }
         
         
        @@ -1650,13 +1672,15 @@
              * @return true if present
              */
             private boolean findNodeFromTail(Node node) {
        -        Node t = tail;
        -        for (;;) {
        -            if (t == node)
        +        // We check for node first, since it's likely to be at or near tail.
        +        // tail is known to be non-null, so we could re-order to "save"
        +        // one null check, but we leave it this way to help the VM.
        +        for (Node p = tail;;) {
        +            if (p == node)
                         return true;
        -            if (t == null)
        +            if (p == null)
                         return false;
        -            t = t.prev;
        +            p = p.prev;
                 }
             }
         
        @@ -1671,7 +1695,7 @@
                 /*
                  * If cannot change waitStatus, the node has been cancelled.
                  */
        -        if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
        +        if (!node.compareAndSetWaitStatus(Node.CONDITION, 0))
                     return false;
         
                 /*
        @@ -1682,7 +1706,7 @@
                  */
                 Node p = enq(node);
                 int ws = p.waitStatus;
        -        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
        +        if (ws > 0 || !p.compareAndSetWaitStatus(ws, Node.SIGNAL))
                     LockSupport.unpark(node.thread);
                 return true;
             }
        @@ -1695,7 +1719,7 @@
              * @return true if cancelled before the node was signalled
              */
             final boolean transferAfterCancelledWait(Node node) {
        -        if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
        +        if (node.compareAndSetWaitStatus(Node.CONDITION, 0)) {
                     enq(node);
                     return true;
                 }
        @@ -1717,18 +1741,14 @@
              * @return previous sync state
              */
             final int fullyRelease(Node node) {
        -        boolean failed = true;
                 try {
                     int savedState = getState();
        -            if (release(savedState)) {
        -                failed = false;
        +            if (release(savedState))
                         return savedState;
        -            } else {
        -                throw new IllegalMonitorStateException();
        -            }
        -        } finally {
        -            if (failed)
        -                node.waitStatus = Node.CANCELLED;
        +            throw new IllegalMonitorStateException();
        +        } catch (Throwable t) {
        +            node.waitStatus = Node.CANCELLED;
        +            throw t;
                 }
             }
         
        @@ -1773,8 +1793,8 @@
              * given condition associated with this synchronizer. Note that
              * because timeouts and interrupts may occur at any time, the
              * estimate serves only as an upper bound on the actual number of
        -     * waiters.  This method is designed for use in monitoring of the
        -     * system state, not for synchronization control.
        +     * waiters.  This method is designed for use in monitoring system
        +     * state, not for synchronization control.
              *
              * @param condition the condition
              * @return the estimated number of waiting threads
        @@ -1852,7 +1872,9 @@
                         unlinkCancelledWaiters();
                         t = lastWaiter;
                     }
        -            Node node = new Node(Thread.currentThread(), Node.CONDITION);
        +
        +            Node node = new Node(Node.CONDITION);
        +
                     if (t == null)
                         firstWaiter = node;
                     else
        @@ -1960,12 +1982,12 @@
                 /**
                  * Implements uninterruptible condition wait.
                  * 
          - *
        1. Save lock state returned by {@link #getState}. - *
        2. Invoke {@link #release} with saved state as argument, - * throwing IllegalMonitorStateException if it fails. - *
        3. Block until signalled. - *
        4. Reacquire by invoking specialized version of - * {@link #acquire} with saved state as argument. + *
        5. Save lock state returned by {@link #getState}. + *
        6. Invoke {@link #release} with saved state as argument, + * throwing IllegalMonitorStateException if it fails. + *
        7. Block until signalled. + *
        8. Reacquire by invoking specialized version of + * {@link #acquire} with saved state as argument. *
        */ public final void awaitUninterruptibly() { @@ -2019,14 +2041,14 @@ /** * Implements interruptible condition wait. *
          - *
        1. If current thread is interrupted, throw InterruptedException. - *
        2. Save lock state returned by {@link #getState}. - *
        3. Invoke {@link #release} with saved state as argument, - * throwing IllegalMonitorStateException if it fails. - *
        4. Block until signalled or interrupted. - *
        5. Reacquire by invoking specialized version of - * {@link #acquire} with saved state as argument. - *
        6. If interrupted while blocked in step 4, throw InterruptedException. + *
        7. If current thread is interrupted, throw InterruptedException. + *
        8. Save lock state returned by {@link #getState}. + *
        9. Invoke {@link #release} with saved state as argument, + * throwing IllegalMonitorStateException if it fails. + *
        10. Block until signalled or interrupted. + *
        11. Reacquire by invoking specialized version of + * {@link #acquire} with saved state as argument. + *
        12. If interrupted while blocked in step 4, throw InterruptedException. *
        */ public final void await() throws InterruptedException { @@ -2051,30 +2073,33 @@ /** * Implements timed condition wait. *
          - *
        1. If current thread is interrupted, throw InterruptedException. - *
        2. Save lock state returned by {@link #getState}. - *
        3. Invoke {@link #release} with saved state as argument, - * throwing IllegalMonitorStateException if it fails. - *
        4. Block until signalled, interrupted, or timed out. - *
        5. Reacquire by invoking specialized version of - * {@link #acquire} with saved state as argument. - *
        6. If interrupted while blocked in step 4, throw InterruptedException. + *
        7. If current thread is interrupted, throw InterruptedException. + *
        8. Save lock state returned by {@link #getState}. + *
        9. Invoke {@link #release} with saved state as argument, + * throwing IllegalMonitorStateException if it fails. + *
        10. Block until signalled, interrupted, or timed out. + *
        11. Reacquire by invoking specialized version of + * {@link #acquire} with saved state as argument. + *
        12. If interrupted while blocked in step 4, throw InterruptedException. *
        */ public final long awaitNanos(long nanosTimeout) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); + // We don't check for nanosTimeout <= 0L here, to allow + // awaitNanos(0) as a way to "yield the lock". + final long deadline = System.nanoTime() + nanosTimeout; + long initialNanos = nanosTimeout; Node node = addConditionWaiter(); int savedState = fullyRelease(node); - final long deadline = System.nanoTime() + nanosTimeout; int interruptMode = 0; while (!isOnSyncQueue(node)) { if (nanosTimeout <= 0L) { transferAfterCancelledWait(node); break; } - if (nanosTimeout >= spinForTimeoutThreshold) + if (nanosTimeout >= SPIN_FOR_TIMEOUT_THRESHOLD) LockSupport.parkNanos(this, nanosTimeout); if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) break; @@ -2086,21 +2111,22 @@ unlinkCancelledWaiters(); if (interruptMode != 0) reportInterruptAfterWait(interruptMode); - return deadline - System.nanoTime(); + long remaining = deadline - System.nanoTime(); // avoid overflow + return (remaining <= initialNanos) ? remaining : Long.MIN_VALUE; } /** * Implements absolute timed condition wait. *
          - *
        1. If current thread is interrupted, throw InterruptedException. - *
        2. Save lock state returned by {@link #getState}. - *
        3. Invoke {@link #release} with saved state as argument, - * throwing IllegalMonitorStateException if it fails. - *
        4. Block until signalled, interrupted, or timed out. - *
        5. Reacquire by invoking specialized version of - * {@link #acquire} with saved state as argument. - *
        6. If interrupted while blocked in step 4, throw InterruptedException. - *
        7. If timed out while blocked in step 4, return false, else true. + *
        8. If current thread is interrupted, throw InterruptedException. + *
        9. Save lock state returned by {@link #getState}. + *
        10. Invoke {@link #release} with saved state as argument, + * throwing IllegalMonitorStateException if it fails. + *
        11. Block until signalled, interrupted, or timed out. + *
        12. Reacquire by invoking specialized version of + * {@link #acquire} with saved state as argument. + *
        13. If interrupted while blocked in step 4, throw InterruptedException. + *
        14. If timed out while blocked in step 4, return false, else true. *
        */ public final boolean awaitUntil(Date deadline) @@ -2113,7 +2139,7 @@ boolean timedout = false; int interruptMode = 0; while (!isOnSyncQueue(node)) { - if (System.currentTimeMillis() > abstime) { + if (System.currentTimeMillis() >= abstime) { timedout = transferAfterCancelledWait(node); break; } @@ -2133,15 +2159,15 @@ /** * Implements timed condition wait. *
          - *
        1. If current thread is interrupted, throw InterruptedException. - *
        2. Save lock state returned by {@link #getState}. - *
        3. Invoke {@link #release} with saved state as argument, - * throwing IllegalMonitorStateException if it fails. - *
        4. Block until signalled, interrupted, or timed out. - *
        5. Reacquire by invoking specialized version of - * {@link #acquire} with saved state as argument. - *
        6. If interrupted while blocked in step 4, throw InterruptedException. - *
        7. If timed out while blocked in step 4, return false, else true. + *
        8. If current thread is interrupted, throw InterruptedException. + *
        9. Save lock state returned by {@link #getState}. + *
        10. Invoke {@link #release} with saved state as argument, + * throwing IllegalMonitorStateException if it fails. + *
        11. Block until signalled, interrupted, or timed out. + *
        12. Reacquire by invoking specialized version of + * {@link #acquire} with saved state as argument. + *
        13. If interrupted while blocked in step 4, throw InterruptedException. + *
        14. If timed out while blocked in step 4, return false, else true. *
        */ public final boolean await(long time, TimeUnit unit) @@ -2149,9 +2175,11 @@ long nanosTimeout = unit.toNanos(time); if (Thread.interrupted()) throw new InterruptedException(); + // We don't check for nanosTimeout <= 0L here, to allow + // await(0, unit) as a way to "yield the lock". + final long deadline = System.nanoTime() + nanosTimeout; Node node = addConditionWaiter(); int savedState = fullyRelease(node); - final long deadline = System.nanoTime() + nanosTimeout; boolean timedout = false; int interruptMode = 0; while (!isOnSyncQueue(node)) { @@ -2159,7 +2187,7 @@ timedout = transferAfterCancelledWait(node); break; } - if (nanosTimeout >= spinForTimeoutThreshold) + if (nanosTimeout >= SPIN_FOR_TIMEOUT_THRESHOLD) LockSupport.parkNanos(this, nanosTimeout); if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) break; @@ -2236,7 +2264,7 @@ protected final Collection getWaitingThreads() { if (!isHeldExclusively()) throw new IllegalMonitorStateException(); - ArrayList list = new ArrayList(); + ArrayList list = new ArrayList<>(); for (Node w = firstWaiter; w != null; w = w.nextWaiter) { if (w.waitStatus == Node.CONDITION) { Thread t = w.thread; @@ -2257,59 +2285,40 @@ * are at it, we do the same for other CASable fields (which could * otherwise be done with atomic field updaters). */ - private static final Unsafe unsafe = Unsafe.getUnsafe(); - private static final long stateOffset; - private static final long headOffset; - private static final long tailOffset; - private static final long waitStatusOffset; - private static final long nextOffset; + private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe(); + private static final long STATE; + private static final long HEAD; + private static final long TAIL; static { try { - stateOffset = unsafe.objectFieldOffset + STATE = U.objectFieldOffset (AbstractQueuedSynchronizer.class.getDeclaredField("state")); - headOffset = unsafe.objectFieldOffset + HEAD = U.objectFieldOffset (AbstractQueuedSynchronizer.class.getDeclaredField("head")); - tailOffset = unsafe.objectFieldOffset + TAIL = U.objectFieldOffset (AbstractQueuedSynchronizer.class.getDeclaredField("tail")); - waitStatusOffset = unsafe.objectFieldOffset - (Node.class.getDeclaredField("waitStatus")); - nextOffset = unsafe.objectFieldOffset - (Node.class.getDeclaredField("next")); + } catch (ReflectiveOperationException e) { + throw new Error(e); + } - } catch (Exception ex) { throw new Error(ex); } + // Reduce the risk of rare disastrous classloading in first call to + // LockSupport.park: https://bugs.openjdk.java.net/browse/JDK-8074773 + Class ensureLoaded = LockSupport.class; } /** - * CAS head field. Used only by enq. + * Initializes head and tail fields on first contention. */ - private final boolean compareAndSetHead(Node update) { - return unsafe.compareAndSwapObject(this, headOffset, null, update); - } - - /** - * CAS tail field. Used only by enq. - */ - private final boolean compareAndSetTail(Node expect, Node update) { - return unsafe.compareAndSwapObject(this, tailOffset, expect, update); + private final void initializeSyncQueue() { + if (U.compareAndSwapObject(this, HEAD, null, new Node())) + tail = head; } /** - * CAS waitStatus field of a node. + * CASes tail field. */ - private static final boolean compareAndSetWaitStatus(Node node, - int expect, - int update) { - return unsafe.compareAndSwapInt(node, waitStatusOffset, - expect, update); - } - - /** - * CAS next field of a node. - */ - private static final boolean compareAndSetNext(Node node, - Node expect, - Node update) { - return unsafe.compareAndSwapObject(node, nextOffset, expect, update); + private final boolean compareAndSetTail(Node expect, Node update) { + return U.compareAndSwapObject(this, TAIL, expect, update); } } diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/locks/Condition.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/Condition.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/Condition.java Wed Jul 05 20:54:48 2017 +0200 @@ -34,8 +34,9 @@ */ package java.util.concurrent.locks; + +import java.util.Date; import java.util.concurrent.TimeUnit; -import java.util.Date; /** * {@code Condition} factors out the {@code Object} monitor @@ -126,7 +127,7 @@ *

        Note that {@code Condition} instances are just normal objects and can * themselves be used as the target in a {@code synchronized} statement, * and can have their own monitor {@link Object#wait wait} and - * {@link Object#notify notification} methods invoked. + * {@link Object#notify notify} methods invoked. * Acquiring the monitor lock of a {@code Condition} instance, or using its * monitor methods, has no specified relationship with acquiring the * {@link Lock} associated with that {@code Condition} or the use of its @@ -308,7 +309,7 @@ * condition still does not hold. Typical uses of this method take * the following form: * - *

         {@code
        +     * 
         {@code
              * boolean aMethod(long timeout, TimeUnit unit) {
              *   long nanos = unit.toNanos(timeout);
              *   lock.lock();
        @@ -361,7 +362,7 @@
              * Causes the current thread to wait until it is signalled or interrupted,
              * or the specified waiting time elapses. This method is behaviorally
              * equivalent to:
        -     *  
         {@code awaitNanos(unit.toNanos(time)) > 0}
        + *
         {@code awaitNanos(unit.toNanos(time)) > 0}
        * * @param time the maximum time to wait * @param unit the time unit of the {@code time} argument @@ -410,7 +411,7 @@ * *

        The return value indicates whether the deadline has elapsed, * which can be used as follows: - *

         {@code
        +     * 
         {@code
              * boolean aMethod(Date deadline) {
              *   boolean stillWaiting = true;
              *   lock.lock();
        diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/locks/Lock.java
        --- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/Lock.java	Wed Oct 21 15:15:26 2015 -0700
        +++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/Lock.java	Wed Jul 05 20:54:48 2017 +0200
        @@ -34,6 +34,7 @@
          */
         
         package java.util.concurrent.locks;
        +
         import java.util.concurrent.TimeUnit;
         
         /**
        @@ -77,7 +78,7 @@
          * methods and statements. In most cases, the following idiom
          * should be used:
          *
        - *  
         {@code
        + * 
         {@code
          * Lock l = ...;
          * l.lock();
          * try {
        @@ -121,8 +122,9 @@
          * 

        All {@code Lock} implementations must enforce the same * memory synchronization semantics as provided by the built-in monitor * lock, as described in - * - * The Java Language Specification (17.4 Memory Model): + * + * Chapter 17 of + * The Java™ Language Specification: *

          *
        • A successful {@code lock} operation has the same memory * synchronization effects as a successful Lock action. @@ -240,7 +242,7 @@ * immediately with the value {@code false}. * *

          A typical usage idiom for this method would be: - *

           {@code
          +     * 
           {@code
                * Lock lock = ...;
                * if (lock.tryLock()) {
                *   try {
          diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java
          --- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java	Wed Oct 21 15:15:26 2015 -0700
          +++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java	Wed Jul 05 20:54:48 2017 +0200
          @@ -34,7 +34,6 @@
            */
           
           package java.util.concurrent.locks;
          -import sun.misc.Unsafe;
           
           /**
            * Basic thread blocking primitives for creating locks and other
          @@ -47,6 +46,10 @@
            * it may block.  A call to {@code unpark} makes the permit
            * available, if it was not already available. (Unlike with Semaphores
            * though, permits do not accumulate. There is at most one.)
          + * Reliable usage requires the use of volatile (or atomic) variables
          + * to control when to park or unpark.  Orderings of calls to these
          + * methods are maintained with respect to volatile variable accesses,
          + * but not necessarily non-volatile variable accesses.
            *
            * 

          Methods {@code park} and {@code unpark} provide efficient * means of blocking and unblocking threads that do not encounter the @@ -77,7 +80,7 @@ * useful for most concurrency control applications. The {@code park} * method is designed for use only in constructions of the form: * - *

           {@code
          + * 
           {@code
            * while (!canProceed()) { ... LockSupport.park(this); }}
          * * where neither {@code canProceed} nor any other actions prior to the @@ -87,11 +90,11 @@ * *

          Sample Usage. Here is a sketch of a first-in-first-out * non-reentrant lock class: - *

           {@code
          + * 
           {@code
            * class FIFOMutex {
            *   private final AtomicBoolean locked = new AtomicBoolean(false);
            *   private final Queue waiters
          - *     = new ConcurrentLinkedQueue();
          + *     = new ConcurrentLinkedQueue<>();
            *
            *   public void lock() {
            *     boolean wasInterrupted = false;
          @@ -122,7 +125,7 @@
           
               private static void setBlocker(Thread t, Object arg) {
                   // Even though volatile, hotspot doesn't need a write barrier here.
          -        UNSAFE.putObject(t, parkBlockerOffset, arg);
          +        U.putObject(t, PARKBLOCKER, arg);
               }
           
               /**
          @@ -138,7 +141,7 @@
                */
               public static void unpark(Thread thread) {
                   if (thread != null)
          -            UNSAFE.unpark(thread);
          +            U.unpark(thread);
               }
           
               /**
          @@ -172,7 +175,7 @@
               public static void park(Object blocker) {
                   Thread t = Thread.currentThread();
                   setBlocker(t, blocker);
          -        UNSAFE.park(false, 0L);
          +        U.park(false, 0L);
                   setBlocker(t, null);
               }
           
          @@ -212,7 +215,7 @@
                   if (nanos > 0) {
                       Thread t = Thread.currentThread();
                       setBlocker(t, blocker);
          -            UNSAFE.park(false, nanos);
          +            U.park(false, nanos);
                       setBlocker(t, null);
                   }
               }
          @@ -253,7 +256,7 @@
               public static void parkUntil(Object blocker, long deadline) {
                   Thread t = Thread.currentThread();
                   setBlocker(t, blocker);
          -        UNSAFE.park(true, deadline);
          +        U.park(true, deadline);
                   setBlocker(t, null);
               }
           
          @@ -272,7 +275,7 @@
               public static Object getBlocker(Thread t) {
                   if (t == null)
                       throw new NullPointerException();
          -        return UNSAFE.getObjectVolatile(t, parkBlockerOffset);
          +        return U.getObjectVolatile(t, PARKBLOCKER);
               }
           
               /**
          @@ -301,7 +304,7 @@
                * for example, the interrupt status of the thread upon return.
                */
               public static void park() {
          -        UNSAFE.park(false, 0L);
          +        U.park(false, 0L);
               }
           
               /**
          @@ -335,7 +338,7 @@
                */
               public static void parkNanos(long nanos) {
                   if (nanos > 0)
          -            UNSAFE.park(false, nanos);
          +            U.park(false, nanos);
               }
           
               /**
          @@ -369,7 +372,7 @@
                *        to wait until
                */
               public static void parkUntil(long deadline) {
          -        UNSAFE.park(true, deadline);
          +        U.park(true, deadline);
               }
           
               /**
          @@ -379,36 +382,30 @@
               static final int nextSecondarySeed() {
                   int r;
                   Thread t = Thread.currentThread();
          -        if ((r = UNSAFE.getInt(t, SECONDARY)) != 0) {
          +        if ((r = U.getInt(t, SECONDARY)) != 0) {
                       r ^= r << 13;   // xorshift
                       r ^= r >>> 17;
                       r ^= r << 5;
                   }
                   else if ((r = java.util.concurrent.ThreadLocalRandom.current().nextInt()) == 0)
                       r = 1; // avoid zero
          -        UNSAFE.putInt(t, SECONDARY, r);
          +        U.putInt(t, SECONDARY, r);
                   return r;
               }
           
               // Hotspot implementation via intrinsics API
          -    private static final sun.misc.Unsafe UNSAFE;
          -    private static final long parkBlockerOffset;
          -    private static final long SEED;
          -    private static final long PROBE;
          +    private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
          +    private static final long PARKBLOCKER;
               private static final long SECONDARY;
               static {
                   try {
          -            UNSAFE = sun.misc.Unsafe.getUnsafe();
          -            Class tk = Thread.class;
          -            parkBlockerOffset = UNSAFE.objectFieldOffset
          -                (tk.getDeclaredField("parkBlocker"));
          -            SEED = UNSAFE.objectFieldOffset
          -                (tk.getDeclaredField("threadLocalRandomSeed"));
          -            PROBE = UNSAFE.objectFieldOffset
          -                (tk.getDeclaredField("threadLocalRandomProbe"));
          -            SECONDARY = UNSAFE.objectFieldOffset
          -                (tk.getDeclaredField("threadLocalRandomSecondarySeed"));
          -        } catch (Exception ex) { throw new Error(ex); }
          +            PARKBLOCKER = U.objectFieldOffset
          +                (Thread.class.getDeclaredField("parkBlocker"));
          +            SECONDARY = U.objectFieldOffset
          +                (Thread.class.getDeclaredField("threadLocalRandomSecondarySeed"));
          +        } catch (ReflectiveOperationException e) {
          +            throw new Error(e);
          +        }
               }
           
           }
          diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/locks/ReadWriteLock.java
          --- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReadWriteLock.java	Wed Oct 21 15:15:26 2015 -0700
          +++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReadWriteLock.java	Wed Jul 05 20:54:48 2017 +0200
          @@ -38,9 +38,9 @@
           /**
            * A {@code ReadWriteLock} maintains a pair of associated {@link
            * Lock locks}, one for read-only operations and one for writing.
          - * The {@link #readLock read lock} may be held simultaneously by
          - * multiple reader threads, so long as there are no writers.  The
          - * {@link #writeLock write lock} is exclusive.
          + * The {@linkplain #readLock read lock} may be held simultaneously
          + * by multiple reader threads, so long as there are no writers.
          + * The {@linkplain #writeLock write lock} is exclusive.
            *
            * 

          All {@code ReadWriteLock} implementations must guarantee that * the memory synchronization effects of {@code writeLock} operations diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/locks/ReentrantLock.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReentrantLock.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReentrantLock.java Wed Jul 05 20:54:48 2017 +0200 @@ -34,8 +34,9 @@ */ package java.util.concurrent.locks; + +import java.util.Collection; import java.util.concurrent.TimeUnit; -import java.util.Collection; /** * A reentrant mutual exclusion {@link Lock} with the same basic @@ -72,7 +73,7 @@ * follow a call to {@code lock} with a {@code try} block, most * typically in a before/after construction such as: * - *

           {@code
          + * 
           {@code
            * class X {
            *   private final ReentrantLock lock = new ReentrantLock();
            *   // ...
          @@ -378,7 +379,7 @@
                * method. If you want a timed {@code tryLock} that does permit barging on
                * a fair lock then combine the timed and un-timed forms together:
                *
          -     *  
           {@code
          +     * 
           {@code
                * if (lock.tryLock() ||
                *     lock.tryLock(timeout, unit)) {
                *   ...
          @@ -484,7 +485,7 @@
                * InterruptedException} will be thrown, and the thread's
                * interrupted status will be cleared.
                *
          -     * 
        • Waiting threads are signalled in FIFO order. + *
        • Waiting threads are signalled in FIFO order. * *
        • The ordering of lock reacquisition for threads returning * from waiting methods is the same as for threads initially @@ -511,7 +512,7 @@ * not be entered with the lock already held then we can assert that * fact: * - *
           {@code
          +     * 
           {@code
                * class X {
                *   ReentrantLock lock = new ReentrantLock();
                *   // ...
          @@ -541,7 +542,7 @@
                * debugging and testing. For example, a method that should only be
                * called while a lock is held can assert that this is the case:
                *
          -     *  
           {@code
          +     * 
           {@code
                * class X {
                *   ReentrantLock lock = new ReentrantLock();
                *   // ...
          @@ -555,7 +556,7 @@
                * 

          It can also be used to ensure that a reentrant lock is used * in a non-reentrant manner, for example: * - *

           {@code
          +     * 
           {@code
                * class X {
                *   ReentrantLock lock = new ReentrantLock();
                *   // ...
          @@ -646,12 +647,11 @@
               }
           
               /**
          -     * Returns an estimate of the number of threads waiting to
          -     * acquire this lock.  The value is only an estimate because the number of
          +     * Returns an estimate of the number of threads waiting to acquire
          +     * this lock.  The value is only an estimate because the number of
                * threads may change dynamically while this method traverses
                * internal data structures.  This method is designed for use in
          -     * monitoring of the system state, not for synchronization
          -     * control.
          +     * monitoring system state, not for synchronization control.
                *
                * @return the estimated number of threads waiting for this lock
                */
          diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/locks/ReentrantReadWriteLock.java
          --- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReentrantReadWriteLock.java	Wed Oct 21 15:15:26 2015 -0700
          +++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReentrantReadWriteLock.java	Wed Jul 05 20:54:48 2017 +0200
          @@ -34,8 +34,9 @@
            */
           
           package java.util.concurrent.locks;
          +
          +import java.util.Collection;
           import java.util.concurrent.TimeUnit;
          -import java.util.Collection;
           
           /**
            * An implementation of {@link ReadWriteLock} supporting similar
          @@ -51,14 +52,16 @@
            *
            * 
          *
          Non-fair mode (default) - *
          When constructed as non-fair (the default), the order of entry + *
          + * When constructed as non-fair (the default), the order of entry * to the read and write lock is unspecified, subject to reentrancy * constraints. A nonfair lock that is continuously contended may * indefinitely postpone one or more reader or writer threads, but * will normally have higher throughput than a fair lock. * *
          Fair mode - *
          When constructed as fair, threads contend for entry using an + *
          + * When constructed as fair, threads contend for entry using an * approximately arrival-order policy. When the currently held lock * is released, either the longest-waiting single writer thread will * be assigned the write lock, or if there is a group of reader threads @@ -173,9 +176,9 @@ * is a class using a TreeMap that is expected to be large and * concurrently accessed. * - *
           {@code
          + * 
           {@code
            * class RWDictionary {
          - *   private final Map m = new TreeMap();
          + *   private final Map m = new TreeMap<>();
            *   private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
            *   private final Lock r = rwl.readLock();
            *   private final Lock w = rwl.writeLock();
          @@ -263,17 +266,17 @@
                   static final int MAX_COUNT      = (1 << SHARED_SHIFT) - 1;
                   static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
           
          -        /** Returns the number of shared holds represented in count  */
          +        /** Returns the number of shared holds represented in count. */
                   static int sharedCount(int c)    { return c >>> SHARED_SHIFT; }
          -        /** Returns the number of exclusive holds represented in count  */
          +        /** Returns the number of exclusive holds represented in count. */
                   static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
           
                   /**
                    * A counter for per-thread read hold counts.
          -         * Maintained as a ThreadLocal; cached in cachedHoldCounter
          +         * Maintained as a ThreadLocal; cached in cachedHoldCounter.
                    */
                   static final class HoldCounter {
          -            int count = 0;
          +            int count;          // initially 0
                       // Use id, not reference, to avoid garbage retention
                       final long tid = getThreadId(Thread.currentThread());
                   }
          @@ -330,7 +333,7 @@
                    * 

          This allows tracking of read holds for uncontended read * locks to be very cheap. */ - private transient Thread firstReader = null; + private transient Thread firstReader; private transient int firstReaderHoldCount; Sync() { @@ -703,7 +706,7 @@ private final Sync sync; /** - * Constructor for use by subclasses + * Constructor for use by subclasses. * * @param lock the outer lock object * @throws NullPointerException if the lock is null @@ -814,7 +817,7 @@ * permit barging on a fair lock then combine the timed and * un-timed forms together: * - *

           {@code
          +         * 
           {@code
                    * if (lock.tryLock() ||
                    *     lock.tryLock(timeout, unit)) {
                    *   ...
          @@ -874,7 +877,12 @@
                    * Attempts to release this lock.
                    *
                    * 

          If the number of readers is now zero then the lock - * is made available for write lock attempts. + * is made available for write lock attempts. If the current + * thread does not hold this lock then {@link + * IllegalMonitorStateException} is thrown. + * + * @throws IllegalMonitorStateException if the current thread + * does not hold this lock */ public void unlock() { sync.releaseShared(1); @@ -912,7 +920,7 @@ private final Sync sync; /** - * Constructor for use by subclasses + * Constructor for use by subclasses. * * @param lock the outer lock object * @throws NullPointerException if the lock is null @@ -1026,7 +1034,7 @@ * by the current thread, or the write lock was already held * by the current thread; and {@code false} otherwise. */ - public boolean tryLock( ) { + public boolean tryLock() { return sync.tryWriteLock(); } @@ -1046,7 +1054,7 @@ * that does permit barging on a fair lock then combine the * timed and un-timed forms together: * - *

           {@code
          +         * 
           {@code
                    * if (lock.tryLock() ||
                    *     lock.tryLock(timeout, unit)) {
                    *   ...
          @@ -1161,7 +1169,7 @@
                    * InterruptedException} will be thrown, and the thread's
                    * interrupted status will be cleared.
                    *
          -         * 
        • Waiting threads are signalled in FIFO order. + *
        • Waiting threads are signalled in FIFO order. * *
        • The ordering of lock reacquisition for threads returning * from waiting methods is the same as for threads initially @@ -1369,7 +1377,7 @@ * either the read or write lock. The value is only an estimate * because the number of threads may change dynamically while this * method traverses internal data structures. This method is - * designed for use in monitoring of the system state, not for + * designed for use in monitoring system state, not for * synchronization control. * * @return the estimated number of threads waiting for this lock @@ -1489,19 +1497,17 @@ * ways that do not preserve unique mappings. */ static final long getThreadId(Thread thread) { - return UNSAFE.getLongVolatile(thread, TID_OFFSET); + return U.getLongVolatile(thread, TID); } // Unsafe mechanics - private static final sun.misc.Unsafe UNSAFE; - private static final long TID_OFFSET; + private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe(); + private static final long TID; static { try { - UNSAFE = sun.misc.Unsafe.getUnsafe(); - Class tk = Thread.class; - TID_OFFSET = UNSAFE.objectFieldOffset - (tk.getDeclaredField("tid")); - } catch (Exception e) { + TID = U.objectFieldOffset + (Thread.class.getDeclaredField("tid")); + } catch (ReflectiveOperationException e) { throw new Error(e); } } diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java Wed Jul 05 20:54:48 2017 +0200 @@ -36,10 +36,6 @@ package java.util.concurrent.locks; import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.Condition; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.LockSupport; /** * A capability-based lock with three modes for controlling read/write @@ -58,12 +54,12 @@ * in method {@link #unlockWrite} to release the lock. Untimed and * timed versions of {@code tryWriteLock} are also provided. When * the lock is held in write mode, no read locks may be obtained, - * and all optimistic read validations will fail.
        • + * and all optimistic read validations will fail. * *
        • Reading. Method {@link #readLock} possibly blocks * waiting for non-exclusive access, returning a stamp that can be * used in method {@link #unlockRead} to release the lock. Untimed - * and timed versions of {@code tryReadLock} are also provided.
        • + * and timed versions of {@code tryReadLock} are also provided. * *
        • Optimistic Reading. Method {@link #tryOptimisticRead} * returns a non-zero stamp only if the lock is not currently held @@ -81,7 +77,7 @@ * invoke method {@code validate()}. For example, such steps are * typically required when first reading an object or array * reference, and then accessing one of its fields, elements or - * methods.
        • + * methods. * *
        * @@ -132,7 +128,7 @@ * not strictly needed here because no exceptions can occur in their * bodies.
        * - *
        {@code
        + * 
         {@code
          * class Point {
          *   private double x, y;
          *   private final StampedLock sl = new StampedLock();
        @@ -542,7 +538,7 @@
                 WNode h;
                 if (state != stamp || (stamp & WBIT) == 0L)
                     throw new IllegalMonitorStateException();
        -        state = (stamp += WBIT) == 0L ? ORIGIN : stamp;
        +        U.putLongVolatile(this, STATE, (stamp += WBIT) == 0L ? ORIGIN : stamp);
                 if ((h = whead) != null && h.status != 0)
                     release(h);
             }
        @@ -589,7 +585,7 @@
                     else if (m == WBIT) {
                         if (a != m)
                             break;
        -                state = (s += WBIT) == 0L ? ORIGIN : s;
        +                U.putLongVolatile(this, STATE, (s += WBIT) == 0L ? ORIGIN : s);
                         if ((h = whead) != null && h.status != 0)
                             release(h);
                         return;
        @@ -610,7 +606,7 @@
             }
         
             /**
        -     * If the lock state matches the given stamp, performs one of
        +     * If the lock state matches the given stamp, atomically performs one of
              * the following actions. If the stamp represents holding a write
              * lock, returns it.  Or, if a read lock, if the write lock is
              * available, releases the read lock and returns a write stamp.
        @@ -647,7 +643,7 @@
             }
         
             /**
        -     * If the lock state matches the given stamp, performs one of
        +     * If the lock state matches the given stamp, atomically performs one of
              * the following actions. If the stamp represents holding a write
              * lock, releases it and obtains a read lock.  Or, if a read lock,
              * returns it. Or, if an optimistic read, acquires a read lock and
        @@ -673,7 +669,7 @@
                     else if (m == WBIT) {
                         if (a != m)
                             break;
        -                state = next = s + (WBIT + RUNIT);
        +                U.putLongVolatile(this, STATE, next = s + (WBIT + RUNIT));
                         if ((h = whead) != null && h.status != 0)
                             release(h);
                         return next;
        @@ -687,7 +683,7 @@
             }
         
             /**
        -     * If the lock state matches the given stamp then, if the stamp
        +     * If the lock state matches the given stamp then, atomically, if the stamp
              * represents holding a lock, releases it and returns an
              * observation stamp.  Or, if an optimistic read, returns it if
              * validated. This method returns zero in all other cases, and so
        @@ -710,7 +706,8 @@
                     else if (m == WBIT) {
                         if (a != m)
                             break;
        -                state = next = (s += WBIT) == 0L ? ORIGIN : s;
        +                U.putLongVolatile(this, STATE,
        +                                  next = (s += WBIT) == 0L ? ORIGIN : s);
                         if ((h = whead) != null && h.status != 0)
                             release(h);
                         return next;
        @@ -740,7 +737,7 @@
             public boolean tryUnlockWrite() {
                 long s; WNode h;
                 if (((s = state) & WBIT) != 0L) {
        -            state = (s += WBIT) == 0L ? ORIGIN : s;
        +            U.putLongVolatile(this, STATE, (s += WBIT) == 0L ? ORIGIN : s);
                     if ((h = whead) != null && h.status != 0)
                         release(h);
                     return true;
        @@ -923,7 +920,7 @@
                 WNode h; long s;
                 if (((s = state) & WBIT) == 0L)
                     throw new IllegalMonitorStateException();
        -        state = (s += WBIT) == 0L ? ORIGIN : s;
        +        U.putLongVolatile(this, STATE, (s += WBIT) == 0L ? ORIGIN : s);
                 if ((h = whead) != null && h.status != 0)
                     release(h);
             }
        @@ -948,7 +945,7 @@
             private void readObject(java.io.ObjectInputStream s)
                 throws java.io.IOException, ClassNotFoundException {
                 s.defaultReadObject();
        -        state = ORIGIN; // reset to unlocked state
        +        U.putLongVolatile(this, STATE, ORIGIN); // reset to unlocked state
             }
         
             // internals
        @@ -966,7 +963,7 @@
                 if ((s & ABITS) == RFULL) {
                     if (U.compareAndSwapLong(this, STATE, s, s | RBITS)) {
                         ++readerOverflow;
        -                state = s;
        +                U.putLongVolatile(this, STATE, s);
                         return s;
                     }
                 }
        @@ -993,8 +990,8 @@
                         }
                         else
                             next = s - RUNIT;
        -                 state = next;
        -                 return next;
        +                U.putLongVolatile(this, STATE, next);
        +                return next;
                     }
                 }
                 else if ((LockSupport.nextSecondarySeed() &
        @@ -1062,6 +1059,7 @@
                     }
                 }
         
        +        boolean wasInterrupted = false;
                 for (int spins = -1;;) {
                     WNode h, np, pp; int ps;
                     if ((h = whead) == p) {
        @@ -1076,6 +1074,8 @@
                                                          ns = s + WBIT)) {
                                     whead = node;
                                     node.prev = null;
        +                            if (wasInterrupted)
        +                                Thread.currentThread().interrupt();
                                     return ns;
                                 }
                             }
        @@ -1119,8 +1119,11 @@
                                 U.park(false, time);  // emulate LockSupport.park
                             node.thread = null;
                             U.putObject(wt, PARKBLOCKER, null);
        -                    if (interruptible && Thread.interrupted())
        -                        return cancelWaiter(node, node, true);
        +                    if (Thread.interrupted()) {
        +                        if (interruptible)
        +                            return cancelWaiter(node, node, true);
        +                        wasInterrupted = true;
        +                    }
                         }
                     }
                 }
        @@ -1136,6 +1139,7 @@
              * @return next state, or INTERRUPTED
              */
             private long acquireRead(boolean interruptible, long deadline) {
        +        boolean wasInterrupted = false;
                 WNode node = null, p;
                 for (int spins = -1;;) {
                     WNode h;
        @@ -1143,8 +1147,11 @@
                         for (long m, s, ns;;) {
                             if ((m = (s = state) & ABITS) < RFULL ?
                                 U.compareAndSwapLong(this, STATE, s, ns = s + RUNIT) :
        -                        (m < WBIT && (ns = tryIncReaderOverflow(s)) != 0L))
        +                        (m < WBIT && (ns = tryIncReaderOverflow(s)) != 0L)) {
        +                        if (wasInterrupted)
        +                            Thread.currentThread().interrupt();
                                 return ns;
        +                    }
                             else if (m >= WBIT) {
                                 if (spins > 0) {
                                     if (LockSupport.nextSecondarySeed() >= 0)
        @@ -1193,8 +1200,11 @@
                                         U.compareAndSwapLong(this, STATE, s,
                                                              ns = s + RUNIT) :
                                         (m < WBIT &&
        -                                 (ns = tryIncReaderOverflow(s)) != 0L))
        +                                 (ns = tryIncReaderOverflow(s)) != 0L)) {
        +                                if (wasInterrupted)
        +                                    Thread.currentThread().interrupt();
                                         return ns;
        +                            }
                                 } while (m < WBIT);
                             }
                             if (whead == h && p.prev == pp) {
        @@ -1205,8 +1215,11 @@
                                 }
                                 if (deadline == 0L)
                                     time = 0L;
        -                        else if ((time = deadline - System.nanoTime()) <= 0L)
        +                        else if ((time = deadline - System.nanoTime()) <= 0L) {
        +                            if (wasInterrupted)
        +                                Thread.currentThread().interrupt();
                                     return cancelWaiter(node, p, false);
        +                        }
                                 Thread wt = Thread.currentThread();
                                 U.putObject(wt, PARKBLOCKER, this);
                                 node.thread = wt;
        @@ -1215,8 +1228,11 @@
                                     U.park(false, time);
                                 node.thread = null;
                                 U.putObject(wt, PARKBLOCKER, null);
        -                        if (interruptible && Thread.interrupted())
        -                            return cancelWaiter(node, p, true);
        +                        if (Thread.interrupted()) {
        +                            if (interruptible)
        +                                return cancelWaiter(node, p, true);
        +                            wasInterrupted = true;
        +                        }
                             }
                         }
                     }
        @@ -1243,6 +1259,8 @@
                                         (w = c.thread) != null)
                                         U.unpark(w);
                                 }
        +                        if (wasInterrupted)
        +                            Thread.currentThread().interrupt();
                                 return ns;
                             }
                             else if (m >= WBIT &&
        @@ -1286,8 +1304,11 @@
                                 U.park(false, time);
                             node.thread = null;
                             U.putObject(wt, PARKBLOCKER, null);
        -                    if (interruptible && Thread.interrupted())
        -                        return cancelWaiter(node, node, true);
        +                    if (Thread.interrupted()) {
        +                        if (interruptible)
        +                            return cancelWaiter(node, node, true);
        +                        wasInterrupted = true;
        +                    }
                         }
                     }
                 }
        @@ -1377,7 +1398,7 @@
             }
         
             // Unsafe mechanics
        -    private static final sun.misc.Unsafe U;
        +    private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
             private static final long STATE;
             private static final long WHEAD;
             private static final long WTAIL;
        @@ -1388,26 +1409,23 @@
         
             static {
                 try {
        -            U = sun.misc.Unsafe.getUnsafe();
        -            Class k = StampedLock.class;
        -            Class wk = WNode.class;
                     STATE = U.objectFieldOffset
        -                (k.getDeclaredField("state"));
        +                (StampedLock.class.getDeclaredField("state"));
                     WHEAD = U.objectFieldOffset
        -                (k.getDeclaredField("whead"));
        +                (StampedLock.class.getDeclaredField("whead"));
                     WTAIL = U.objectFieldOffset
        -                (k.getDeclaredField("wtail"));
        +                (StampedLock.class.getDeclaredField("wtail"));
        +
                     WSTATUS = U.objectFieldOffset
        -                (wk.getDeclaredField("status"));
        +                (WNode.class.getDeclaredField("status"));
                     WNEXT = U.objectFieldOffset
        -                (wk.getDeclaredField("next"));
        +                (WNode.class.getDeclaredField("next"));
                     WCOWAIT = U.objectFieldOffset
        -                (wk.getDeclaredField("cowait"));
        -            Class tk = Thread.class;
        +                (WNode.class.getDeclaredField("cowait"));
        +
                     PARKBLOCKER = U.objectFieldOffset
        -                (tk.getDeclaredField("parkBlocker"));
        -
        -        } catch (Exception e) {
        +                (Thread.class.getDeclaredField("parkBlocker"));
        +        } catch (ReflectiveOperationException e) {
                     throw new Error(e);
                 }
             }
        diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/concurrent/package-info.java
        --- a/jdk/src/java.base/share/classes/java/util/concurrent/package-info.java	Wed Oct 21 15:15:26 2015 -0700
        +++ b/jdk/src/java.base/share/classes/java/util/concurrent/package-info.java	Wed Jul 05 20:54:48 2017 +0200
        @@ -226,8 +226,9 @@
          *
          * 

        Memory Consistency Properties

        * - * - * Chapter 17 of the Java Language Specification defines the + * + * Chapter 17 of + * The Java™ Language Specification defines the * happens-before relation on memory operations such as reads and * writes of shared variables. The results of a write by one thread are * guaranteed to be visible to a read by another thread only if the write diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/zip/Deflater.java --- a/jdk/src/java.base/share/classes/java/util/zip/Deflater.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/zip/Deflater.java Wed Jul 05 20:54:48 2017 +0200 @@ -318,7 +318,9 @@ * should be called in order to provide more input */ public boolean needsInput() { - return len <= 0; + synchronized (zsRef) { + return len <= 0; + } } /** diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/zip/ZStreamRef.java --- a/jdk/src/java.base/share/classes/java/util/zip/ZStreamRef.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/zip/ZStreamRef.java Wed Jul 05 20:54:48 2017 +0200 @@ -31,7 +31,7 @@ class ZStreamRef { - private long address; + private volatile long address; ZStreamRef (long address) { this.address = address; } diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/java/util/zip/ZipFile.java --- a/jdk/src/java.base/share/classes/java/util/zip/ZipFile.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/util/zip/ZipFile.java Wed Jul 05 20:54:48 2017 +0200 @@ -60,7 +60,7 @@ */ public class ZipFile implements ZipConstants, Closeable { - private long jzfile; // address of jzfile data + private long jzfile; // address of jzfile data private final String name; // zip file name private final int total; // total number of entries private final boolean locsig; // if zip file starts with LOCSIG (usually true) @@ -691,7 +691,7 @@ * (possibly compressed) zip file entry. */ private class ZipFileInputStream extends InputStream { - private volatile boolean closeRequested = false; + private volatile boolean zfisCloseRequested = false; protected long jzentry; // address of jzentry data private long pos; // current position within entry data protected long rem; // number of remaining bytes within entry @@ -718,6 +718,7 @@ len = (int) rem; } + // Check if ZipFile open ensureOpenOrZipException(); len = ZipFile.read(ZipFile.this.jzfile, jzentry, pos, b, off, len); @@ -761,9 +762,9 @@ } public void close() { - if (closeRequested) + if (zfisCloseRequested) return; - closeRequested = true; + zfisCloseRequested = true; rem = 0; synchronized (ZipFile.this) { diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/jdk/internal/jimage/BasicImageReader.java --- a/jdk/src/java.base/share/classes/jdk/internal/jimage/BasicImageReader.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/BasicImageReader.java Wed Jul 05 20:54:48 2017 +0200 @@ -58,7 +58,7 @@ try { substrate = ImageNativeSubstrate.openImage(imagePath, byteOrder); - } catch (UnsatisfiedLinkError ex) { + } catch (UnsatisfiedLinkError | NoClassDefFoundError ex) { substrate = ImageJavaSubstrate.openImage(imagePath, byteOrder); } diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/sun/invoke/util/BytecodeDescriptor.java --- a/jdk/src/java.base/share/classes/sun/invoke/util/BytecodeDescriptor.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/classes/sun/invoke/util/BytecodeDescriptor.java Wed Jul 05 20:54:48 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2015, 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 @@ -37,14 +37,20 @@ private BytecodeDescriptor() { } // cannot instantiate + /** + * @param loader the class loader in which to look up the types (null means + * bootstrap class loader) + */ public static List> parseMethod(String bytecodeSignature, ClassLoader loader) { return parseMethod(bytecodeSignature, 0, bytecodeSignature.length(), loader); } + /** + * @param loader the class loader in which to look up the types (null means + * bootstrap class loader) + */ static List> parseMethod(String bytecodeSignature, int start, int end, ClassLoader loader) { - if (loader == null) - loader = ClassLoader.getSystemClassLoader(); String str = bytecodeSignature; int[] i = {start}; ArrayList> ptypes = new ArrayList>(); @@ -71,6 +77,10 @@ throw new IllegalArgumentException("bad signature: "+str+": "+msg); } + /** + * @param loader the class loader in which to look up the types (null means + * bootstrap class loader) + */ private static Class parseSig(String str, int[] i, int end, ClassLoader loader) { if (i[0] == end) return null; char c = str.charAt(i[0]++); @@ -80,7 +90,9 @@ i[0] = endc+1; String name = str.substring(begc, endc).replace('/', '.'); try { - return loader.loadClass(name); + return (loader == null) + ? Class.forName(name, false, null) + : loader.loadClass(name); } catch (ClassNotFoundException ex) { throw new TypeNotPresentException(name, ex); } diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/sun/misc/ConditionLock.java --- a/jdk/src/java.base/share/classes/sun/misc/ConditionLock.java Wed Oct 21 15:15:26 2015 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,82 +0,0 @@ -/* - * Copyright (c) 1994, 2005, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -package sun.misc; - -/** - * ConditionLock is a Lock with a built in state variable. This class - * provides the ability to wait for the state variable to be set to a - * desired value and then acquire the lock.

        - * - * The lockWhen() and unlockWith() methods can be safely intermixed - * with the lock() and unlock() methods. However if there is a thread - * waiting for the state variable to become a particular value and you - * simply call Unlock(), that thread will not be able to acquire the - * lock until the state variable equals its desired value. - * - * @author Peter King - */ -public final -class ConditionLock extends Lock { - private int state = 0; - - /** - * Creates a ConditionLock. - */ - public ConditionLock () { - } - - /** - * Creates a ConditionLock in an initialState. - */ - public ConditionLock (int initialState) { - state = initialState; - } - - /** - * Acquires the lock when the state variable equals the desired state. - * - * @param desiredState the desired state - * @exception java.lang.InterruptedException if any thread has - * interrupted this thread. - */ - public synchronized void lockWhen(int desiredState) - throws InterruptedException - { - while (state != desiredState) { - wait(); - } - lock(); - } - - /** - * Releases the lock, and sets the state to a new value. - * @param newState the new state - */ - public synchronized void unlockWith(int newState) { - state = newState; - unlock(); - } -} diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/classes/sun/misc/Lock.java --- a/jdk/src/java.base/share/classes/sun/misc/Lock.java Wed Oct 21 15:15:26 2015 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,93 +0,0 @@ -/* - * Copyright (c) 1994, 2005, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -package sun.misc; - -/** - * The Lock class provides a simple, useful interface to a lock. - * Unlike monitors which synchronize access to an object, locks - * synchronize access to an arbitrary set of resources (objects, - * methods, variables, etc.).

        - * - * The programmer using locks must be responsible for clearly defining - * the semantics of their use and should handle deadlock avoidance in - * the face of exceptions.

        - * - * For example, if you want to protect a set of method invocations with - * a lock, and one of the methods may throw an exception, you must be - * prepared to release the lock similarly to the following example: - *

        - *      class SomeClass {
        - *          Lock myLock = new Lock();
        -
        - *          void someMethod() {
        - *              myLock.lock();
        - *              try {
        - *                  StartOperation();
        - *                  ContinueOperation();
        - *                  EndOperation();
        - *              } finally {
        - *                  myLock.unlock();
        - *              }
        - *          }
        - *      }
        - * 
        - * - * @author Peter King - */ -public -class Lock { - private boolean locked = false; - - /** - * Create a lock, which is initially not locked. - */ - public Lock () { - } - - /** - * Acquire the lock. If someone else has the lock, wait until it - * has been freed, and then try to acquire it again. This method - * will not return until the lock has been acquired. - * - * @exception java.lang.InterruptedException if any thread has - * interrupted this thread. - */ - public final synchronized void lock() throws InterruptedException { - while (locked) { - wait(); - } - locked = true; - } - - /** - * Release the lock. If someone else is waiting for the lock, the - * will be notitified so they can try to acquire the lock again. - */ - public final synchronized void unlock() { - locked = false; - notifyAll(); - } -} diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/native/libfdlibm/s_cbrt.c --- a/jdk/src/java.base/share/native/libfdlibm/s_cbrt.c Wed Oct 21 15:15:26 2015 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,99 +0,0 @@ - -/* - * Copyright (c) 1998, 2001, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -#include "fdlibm.h" - -/* cbrt(x) - * Return cube root of x - */ -#ifdef __STDC__ -static const unsigned -#else -static unsigned -#endif - B1 = 715094163, /* B1 = (682-0.03306235651)*2**20 */ - B2 = 696219795; /* B2 = (664-0.03306235651)*2**20 */ - -#ifdef __STDC__ -static const double -#else -static double -#endif -C = 5.42857142857142815906e-01, /* 19/35 = 0x3FE15F15, 0xF15F15F1 */ -D = -7.05306122448979611050e-01, /* -864/1225 = 0xBFE691DE, 0x2532C834 */ -E = 1.41428571428571436819e+00, /* 99/70 = 0x3FF6A0EA, 0x0EA0EA0F */ -F = 1.60714285714285720630e+00, /* 45/28 = 0x3FF9B6DB, 0x6DB6DB6E */ -G = 3.57142857142857150787e-01; /* 5/14 = 0x3FD6DB6D, 0xB6DB6DB7 */ - -#ifdef __STDC__ - double cbrt(double x) -#else - double cbrt(x) - double x; -#endif -{ - int hx; - double r,s,t=0.0,w; - unsigned sign; - - - hx = __HI(x); /* high word of x */ - sign=hx&0x80000000; /* sign= sign(x) */ - hx ^=sign; - if(hx>=0x7ff00000) return(x+x); /* cbrt(NaN,INF) is itself */ - if((hx|__LO(x))==0) - return(x); /* cbrt(0) is itself */ - - __HI(x) = hx; /* x <- |x| */ - /* rough cbrt to 5 bits */ - if(hx<0x00100000) /* subnormal number */ - {__HI(t)=0x43500000; /* set t= 2**54 */ - t*=x; __HI(t)=__HI(t)/3+B2; - } - else - __HI(t)=hx/3+B1; - - - /* new cbrt to 23 bits, may be implemented in single precision */ - r=t*t/x; - s=C+r*t; - t*=G+F/(s+E+D/s); - - /* chopped to 20 bits and make it larger than cbrt(x) */ - __LO(t)=0; __HI(t)+=0x00000001; - - - /* one step newton iteration to 53 bits with error less than 0.667 ulps */ - s=t*t; /* t*t is exact */ - r=x/s; - w=t+t; - r=(r-t)/(w+r); /* r-s is exact */ - t=t+t*r; - - /* retore the sign bit */ - __HI(t) |= sign; - return(t); -} diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/native/libjava/StrictMath.c --- a/jdk/src/java.base/share/native/libjava/StrictMath.c Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/native/libjava/StrictMath.c Wed Jul 05 20:54:48 2017 +0200 @@ -89,12 +89,6 @@ } JNIEXPORT jdouble JNICALL -Java_java_lang_StrictMath_cbrt(JNIEnv *env, jclass unused, jdouble d) -{ - return (jdouble) jcbrt((double)d); -} - -JNIEXPORT jdouble JNICALL Java_java_lang_StrictMath_atan2(JNIEnv *env, jclass unused, jdouble d1, jdouble d2) { return (jdouble) jatan2((double)d1, (double)d2); diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.base/share/native/libzip/zip_util.c --- a/jdk/src/java.base/share/native/libzip/zip_util.c Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.base/share/native/libzip/zip_util.c Wed Jul 05 20:54:48 2017 +0200 @@ -1302,12 +1302,23 @@ jint ZIP_Read(jzfile *zip, jzentry *entry, jlong pos, void *buf, jint len) { - jlong entry_size = (entry->csize != 0) ? entry->csize : entry->size; + jlong entry_size; jlong start; + if (zip == 0) { + return -1; + } + /* Clear previous zip error */ zip->msg = NULL; + if (entry == 0) { + zip->msg = "ZIP_Read: jzentry is NULL"; + return -1; + } + + entry_size = (entry->csize != 0) ? entry->csize : entry->size; + /* Check specified position */ if (pos < 0 || pos > entry_size - 1) { zip->msg = "ZIP_Read: specified offset out of range"; @@ -1440,6 +1451,11 @@ char *msg; char tmpbuf[1024]; + if (entry == 0) { + jio_fprintf(stderr, "jzentry was invalid"); + return JNI_FALSE; + } + strcpy(entryname, entry->name); if (entry->csize == 0) { /* Entry is stored */ diff -r 9d153359b8ff -r 470068717ae2 jdk/src/java.logging/share/classes/java/util/logging/LogManager.java --- a/jdk/src/java.logging/share/classes/java/util/logging/LogManager.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/java.logging/share/classes/java/util/logging/LogManager.java Wed Jul 05 20:54:48 2017 +0200 @@ -32,8 +32,14 @@ import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; import java.util.concurrent.ConcurrentHashMap; +import java.nio.file.Paths; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.locks.ReentrantLock; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; import jdk.internal.misc.JavaAWTAccess; import jdk.internal.misc.SharedSecrets; import sun.misc.ManagedLocalsThread; @@ -57,37 +63,28 @@ *

        * At startup the LogManager class is located using the * java.util.logging.manager system property. + * + *

        LogManager Configuration

        + * + * A LogManager initializes the logging configuration via + * the {@link #readConfiguration()} method during LogManager initialization. + * By default, LogManager default configuration is used. + * The logging configuration read by LogManager must be in the + * {@linkplain Properties properties file} format. *

        * The LogManager defines two optional system properties that allow control over - * the initial configuration: + * the initial configuration, as specified in the {@link #readConfiguration()} + * method: *

          *
        • "java.util.logging.config.class" *
        • "java.util.logging.config.file" *
        - * These two properties may be specified on the command line to the "java" + *

        + * These two system properties may be specified on the command line to the "java" * command, or as system property definitions passed to JNI_CreateJavaVM. *

        - * If the "java.util.logging.config.class" property is set, then the - * property value is treated as a class name. The given class will be - * loaded, an object will be instantiated, and that object's constructor - * is responsible for reading in the initial configuration. (That object - * may use other system properties to control its configuration.) The - * alternate configuration class can use {@code readConfiguration(InputStream)} - * to define properties in the LogManager. - *

        - * If "java.util.logging.config.class" property is not set, - * then the "java.util.logging.config.file" system property can be used - * to specify a properties file (in java.util.Properties format). The - * initial logging configuration will be read from this file. - *

        - * If neither of these properties is defined then the LogManager uses its - * default configuration. The default configuration is typically loaded from the - * properties file "{@code conf/logging.properties}" in the Java installation - * directory. - *

        - * The properties for loggers and Handlers will have names starting - * with the dot-separated name for the handler or logger. - *

        + * The {@linkplain Properties properties} for loggers and Handlers will have + * names starting with the dot-separated name for the handler or logger.
        * The global logging properties may include: *

          *
        • A property "handlers". This defines a whitespace or comma separated @@ -788,7 +785,7 @@ // instantiation of the handler is done in the LogManager.addLogger // implementation as a handler class may be only visible to LogManager // subclass for the custom log manager case - processParentHandlers(logger, name); + processParentHandlers(logger, name, VisitedLoggers.NEVER); // Find the new node and its parent. LogNode node = getNode(name); @@ -836,7 +833,8 @@ // If logger.getUseParentHandlers() returns 'true' and any of the logger's // parents have levels or handlers defined, make sure they are instantiated. - private void processParentHandlers(final Logger logger, final String name) { + private void processParentHandlers(final Logger logger, final String name, + Predicate visited) { final LogManager owner = getOwner(); AccessController.doPrivileged(new PrivilegedAction() { @Override @@ -862,7 +860,9 @@ owner.getProperty(pname + ".handlers") != null) { // This pname has a level/handlers definition. // Make sure it exists. - demandLogger(pname, null, null); + if (visited.test(demandLogger(pname, null, null))) { + break; + } } ix = ix2+1; } @@ -942,48 +942,64 @@ private void loadLoggerHandlers(final Logger logger, final String name, final String handlersPropertyName) { - AccessController.doPrivileged(new PrivilegedAction() { + AccessController.doPrivileged(new PrivilegedAction() { @Override - public Object run() { - String names[] = parseClassNames(handlersPropertyName); - final boolean ensureCloseOnReset = names.length > 0 - && getBooleanProperty(handlersPropertyName + ".ensureCloseOnReset",true); - - int count = 0; - for (String type : names) { - try { - Class clz = ClassLoader.getSystemClassLoader().loadClass(type); - Handler hdl = (Handler) clz.newInstance(); - // Check if there is a property defining the - // this handler's level. - String levs = getProperty(type + ".level"); - if (levs != null) { - Level l = Level.findLevel(levs); - if (l != null) { - hdl.setLevel(l); - } else { - // Probably a bad level. Drop through. - System.err.println("Can't set level for " + type); - } - } - // Add this Handler to the logger - logger.addHandler(hdl); - if (++count == 1 && ensureCloseOnReset) { - // add this logger to the closeOnResetLoggers list. - closeOnResetLoggers.addIfAbsent(CloseOnReset.create(logger)); - } - } catch (Exception ex) { - System.err.println("Can't load log handler \"" + type + "\""); - System.err.println("" + ex); - ex.printStackTrace(); - } - } - + public Void run() { + setLoggerHandlers(logger, name, handlersPropertyName, + createLoggerHandlers(name, handlersPropertyName)); return null; } }); } + private void setLoggerHandlers(final Logger logger, final String name, + final String handlersPropertyName, + List handlers) + { + final boolean ensureCloseOnReset = ! handlers.isEmpty() + && getBooleanProperty(handlersPropertyName + ".ensureCloseOnReset",true); + int count = 0; + for (Handler hdl : handlers) { + logger.addHandler(hdl); + if (++count == 1 && ensureCloseOnReset) { + // add this logger to the closeOnResetLoggers list. + closeOnResetLoggers.addIfAbsent(CloseOnReset.create(logger)); + } + } + } + + private List createLoggerHandlers(final String name, final String handlersPropertyName) + { + String names[] = parseClassNames(handlersPropertyName); + List handlers = new ArrayList<>(names.length); + for (String type : names) { + try { + Class clz = ClassLoader.getSystemClassLoader().loadClass(type); + Handler hdl = (Handler) clz.newInstance(); + // Check if there is a property defining the + // this handler's level. + String levs = getProperty(type + ".level"); + if (levs != null) { + Level l = Level.findLevel(levs); + if (l != null) { + hdl.setLevel(l); + } else { + // Probably a bad level. Drop through. + System.err.println("Can't set level for " + type); + } + } + // Add this Handler to the logger + handlers.add(hdl); + } catch (Exception ex) { + System.err.println("Can't load log handler \"" + type + "\""); + System.err.println("" + ex); + ex.printStackTrace(); + } + } + + return handlers; + } + // loggerRefQueue holds LoggerWeakRef objects for Logger objects // that have been GC'ed. @@ -1242,21 +1258,48 @@ } /** - * Reinitialize the logging properties and reread the logging configuration. + * Reads and initializes the logging configuration. + *

          + * If the "java.util.logging.config.class" system property is set, then the + * property value is treated as a class name. The given class will be + * loaded, an object will be instantiated, and that object's constructor + * is responsible for reading in the initial configuration. (That object + * may use other system properties to control its configuration.) The + * alternate configuration class can use {@code readConfiguration(InputStream)} + * to define properties in the LogManager. *

          - * The same rules are used for locating the configuration properties - * as are used at startup. So normally the logging properties will - * be re-read from the same file that was used at startup. - *

          - * Any log level definitions in the new configuration file will be - * applied using Logger.setLevel(), if the target Logger exists. + * If "java.util.logging.config.class" system property is not set, + * then this method will read the initial configuration from a properties + * file and calls the {@link #readConfiguration(InputStream)} method to initialize + * the configuration. The "java.util.logging.config.file" system property can be used + * to specify the properties file that will be read as the initial configuration; + * if not set, then the LogManager default configuration is used. + * The default configuration is typically loaded from the + * properties file "{@code conf/logging.properties}" in the Java installation + * directory. + * *

          * Any {@linkplain #addConfigurationListener registered configuration * listener} will be invoked after the properties are read. * - * @exception SecurityException if a security manager exists and if - * the caller does not have LoggingPermission("control"). - * @exception IOException if there are IO problems reading the configuration. + * @apiNote This {@code readConfiguration} method should only be used for + * initializing the configuration during LogManager initialization or + * used with the "java.util.logging.config.class" property. + * When this method is called after loggers have been created, and + * the "java.util.logging.config.class" system property is not set, all + * existing loggers will be {@linkplain #reset() reset}. Then any + * existing loggers that have a level property specified in the new + * configuration stream will be {@linkplain + * Logger#setLevel(java.util.logging.Level) set} to the specified log level. + *

          + * To properly update the logging configuration, use the + * {@link #updateConfiguration(java.util.function.Function)} or + * {@link #updateConfiguration(java.io.InputStream, java.util.function.Function)} + * methods instead. + * + * @throws SecurityException if a security manager exists and if + * the caller does not have LoggingPermission("control"). + * @throws IOException if there are IO problems reading the configuration. */ public void readConfiguration() throws IOException, SecurityException { checkPermission(); @@ -1284,20 +1327,24 @@ } } + String fname = getConfigurationFileName(); + try (final InputStream in = new FileInputStream(fname)) { + final BufferedInputStream bin = new BufferedInputStream(in); + readConfiguration(bin); + } + } + + String getConfigurationFileName() throws IOException { String fname = System.getProperty("java.util.logging.config.file"); if (fname == null) { fname = System.getProperty("java.home"); if (fname == null) { throw new Error("Can't find java.home ??"); } - File f = new File(fname, "conf"); - f = new File(f, "logging.properties"); - fname = f.getCanonicalPath(); + fname = Paths.get(fname, "conf", "logging.properties") + .toAbsolutePath().normalize().toString(); } - try (final InputStream in = new FileInputStream(fname)) { - final BufferedInputStream bin = new BufferedInputStream(in); - readConfiguration(bin); - } + return fname; } /** @@ -1305,9 +1352,17 @@ *

          * For all named loggers, the reset operation removes and closes * all Handlers and (except for the root logger) sets the level - * to null. The root logger's level is set to Level.INFO. + * to {@code null}. The root logger's level is set to {@code Level.INFO}. * - * @exception SecurityException if a security manager exists and if + * @apiNote Calling this method also clears the LogManager {@linkplain + * #getProperty(java.lang.String) properties}. The {@link + * #updateConfiguration(java.util.function.Function) + * updateConfiguration(Function)} or + * {@link #updateConfiguration(java.io.InputStream, java.util.function.Function) + * updateConfiguration(InputStream, Function)} method can be used to + * properly update to a new configuration. + * + * @throws SecurityException if a security manager exists and if * the caller does not have LoggingPermission("control"). */ @@ -1421,18 +1476,32 @@ } /** - * Reinitialize the logging properties and reread the logging configuration - * from the given stream, which should be in java.util.Properties format. + * Reads and initializes the logging configuration from the given input stream. + * + *

          * Any {@linkplain #addConfigurationListener registered configuration * listener} will be invoked after the properties are read. *

          - * Any log level definitions in the new configuration file will be - * applied using Logger.setLevel(), if the target Logger exists. + * @apiNote This {@code readConfiguration} method should only be used for + * initializing the configuration during LogManager initialization or + * used with the "java.util.logging.config.class" property. + * When this method is called after loggers have been created, all + * existing loggers will be {@linkplain #reset() reset}. Then any + * existing loggers that have a level property specified in the + * given input stream will be {@linkplain + * Logger#setLevel(java.util.logging.Level) set} to the specified log level. + *

          + * To properly update the logging configuration, use the + * {@link #updateConfiguration(java.util.function.Function)} or + * {@link #updateConfiguration(java.io.InputStream, java.util.function.Function)} + * method instead. * - * @param ins stream to read properties from - * @exception SecurityException if a security manager exists and if + * @param ins stream to read properties from + * @throws SecurityException if a security manager exists and if * the caller does not have LoggingPermission("control"). - * @exception IOException if there are problems reading from the stream. + * @throws IOException if there are problems reading from the stream, + * or the given stream is not in the + * {@linkplain java.util.Properties properties file} format. */ public void readConfiguration(InputStream ins) throws IOException, SecurityException { checkPermission(); @@ -1506,6 +1575,633 @@ invokeConfigurationListeners(); } + // This enum enumerate the configuration properties that will be + // updated on existing loggers when the configuration is updated + // with LogManager.updateConfiguration(). + // + // Note that this works properly only for the global LogManager - as + // Handler and its subclasses get their configuration from + // LogManager.getLogManager(). + // + static enum ConfigProperty { + LEVEL(".level"), HANDLERS(".handlers"), USEPARENT(".useParentHandlers"); + final String suffix; + final int length; + private ConfigProperty(String suffix) { + this.suffix = Objects.requireNonNull(suffix); + length = suffix.length(); + } + + public boolean handleKey(String key) { + if (this == HANDLERS && suffix.substring(1).equals(key)) return true; + if (this == HANDLERS && suffix.equals(key)) return false; + return key.endsWith(suffix); + } + String key(String loggerName) { + if (this == HANDLERS && (loggerName == null || loggerName.isEmpty())) { + return suffix.substring(1); + } + return loggerName + suffix; + } + String loggerName(String key) { + assert key.equals(suffix.substring(1)) && this == HANDLERS || key.endsWith(suffix); + if (this == HANDLERS && suffix.substring(1).equals(key)) return ""; + return key.substring(0, key.length() - length); + } + + /** + * If the property is one that should be updated on existing loggers by + * updateConfiguration, returns the name of the logger for which the + * property is configured. Otherwise, returns null. + * @param property a property key in 'props' + * @return the name of the logger on which the property is to be set, + * if the property is one that should be updated on existing + * loggers, {@code null} otherwise. + */ + static String getLoggerName(String property) { + for (ConfigProperty p : ConfigProperty.ALL) { + if (p.handleKey(property)) { + return p.loggerName(property); + } + } + return null; // Not a property that should be updated. + } + + /** + * Find the ConfigProperty corresponding to the given + * property key (may find none). + * @param property a property key in 'props' + * @return An optional containing a ConfigProperty object, + * if the property is one that should be updated on existing + * loggers, empty otherwise. + */ + static Optional find(String property) { + return ConfigProperty.ALL.stream() + .filter(p -> p.handleKey(property)) + .findFirst(); + } + + /** + * Returns true if the given property is one that should be updated + * on existing loggers. + * Used to filter property name streams. + * @param property a property key from the configuration. + * @return true if this property is of interest for updateConfiguration. + */ + static boolean matches(String property) { + return find(property).isPresent(); + } + + /** + * Returns true if the new property value is different from the old, + * and therefore needs to be updated on existing loggers. + * @param k a property key in the configuration + * @param previous the old configuration + * @param next the new configuration + * @return true if the property is changing value between the two + * configurations. + */ + static boolean needsUpdating(String k, Properties previous, Properties next) { + final String p = trim(previous.getProperty(k, null)); + final String n = trim(next.getProperty(k, null)); + return ! Objects.equals(p,n); + } + + /** + * Applies the mapping function for the given key to the next + * configuration. + * If the mapping function is null then this method does nothing. + * Otherwise, it calls the mapping function to compute the value + * that should be associated with {@code key} in the resulting + * configuration, and applies it to {@code next}. + * If the mapping function returns {@code null} the key is removed + * from {@code next}. + * + * @param k a property key in the configuration + * @param previous the old configuration + * @param next the new configuration (modified by this function) + * @param remappingFunction the mapping function. + */ + static void merge(String k, Properties previous, Properties next, + BiFunction mappingFunction) { + String p = trim(previous.getProperty(k, null)); + String n = trim(next.getProperty(k, null)); + String mapped = trim(mappingFunction.apply(p,n)); + if (!Objects.equals(n, mapped)) { + if (mapped == null) { + next.remove(k); + } else { + next.setProperty(k, mapped); + } + } + } + + private static final EnumSet ALL = + EnumSet.allOf(ConfigProperty.class); + } + + // trim the value if not null. + private static String trim(String value) { + return value == null ? null : value.trim(); + } + + /** + * An object that keep track of loggers we have already visited. + * Used when updating configuration, to avoid processing the same logger + * twice. + */ + static final class VisitedLoggers implements Predicate { + final IdentityHashMap visited; + private VisitedLoggers(IdentityHashMap visited) { + this.visited = visited; + } + VisitedLoggers() { + this(new IdentityHashMap<>()); + } + @Override + public boolean test(Logger logger) { + return visited != null && visited.put(logger, Boolean.TRUE) != null; + } + public void clear() { + if (visited != null) visited.clear(); + } + + // An object that considers that no logger has ever been visited. + // This is used when processParentHandlers is called from + // LoggerContext.addLocalLogger + static final VisitedLoggers NEVER = new VisitedLoggers(null); + } + + + /** + * Type of the modification for a given property. One of SAME, ADDED, CHANGED, + * or REMOVED. + */ + static enum ModType { + SAME, // property had no value in the old and new conf, or had the + // same value in both. + ADDED, // property had no value in the old conf, but has one in the new. + CHANGED, // property has a different value in the old conf and the new conf. + REMOVED; // property has no value in the new conf, but had one in the old. + static ModType of(String previous, String next) { + if (previous == null && next != null) { + return ADDED; + } + if (next == null && previous != null) { + return REMOVED; + } + if (!Objects.equals(trim(previous), trim(next))) { + return CHANGED; + } + return SAME; + } + } + + /** + * Updates the logging configuration. + *

          + * If the "java.util.logging.config.file" system property is set, + * then the property value specifies the properties file to be read + * as the new configuration. Otherwise, the LogManager default + * configuration is used. + *
          The default configuration is typically loaded from the + * properties file "{@code conf/logging.properties}" in the + * Java installation directory. + *

          + * This method reads the new configuration and calls the {@link + * #updateConfiguration(java.io.InputStream, java.util.function.Function) + * updateConfiguration(ins, mapper)} method to + * update the configuration. + * + * @apiNote + * This method updates the logging configuration from reading + * a properties file and ignores the "java.util.logging.config.class" + * system property. The "java.util.logging.config.class" property is + * only used by the {@link #readConfiguration()} method to load a custom + * configuration class as an initial configuration. + * + * @param mapper a functional interface that takes a configuration + * key k and returns a function f(o,n) whose returned + * value will be applied to the resulting configuration. The + * function f may return {@code null} to indicate that the property + * k will not be added to the resulting configuration. + *
          + * If {@code mapper} is {@code null} then {@code (k) -> ((o, n) -> n)} is + * assumed. + *
          + * For each k, the mapped function f will + * be invoked with the value associated with k in the old + * configuration (i.e o) and the value associated with + * k in the new configuration (i.e. n). + *
          A {@code null} value for o or n indicates that no + * value was present for k in the corresponding configuration. + * + * @throws SecurityException if a security manager exists and if + * the caller does not have LoggingPermission("control"), or + * does not have the permissions required to set up the + * configuration (e.g. open file specified for FileHandlers + * etc...) + * + * @throws NullPointerException if {@code mapper} returns a {@code null} + * function when invoked. + * + * @throws IOException if there are problems reading from the + * logging configuration file. + * + * @see #updateConfiguration(java.io.InputStream, java.util.function.Function) + */ + public void updateConfiguration(Function> mapper) + throws IOException { + checkPermission(); + ensureLogManagerInitialized(); + drainLoggerRefQueueBounded(); + + String fname = getConfigurationFileName(); + try (final InputStream in = new FileInputStream(fname)) { + final BufferedInputStream bin = new BufferedInputStream(in); + updateConfiguration(bin, mapper); + } + } + + /** + * Updates the logging configuration. + *

          + * For each configuration key in the {@linkplain + * #getProperty(java.lang.String) existing configuration} and + * the given input stream configuration, the given {@code mapper} function + * is invoked to map from the configuration key to a function, + * f(o,n), that takes the old value and new value and returns + * the resulting value to be applied in the resulting configuration, + * as specified in the table below. + *

          Let k be a configuration key in the old or new configuration, + * o be the old value (i.e. the value associated + * with k in the old configuration), n be the + * new value (i.e. the value associated with k in the new + * configuration), and f be the function returned + * by {@code mapper.apply(}k{@code )}: then v = f(o,n) is the + * resulting value. If v is not {@code null}, then a property + * k with value v will be added to the resulting configuration. + * Otherwise, it will be omitted. + *
          A {@code null} value may be passed to function + * f to indicate that the corresponding configuration has no + * configuration key k. + * The function f may return {@code null} to indicate that + * there will be no value associated with k in the resulting + * configuration. + *

          + * If {@code mapper} is {@code null}, then v will be set to + * n. + *

          + * LogManager {@linkplain #getProperty(java.lang.String) properties} are + * updated with the resulting value in the resulting configuration. + *

          + * The registered {@linkplain #addConfigurationListener configuration + * listeners} will be invoked after the configuration is successfully updated. + *

          + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
          PropertyResulting Behavior
          {@code .level} + *
            + *
          • If the resulting configuration defines a level for a logger and + * if the resulting level is different than the level specified in the + * the old configuration, or not specified in + * the old configuration, then if the logger exists or if children for + * that logger exist, the level for that logger will be updated, + * and the change propagated to any existing logger children. + * This may cause the logger to be created, if necessary. + *
          • + *
          • If the old configuration defined a level for a logger, and the + * resulting configuration doesn't, then this change will not be + * propagated to existing loggers, if any. + * To completely replace a configuration - the caller should therefore + * call {@link #reset() reset} to empty the current configuration, + * before calling {@code updateConfiguration}. + *
          • + *
          + *
          {@code .useParentHandlers} + *
            + *
          • If either the resulting or the old value for the useParentHandlers + * property is not null, then if the logger exists or if children for + * that logger exist, that logger will be updated to the resulting + * value. + * The value of the useParentHandlers property is the value specified + * in the configuration; if not specified, the default is true. + *
          • + *
          + *
          {@code .handlers} + *
            + *
          • If the resulting configuration defines a list of handlers for a + * logger, and if the resulting list is different than the list + * specified in the old configuration for that logger (that could be + * empty), then if the logger exists or its children exist, the + * handlers associated with that logger are closed and removed and + * the new handlers will be created per the resulting configuration + * and added to that logger, creating that logger if necessary. + *
          • + *
          • If the old configuration defined some handlers for a logger, and + * the resulting configuration doesn't, if that logger exists, + * its handlers will be removed and closed. + *
          • + *
          • Changing the list of handlers on an existing logger will cause all + * its previous handlers to be removed and closed, regardless of whether + * they had been created from the configuration or programmatically. + * The old handlers will be replaced by new handlers, if any. + *
          • + *
          + *
          {@code .*} + *
            + *
          • Properties configured/changed on handler classes will only affect + * newly created handlers. If a node is configured with the same list + * of handlers in the old and the resulting configuration, then these + * handlers will remain unchanged. + *
          • + *
          + *
          {@code config} and any other property + *
            + *
          • The resulting value for these property will be stored in the + * LogManager properties, but {@code updateConfiguration} will not parse + * or process their values. + *
          • + *
          + *
          + *

          + * Example mapper functions: + *

          + *

            + *
          • Replace all logging properties with the new configuration: + *

            {@code (k) -> ((o, n) -> n)}: + *

            this is equivalent to passing a null {@code mapper} parameter. + *
          • + *
          • Merge the new configuration and old configuration and use the + * new value if k exists in the new configuration: + *

            {@code (k) -> ((o, n) -> n == null ? o : n)}: + *

            as if merging two collections as follows: + * {@code result.putAll(oldc); result.putAll(newc)}.
          • + *
          • Merge the new configuration and old configuration and use the old + * value if k exists in the old configuration: + *

            {@code (k) -> ((o, n) -> o == null ? n : o)}: + *

            as if merging two collections as follows: + * {@code result.putAll(newc); result.putAll(oldc)}.
          • + *
          • Replace all properties with the new configuration except the handler + * property to configure Logger's handler that is not root logger: + *
            + *
            {@code (k) -> k.endsWith(".handlers")}
            +     *      {@code     ? ((o, n) -> (o == null ? n : o))}
            +     *      {@code     : ((o, n) -> n)}
            + *
          • + *
          + *

          + * To completely reinitialize a configuration, an application can first call + * {@link #reset() reset} to fully remove the old configuration, followed by + * {@code updateConfiguration} to initialize the new configuration. + * + * @param ins a stream to read properties from + * @param mapper a functional interface that takes a configuration + * key k and returns a function f(o,n) whose returned + * value will be applied to the resulting configuration. The + * function f may return {@code null} to indicate that the property + * k will not be added to the resulting configuration. + *
          + * If {@code mapper} is {@code null} then {@code (k) -> ((o, n) -> n)} is + * assumed. + *
          + * For each k, the mapped function f will + * be invoked with the value associated with k in the old + * configuration (i.e o) and the value associated with + * k in the new configuration (i.e. n). + *
          A {@code null} value for o or n indicates that no + * value was present for k in the corresponding configuration. + * + * @throws SecurityException if a security manager exists and if + * the caller does not have LoggingPermission("control"), or + * does not have the permissions required to set up the + * configuration (e.g. open files specified for FileHandlers) + * + * @throws NullPointerException if {@code ins} is null or if + * {@code mapper} returns a null function when invoked. + * + * @throws IOException if there are problems reading from the stream, + * or the given stream is not in the + * {@linkplain java.util.Properties properties file} format. + */ + public void updateConfiguration(InputStream ins, + Function> mapper) + throws IOException { + checkPermission(); + ensureLogManagerInitialized(); + drainLoggerRefQueueBounded(); + + final Properties previous; + final Set updatePropertyNames; + List cxs = Collections.emptyList(); + final VisitedLoggers visited = new VisitedLoggers(); + final Properties next = new Properties(); + + try { + // Load the properties + next.load(ins); + } catch (IllegalArgumentException x) { + // props.load may throw an IllegalArgumentException if the stream + // contains malformed Unicode escape sequences. + // We wrap that in an IOException as updateConfiguration is + // specified to throw IOException if there are problems reading + // from the stream. + // Note: new IOException(x.getMessage(), x) allow us to get a more + // concise error message than new IOException(x); + throw new IOException(x.getMessage(), x); + } + + if (globalHandlersState == STATE_SHUTDOWN) return; + + // exclusive lock: readConfiguration/reset/updateConfiguration can't + // run concurrently. + // configurationLock.writeLock().lock(); + configurationLock.lock(); + try { + if (globalHandlersState == STATE_SHUTDOWN) return; + previous = props; + + // Builds a TreeSet of all (old and new) property names. + updatePropertyNames = + Stream.concat(previous.stringPropertyNames().stream(), + next.stringPropertyNames().stream()) + .collect(Collectors.toCollection(TreeSet::new)); + + if (mapper != null) { + // mapper will potentially modify the content of + // 'next', so we need to call it before affecting props=next. + // give a chance to the mapper to control all + // properties - not just those we will reset. + updatePropertyNames.stream() + .forEachOrdered(k -> ConfigProperty + .merge(k, previous, next, + Objects.requireNonNull(mapper.apply(k)))); + } + + props = next; + + // allKeys will contain all keys: + // - which correspond to a configuration property we are interested in + // (first filter) + // - whose value needs to be updated (because it's new, removed, or + // different) in the resulting configuration (second filter) + final Stream allKeys = updatePropertyNames.stream() + .filter(ConfigProperty::matches) + .filter(k -> ConfigProperty.needsUpdating(k, previous, next)); + + // Group configuration properties by logger name + // We use a TreeMap so that parent loggers will be visited before + // child loggers. + final Map> loggerConfigs = + allKeys.collect(Collectors.groupingBy(ConfigProperty::getLoggerName, + TreeMap::new, + Collectors.toCollection(TreeSet::new))); + + if (!loggerConfigs.isEmpty()) { + cxs = contexts(); + } + final List loggers = cxs.isEmpty() + ? Collections.emptyList() : new ArrayList<>(cxs.size()); + for (Map.Entry> e : loggerConfigs.entrySet()) { + // This can be a logger name, or something else... + // The only thing we know is that we found a property + // we are interested in. + // For instance, if we found x.y.z.level, then x.y.z could be + // a logger, but it could also be a handler class... + // Anyway... + final String name = e.getKey(); + final Set properties = e.getValue(); + loggers.clear(); + for (LoggerContext cx : cxs) { + Logger l = cx.findLogger(name); + if (l != null && !visited.test(l)) { + loggers.add(l); + } + } + if (loggers.isEmpty()) continue; + for (String pk : properties) { + ConfigProperty cp = ConfigProperty.find(pk).get(); + String p = previous.getProperty(pk, null); + String n = next.getProperty(pk, null); + + // Determines the type of modification. + ModType mod = ModType.of(p, n); + + // mod == SAME means that the two values are equals, there + // is nothing to do. Usually, this should not happen as such + // properties should have been filtered above. + // It could happen however if the properties had + // trailing/leading whitespaces. + if (mod == ModType.SAME) continue; + + switch (cp) { + case LEVEL: + if (mod == ModType.REMOVED) continue; + Level level = Level.findLevel(trim(n)); + if (level != null) { + if (name.isEmpty()) { + rootLogger.setLevel(level); + } + for (Logger l : loggers) { + if (!name.isEmpty() || l != rootLogger) { + l.setLevel(level); + } + } + } + break; + case USEPARENT: + if (!name.isEmpty()) { + boolean useParent = getBooleanProperty(pk, true); + if (n != null || p != null) { + // reset the flag only if the previous value + // or the new value are not null. + for (Logger l : loggers) { + l.setUseParentHandlers(useParent); + } + } + } + break; + case HANDLERS: + List hdls = null; + if (name.isEmpty()) { + // special handling for the root logger. + globalHandlersState = STATE_READING_CONFIG; + try { + closeHandlers(rootLogger); + globalHandlersState = STATE_UNINITIALIZED; + } catch (Throwable t) { + globalHandlersState = STATE_INITIALIZED; + throw t; + } + } + for (Logger l : loggers) { + if (l == rootLogger) continue; + closeHandlers(l); + if (mod == ModType.REMOVED) { + closeOnResetLoggers.removeIf(c -> c.logger == l); + continue; + } + if (hdls == null) { + hdls = name.isEmpty() + ? Arrays.asList(rootLogger.getHandlers()) + : createLoggerHandlers(name, pk); + } + setLoggerHandlers(l, name, pk, hdls); + } + break; + default: break; + } + } + } + } finally { + configurationLock.unlock(); + visited.clear(); + } + + // Now ensure that if an existing logger has acquired a new parent + // in the configuration, this new parent will be created - if needed, + // and added to the context of the existing child. + // + drainLoggerRefQueueBounded(); + for (LoggerContext cx : cxs) { + for (Enumeration names = cx.getLoggerNames() ; names.hasMoreElements();) { + String name = names.nextElement(); + if (name.isEmpty()) continue; // don't need to process parents on root. + Logger l = cx.findLogger(name); + if (l != null && !visited.test(l)) { + // should pass visited here to cut the processing when + // reaching a logger already visited. + cx.processParentHandlers(l, name, visited); + } + } + } + + // We changed the configuration: invoke configuration listeners + invokeConfigurationListeners(); + } + /** * Get the value of a logging property. * The method returns null if the property is not found. diff -r 9d153359b8ff -r 470068717ae2 jdk/src/jdk.localedata/share/classes/sun/util/resources/en/GB/TimeZoneNames_en_GB.java --- a/jdk/src/jdk.localedata/share/classes/sun/util/resources/en/GB/TimeZoneNames_en_GB.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/jdk.localedata/share/classes/sun/util/resources/en/GB/TimeZoneNames_en_GB.java Wed Jul 05 20:54:48 2017 +0200 @@ -47,7 +47,8 @@ protected final Object[][] getContents() { return new Object[][] { {"Europe/London", new String[] {"Greenwich Mean Time", "GMT", - "British Summer Time", "BST"}}, + "British Summer Time", "BST", + "British Time", "BT"}}, }; } } diff -r 9d153359b8ff -r 470068717ae2 jdk/src/jdk.localedata/share/classes/sun/util/resources/hi/TimeZoneNames_hi.java --- a/jdk/src/jdk.localedata/share/classes/sun/util/resources/hi/TimeZoneNames_hi.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/src/jdk.localedata/share/classes/sun/util/resources/hi/TimeZoneNames_hi.java Wed Jul 05 20:54:48 2017 +0200 @@ -40,7 +40,8 @@ {"Asia/Calcutta", new String[] { "\u092d\u093e\u0930\u0924\u0940\u092f \u0938\u092e\u092f", "IST", - "\u092d\u093e\u0930\u0924\u0940\u092f \u0938\u092e\u092f", "IST" + "\u092d\u093e\u0930\u0924\u0940\u092f \u0938\u092e\u092f", "IST", + "\u092d\u093e\u0930\u0924\u0940\u092f \u0938\u092e\u092f", "IT" } }, }; diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/lang/Math/CubeRootTests.java --- a/jdk/test/java/lang/Math/CubeRootTests.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/lang/Math/CubeRootTests.java Wed Jul 05 20:54:48 2017 +0200 @@ -24,7 +24,7 @@ /* * @test * @library /lib/testlibrary/ - * @build jdk.testlibrary.* + * @build jdk.testlibrary.RandomFactory * @run main CubeRootTests * @bug 4347132 4939441 8078672 * @summary Tests for {Math, StrictMath}.cbrt (use -Dseed=X to set PRNG seed) diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/lang/Math/HypotTests.java --- a/jdk/test/java/lang/Math/HypotTests.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/lang/Math/HypotTests.java Wed Jul 05 20:54:48 2017 +0200 @@ -24,7 +24,7 @@ /* * @test * @library /lib/testlibrary/ - * @build jdk.testlibrary.* + * @build jdk.testlibrary.RandomFactory * @run main HypotTests * @bug 4851638 4939441 8078672 * @summary Tests for {Math, StrictMath}.hypot (use -Dseed=X to set PRNG seed) diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/lang/Math/IeeeRecommendedTests.java --- a/jdk/test/java/lang/Math/IeeeRecommendedTests.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/lang/Math/IeeeRecommendedTests.java Wed Jul 05 20:54:48 2017 +0200 @@ -24,7 +24,7 @@ /* * @test * @library /lib/testlibrary/ - * @build jdk.testlibrary.* + * @build jdk.testlibrary.RandomFactory * @run main IeeeRecommendedTests * @bug 4860891 4826732 4780454 4939441 4826652 8078672 * @summary Tests for IEEE 754[R] recommended functions and similar methods (use -Dseed=X to set PRNG seed) diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/lang/Math/Log1pTests.java --- a/jdk/test/java/lang/Math/Log1pTests.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/lang/Math/Log1pTests.java Wed Jul 05 20:54:48 2017 +0200 @@ -24,7 +24,7 @@ /* * @test * @library /lib/testlibrary/ - * @build jdk.testlibrary.* + * @build jdk.testlibrary.RandomFactory * @run main Log1pTests * @bug 4851638 4939441 8078672 * @summary Tests for {Math, StrictMath}.log1p (use -Dseed=X to set PRNG seed) diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/lang/StrictMath/CubeRootTests.java --- a/jdk/test/java/lang/StrictMath/CubeRootTests.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/lang/StrictMath/CubeRootTests.java Wed Jul 05 20:54:48 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2004, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2015, 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 @@ -23,11 +23,20 @@ /* * @test - * @bug 4347132 + * @bug 4347132 8136799 + * @key randomness + * @library /lib/testlibrary/ + * @build jdk.testlibrary.RandomFactory + * @build Tests + * @build FdlibmTranslit + * @build CubeRootTests + * @run main CubeRootTests * @summary Tests specifically for StrictMath.cbrt * @author Joseph D. Darcy */ +import jdk.testlibrary.RandomFactory; + /** * The tests in ../Math/CubeRootTests.java test properties that should * hold for any cube root implementation, including the FDLIBM-based @@ -42,6 +51,19 @@ public class CubeRootTests { private CubeRootTests(){} + public static void main(String [] argv) { + int failures = 0; + + failures += testCubeRoot(); + failures += testAgainstTranslit(); + + if (failures > 0) { + System.err.println("Testing the cube root incurred " + + failures + " failures."); + throw new RuntimeException(); + } + } + static int testCubeRootCase(double input, double expected) { int failures=0; @@ -458,16 +480,44 @@ return failures; } + // Initialize shared random number generator + private static java.util.Random random = RandomFactory.getRandom(); - public static void main(String [] argv) { + /** + * Test StrictMath.cbrt against transliteration port of cbrt. + */ + private static int testAgainstTranslit() { int failures = 0; + double x; - failures += testCubeRoot(); + // Test just above subnormal threshold... + x = Double.MIN_NORMAL; + failures += testRange(x, Math.ulp(x), 1000); + + // ... and just below subnormal threshold ... + x = Math.nextDown(Double.MIN_NORMAL); + failures += testRange(x, -Math.ulp(x), 1000); - if (failures > 0) { - System.err.println("Testing the cube root incurred " - + failures + " failures."); - throw new RuntimeException(); + // ... and near zero. + failures += testRange(0.0, Double.MIN_VALUE, 1000); + + x = Tests.createRandomDouble(random); + + // Make the increment twice the ulp value in case the random + // value is near an exponent threshold. Don't worry about test + // elements overflowing to infinity if the starting value is + // near Double.MAX_VALUE. + failures += testRange(x, 2.0 * Math.ulp(x), 1000); + + return failures; + } + + private static int testRange(double start, double increment, int count) { + int failures = 0; + double x = start; + for (int i = 0; i < count; i++, x += increment) { + failures += testCubeRootCase(x, FdlibmTranslit.Cbrt.compute(x)); } + return failures; } } diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/lang/StrictMath/FdlibmTranslit.java --- a/jdk/test/java/lang/StrictMath/FdlibmTranslit.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/lang/StrictMath/FdlibmTranslit.java Wed Jul 05 20:54:48 2017 +0200 @@ -73,6 +73,67 @@ } /** + * cbrt(x) + * Return cube root of x + */ + public static class Cbrt { + // unsigned + private static final int B1 = 715094163; /* B1 = (682-0.03306235651)*2**20 */ + private static final int B2 = 696219795; /* B2 = (664-0.03306235651)*2**20 */ + + private static final double C = 5.42857142857142815906e-01; /* 19/35 = 0x3FE15F15, 0xF15F15F1 */ + private static final double D = -7.05306122448979611050e-01; /* -864/1225 = 0xBFE691DE, 0x2532C834 */ + private static final double E = 1.41428571428571436819e+00; /* 99/70 = 0x3FF6A0EA, 0x0EA0EA0F */ + private static final double F = 1.60714285714285720630e+00; /* 45/28 = 0x3FF9B6DB, 0x6DB6DB6E */ + private static final double G = 3.57142857142857150787e-01; /* 5/14 = 0x3FD6DB6D, 0xB6DB6DB7 */ + + public static strictfp double compute(double x) { + int hx; + double r, s, t=0.0, w; + int sign; // unsigned + + hx = __HI(x); // high word of x + sign = hx & 0x80000000; // sign= sign(x) + hx ^= sign; + if (hx >= 0x7ff00000) + return (x+x); // cbrt(NaN,INF) is itself + if ((hx | __LO(x)) == 0) + return(x); // cbrt(0) is itself + + x = __HI(x, hx); // x <- |x| + // rough cbrt to 5 bits + if (hx < 0x00100000) { // subnormal number + t = __HI(t, 0x43500000); // set t= 2**54 + t *= x; + t = __HI(t, __HI(t)/3+B2); + } else { + t = __HI(t, hx/3+B1); + } + + // new cbrt to 23 bits, may be implemented in single precision + r = t * t/x; + s = C + r*t; + t *= G + F/(s + E + D/s); + + // chopped to 20 bits and make it larger than cbrt(x) + t = __LO(t, 0); + t = __HI(t, __HI(t)+0x00000001); + + + // one step newton iteration to 53 bits with error less than 0.667 ulps + s = t * t; // t*t is exact + r = x / s; + w = t + t; + r= (r - t)/(w + r); // r-s is exact + t= t + t*r; + + // retore the sign bit + t = __HI(t, __HI(t) | sign); + return(t); + } + } + + /** * hypot(x,y) * * Method : diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/lang/StrictMath/HypotTests.java --- a/jdk/test/java/lang/StrictMath/HypotTests.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/lang/StrictMath/HypotTests.java Wed Jul 05 20:54:48 2017 +0200 @@ -27,7 +27,7 @@ * @key randomness * @summary Tests for StrictMath.hypot * @library /lib/testlibrary/ - * @build jdk.testlibrary.* + * @build jdk.testlibrary.RandomFactory * @build Tests * @build FdlibmTranslit * @build HypotTests diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/rmi/registry/readTest/readTest.sh --- a/jdk/test/java/rmi/registry/readTest/readTest.sh Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/rmi/registry/readTest/readTest.sh Wed Jul 05 20:54:48 2017 +0200 @@ -27,6 +27,7 @@ # @build TestLibrary # @summary remove java.rmi.server.codebase property parsing from registyimpl # @run shell readTest.sh +# @key intermittent OS=`uname -s` VER=`uname -r` diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/AbstractList/CheckForComodification.java --- a/jdk/test/java/util/AbstractList/CheckForComodification.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/AbstractList/CheckForComodification.java Wed Jul 05 20:54:48 2017 +0200 @@ -42,7 +42,7 @@ for (int i : list) if (i == LENGTH - 2) list.remove(i); - } catch(ConcurrentModificationException e) { + } catch (ConcurrentModificationException e) { return; } throw new RuntimeException("No ConcurrentModificationException"); diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/AbstractList/FailFastIterator.java --- a/jdk/test/java/util/AbstractList/FailFastIterator.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/AbstractList/FailFastIterator.java Wed Jul 05 20:54:48 2017 +0200 @@ -24,7 +24,7 @@ /** * @test * @bug 4189896 - * @summary AbstractList iterators previously checked for co-modificatin + * @summary AbstractList iterators previously checked for co-modification * *after* the set/add/remove operations were performed. */ @@ -44,7 +44,7 @@ copy.add(new Integer(99)); i.remove(); throw new Exception("remove: iterator didn't fail fast"); - } catch(ConcurrentModificationException e) { + } catch (ConcurrentModificationException e) { } if (!copy.equals(orig)) throw new Exception("remove: iterator didn't fail fast enough"); @@ -56,7 +56,7 @@ copy.add(new Integer(99)); i.set(new Integer(666)); throw new Exception("set: iterator didn't fail fast"); - } catch(ConcurrentModificationException e) { + } catch (ConcurrentModificationException e) { } if (!copy.equals(orig)) throw new Exception("set: iterator didn't fail fast enough"); @@ -67,7 +67,7 @@ copy.add(new Integer(99)); i.add(new Integer(666)); throw new Exception("add: iterator didn't fail fast"); - } catch(ConcurrentModificationException e) { + } catch (ConcurrentModificationException e) { } if (!copy.equals(orig)) throw new Exception("add: iterator didn't fail fast enough"); diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/Collection/BiggernYours.java --- a/jdk/test/java/util/Collection/BiggernYours.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/Collection/BiggernYours.java Wed Jul 05 20:54:48 2017 +0200 @@ -41,16 +41,16 @@ Object[] c2Array = c2.toArray(); check(c1Array.length == c2Array.length); - for(Object aC1 : c1Array) { + for (Object aC1 : c1Array) { boolean found = false; - for(Object aC2 : c2Array) { - if(Objects.equals(aC1, aC2)) { + for (Object aC2 : c2Array) { + if (Objects.equals(aC1, aC2)) { found = true; break; } } - if(!found) + if (!found) fail(aC1 + " not found in " + Arrays.toString(c2Array)); } } diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/Collection/MOAT.java --- a/jdk/test/java/util/Collection/MOAT.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/Collection/MOAT.java Wed Jul 05 20:54:48 2017 +0200 @@ -101,7 +101,12 @@ testMap(new HashMap()); testMap(new LinkedHashMap()); - testMap(new WeakHashMap()); + + // TODO: Add reliable support for WeakHashMap. + // This test is subject to very rare failures because the GC + // may remove unreferenced-keys from the map at any time. + // testMap(new WeakHashMap()); + testMap(new IdentityHashMap()); testMap(new TreeMap()); testMap(new Hashtable()); @@ -343,6 +348,12 @@ return true; } + // 6260652: (coll) Arrays.asList(x).toArray().getClass() + // should be Object[].class + // Fixed in jdk9, but not jdk8 ... + static final boolean needToWorkAround6260652 = + Arrays.asList("").toArray().getClass() != Object[].class; + private static void checkFunctionalInvariants(Collection c) { try { checkContainsSelf(c); @@ -356,7 +367,10 @@ } check(c.toArray().length == c.size()); - check(c.toArray().getClass() == Object[].class); + check(c.toArray().getClass() == Object[].class + || + (needToWorkAround6260652 && + c.getClass().getName().equals("java.util.Arrays$ArrayList"))); for (int size : new int[]{0,1,c.size(), c.size()+1}) { Integer[] a = c.toArray(new Integer[size]); check((size > c.size()) || a.length == c.size()); @@ -408,7 +422,6 @@ catch (Throwable t) { unexpected(t); } } - //---------------------------------------------------------------- // If add("x") succeeds, contains("x") & remove("x") should succeed //---------------------------------------------------------------- @@ -1291,7 +1304,7 @@ equalNext(descItr, expected[idx--]); descItr.remove(); - while(idx >= 0 && descItr.hasNext()) { + while (idx >= 0 && descItr.hasNext()) { equalNext(descItr, expected[idx--]); } equal(descItr.hasNext(), false); diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/Collection/testlibrary/CollectionAsserts.java --- a/jdk/test/java/util/Collection/testlibrary/CollectionAsserts.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/Collection/testlibrary/CollectionAsserts.java Wed Jul 05 20:54:48 2017 +0200 @@ -113,7 +113,7 @@ } Set uniq = new HashSet<>(); - while(iter.hasNext()) { + while (iter.hasNext()) { T each = iter.next(); assertTrue(!uniq.contains(each)); uniq.add(each); @@ -209,7 +209,7 @@ assertTrue(!pI.hasNext()); } - while(mI.hasNext()) { + while (mI.hasNext()) { pI = mI.next().iterator(); assertTrue(!pI.hasNext()); } diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/Collections/BigBinarySearch.java --- a/jdk/test/java/util/Collections/BigBinarySearch.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/Collections/BigBinarySearch.java Wed Jul 05 20:54:48 2017 +0200 @@ -61,13 +61,13 @@ } } - /** Check that binarySearch finds an element where we got it */ + /** Checks that binarySearch finds an element where we got it. */ private static void checkBinarySearch(List l, int i) { try { equal(i, Collections.binarySearch(l, l.get(i))); } catch (Throwable t) { unexpected(t); } } - /** Check that binarySearch finds an element where we got it */ + /** Checks that binarySearch finds an element where we got it. */ private static void checkBinarySearch(List l, int i, Comparator comparator) { try { equal(i, Collections.binarySearch(l, l.get(i), comparator)); } diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/Collections/BinarySearchNullComparator.java --- a/jdk/test/java/util/Collections/BinarySearchNullComparator.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/Collections/BinarySearchNullComparator.java Wed Jul 05 20:54:48 2017 +0200 @@ -27,11 +27,10 @@ * @summary Test Collections.binarySearch() with a null comparator */ - import java.util.*; public class BinarySearchNullComparator { - public static void main (String args[]) throws Exception { + public static void main(String args[]) throws Exception { List list = Arrays.asList(new String[] {"I", "Love", "You"}); int result = Collections.binarySearch(list, "You", null); diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/Collections/CheckedListBash.java --- a/jdk/test/java/util/Collections/CheckedListBash.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/Collections/CheckedListBash.java Wed Jul 05 20:54:48 2017 +0200 @@ -109,19 +109,19 @@ List even = clone(s); Iterator it = even.iterator(); - while(it.hasNext()) - if(((Integer)it.next()).intValue() % 2 == 1) + while (it.hasNext()) + if (((Integer)it.next()).intValue() % 2 == 1) it.remove(); it = even.iterator(); - while(it.hasNext()) - if(((Integer)it.next()).intValue() % 2 == 1) + while (it.hasNext()) + if (((Integer)it.next()).intValue() % 2 == 1) fail("Failed to remove all odd nubmers."); List odd = clone(s); for (int i=0; i<(listSize/2); i++) odd.remove(i); for (int i=0; i<(listSize/2); i++) - if(((Integer)odd.get(i)).intValue() % 2 != 1) + if (((Integer)odd.get(i)).intValue() % 2 != 1) fail("Failed to remove all even nubmers."); List all = clone(odd); @@ -145,8 +145,8 @@ } itAll = all.listIterator(); it = s.iterator(); - while(it.hasNext()) - if(it.next()==itAll.next()) + while (it.hasNext()) + if (it.next()==itAll.next()) fail("Iterator.set failed to change value."); if (!all.equals(s)) fail("Failed to reconstruct ints with ListIterator."); @@ -215,10 +215,10 @@ int preSize = s.size(); if (!s.add(e)) - fail ("Add failed."); + fail("Add failed."); int postSize = s.size(); if (postSize-preSize != 1) - fail ("Add didn't increase size by 1."); + fail("Add didn't increase size by 1."); } } diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/Collections/CheckedMapBash.java --- a/jdk/test/java/util/Collections/CheckedMapBash.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/Collections/CheckedMapBash.java Wed Jul 05 20:54:48 2017 +0200 @@ -134,7 +134,7 @@ fail("clear failed."); Iterator i = m.entrySet().iterator(); - while(i.hasNext()) { + while (i.hasNext()) { i.next(); i.remove(); } @@ -142,12 +142,11 @@ fail("Iterator.remove() failed"); } - @DataProvider(name = "Bash.Supplier>", parallel = true) public static Iterator bashNavigableMapProvider() { ArrayList iters = new ArrayList<>(makeCheckedMaps()); iters.ensureCapacity(numItr * iters.size()); - for(int each=1; each < numItr; each++) { + for (int each=1; each < numItr; each++) { iters.addAll( makeCheckedMaps()); } return iters.iterator(); diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/Collections/CheckedSetBash.java --- a/jdk/test/java/util/Collections/CheckedSetBash.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/Collections/CheckedSetBash.java Wed Jul 05 20:54:48 2017 +0200 @@ -130,14 +130,14 @@ boolean prePresent = s.contains(e); boolean added = s.add(e); if (!s.contains(e)) - fail ("Element not present after addition."); + fail("Element not present after addition."); if (added == prePresent) - fail ("added == alreadyPresent"); + fail("added == alreadyPresent"); int postSize = s.size(); if (added && preSize == postSize) - fail ("Add returned true, but size didn't change."); + fail("Add returned true, but size didn't change."); if (!added && preSize != postSize) - fail ("Add returned false, but size changed."); + fail("Add returned false, but size changed."); } } @@ -145,7 +145,7 @@ public static Iterator navigableSetsProvider() { ArrayList iters = new ArrayList<>(makeCheckedSets()); iters.ensureCapacity(numItr * iters.size()); - for(int each=1; each < numItr; each++) { + for (int each=1; each < numItr; each++) { iters.addAll( makeCheckedSets()); } return iters.iterator(); diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/Collections/EmptyCollectionSerialization.java --- a/jdk/test/java/util/Collections/EmptyCollectionSerialization.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/Collections/EmptyCollectionSerialization.java Wed Jul 05 20:54:48 2017 +0200 @@ -62,7 +62,7 @@ copy.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(copy)) + " is not the singleton " + singleton.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(singleton))); - } catch(Exception all) { + } catch (Exception all) { fail(description + ": Unexpected Exception", all); } } diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/Collections/EmptyIterator.java --- a/jdk/test/java/util/Collections/EmptyIterator.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/Collections/EmptyIterator.java Wed Jul 05 20:54:48 2017 +0200 @@ -29,13 +29,14 @@ import static java.util.Collections.*; import java.util.*; +import java.util.concurrent.SynchronousQueue; public class EmptyIterator { void test(String[] args) throws Throwable { testEmptyCollection(emptyList()); testEmptyCollection(emptySet()); - + testEmptyCollection(new SynchronousQueue()); testEmptyMap(emptyMap()); Hashtable emptyTable = new Hashtable<>(); diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/Collections/EmptyNavigableMap.java --- a/jdk/test/java/util/Collections/EmptyNavigableMap.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/Collections/EmptyNavigableMap.java Wed Jul 05 20:54:48 2017 +0200 @@ -282,7 +282,7 @@ // slightly smaller NavigableMap ns = subMap.subMap(first, false, last, false); - // slight exapansion + // slight expansion assertThrows(() -> { ns.subMap(first, true, last, true); }, @@ -303,7 +303,7 @@ // slightly smaller NavigableMap ns = subMap.headMap(BigInteger.ONE, false); - // slight exapansion + // slight expansion assertThrows(() -> { ns.headMap(BigInteger.ONE, true); }, @@ -324,7 +324,7 @@ // slightly smaller NavigableMap ns = subMap.tailMap(BigInteger.ONE, false); - // slight exapansion + // slight expansion assertThrows(() -> { ns.tailMap(BigInteger.ONE, true); }, diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/Collections/EmptyNavigableSet.java --- a/jdk/test/java/util/Collections/EmptyNavigableSet.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/Collections/EmptyNavigableSet.java Wed Jul 05 20:54:48 2017 +0200 @@ -301,7 +301,7 @@ // slightly smaller NavigableSet ns = subSet.subSet(first, false, last, false); - // slight exapansion + // slight expansion assertThrows(() -> { ns.subSet(first, true, last, true); }, @@ -322,7 +322,7 @@ // slightly smaller NavigableSet ns = subSet.headSet(BigInteger.ONE, false); - // slight exapansion + // slight expansion assertThrows(() -> { ns.headSet(BigInteger.ONE, true); }, @@ -343,7 +343,7 @@ // slightly smaller NavigableSet ns = subSet.tailSet(BigInteger.ONE, false); - // slight exapansion + // slight expansion assertThrows(() -> { ns.tailSet(BigInteger.ONE, true); }, diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/Collections/RacingCollections.java --- a/jdk/test/java/util/Collections/RacingCollections.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/Collections/RacingCollections.java Wed Jul 05 20:54:48 2017 +0200 @@ -62,14 +62,16 @@ this.start(); } - @SuppressWarnings("unchecked") void clear(Object o) { + @SuppressWarnings("unchecked") + void clear(Object o) { if (o instanceof Collection) ((Collection)o).clear(); else ((Map)o).clear(); } - @SuppressWarnings("unchecked") void realRun() { + @SuppressWarnings("unchecked") + void realRun() { // Mutate elLoco wildly forever, checking occasionally for "done" clear(elLoco); if (elLoco instanceof List) { @@ -156,7 +158,7 @@ quittingTime = System.nanoTime() + workTimeMillis * 1024 * 1024; } boolean keepGoing() { - return (i++ % 128 != 0) || (System.nanoTime() < quittingTime); + return (i++ % 128 != 0) || (System.nanoTime() - quittingTime < 0); } } @@ -233,6 +235,7 @@ private static List> newConcurrentQueues() { List> list = new ArrayList>(newConcurrentDeques()); + list.add(new ArrayBlockingQueue(10)); list.add(new LinkedBlockingQueue(10)); list.add(new LinkedTransferQueue()); list.add(new ConcurrentLinkedQueue()); diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/Collections/ReverseOrder.java --- a/jdk/test/java/util/Collections/ReverseOrder.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/Collections/ReverseOrder.java Wed Jul 05 20:54:48 2017 +0200 @@ -25,7 +25,7 @@ * @test * @bug 4593209 8001667 * @summary Reverse comparator was subtly broken - * @author Josh bloch + * @author Josh Bloch */ import java.util.*; diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/Collections/RotateEmpty.java --- a/jdk/test/java/util/Collections/RotateEmpty.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/Collections/RotateEmpty.java Wed Jul 05 20:54:48 2017 +0200 @@ -33,6 +33,6 @@ public static void main(String[] args) throws Exception { List l = new ArrayList(); - Collections.rotate (l, 1); + Collections.rotate(l, 1); } } diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/Collections/T6433170.java --- a/jdk/test/java/util/Collections/T6433170.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/Collections/T6433170.java Wed Jul 05 20:54:48 2017 +0200 @@ -58,7 +58,6 @@ checkEmpty(checked); } - //--------------------- Infrastructure --------------------------- volatile int passed = 0, failed = 0; void pass() {passed++;} diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/Collections/WrappedNull.java --- a/jdk/test/java/util/Collections/WrappedNull.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/Collections/WrappedNull.java Wed Jul 05 20:54:48 2017 +0200 @@ -33,133 +33,133 @@ public class WrappedNull { public static void main(String argv[]) throws Exception { boolean testSucceeded = false; - try{ + try { List l = Arrays.asList(null); } catch (NullPointerException e) { testSucceeded = true; } - if(!testSucceeded) + if (!testSucceeded) throw new Exception("Arrays.asList"); testSucceeded = false; - try{ + try { Collection c = Collections.unmodifiableCollection(null); } catch (NullPointerException e) { testSucceeded = true; } - if(!testSucceeded) + if (!testSucceeded) throw new Exception("unmodifiableCollection"); testSucceeded = false; - try{ + try { Set c = Collections.unmodifiableSet(null); } catch (NullPointerException e) { testSucceeded = true; } - if(!testSucceeded) + if (!testSucceeded) throw new Exception("unmodifiableSet"); testSucceeded = false; - try{ + try { List c = Collections.unmodifiableList(null); } catch (NullPointerException e) { testSucceeded = true; } - if(!testSucceeded) + if (!testSucceeded) throw new Exception("unmodifiableList"); testSucceeded = false; - try{ + try { Map c = Collections.unmodifiableMap(null); } catch (NullPointerException e) { testSucceeded = true; } - if(!testSucceeded) + if (!testSucceeded) throw new Exception("unmodifiableMap"); testSucceeded = false; - try{ + try { SortedSet c = Collections.unmodifiableSortedSet(null); } catch (NullPointerException e) { testSucceeded = true; } - if(!testSucceeded) + if (!testSucceeded) throw new Exception("unmodifiableSortedSet"); testSucceeded = false; - try{ + try { SortedMap c = Collections.unmodifiableSortedMap(null); } catch (NullPointerException e) { testSucceeded = true; } - if(!testSucceeded) + if (!testSucceeded) throw new Exception("unmodifiableSortedMap"); testSucceeded = false; - try{ + try { Collection c = Collections.synchronizedCollection(null); } catch (NullPointerException e) { testSucceeded = true; } - if(!testSucceeded) + if (!testSucceeded) throw new Exception("synchronizedCollection"); testSucceeded = false; - try{ + try { Set c = Collections.synchronizedSet(null); } catch (NullPointerException e) { testSucceeded = true; } - if(!testSucceeded) + if (!testSucceeded) throw new Exception("synchronizedSet"); testSucceeded = false; - try{ + try { List c = Collections.synchronizedList(null); } catch (NullPointerException e) { testSucceeded = true; } - if(!testSucceeded) + if (!testSucceeded) throw new Exception("synchronizedList"); testSucceeded = false; - try{ + try { Map c = Collections.synchronizedMap(null); } catch (NullPointerException e) { testSucceeded = true; } - if(!testSucceeded) + if (!testSucceeded) throw new Exception("synchronizedMap"); testSucceeded = false; - try{ + try { SortedSet c = Collections.synchronizedSortedSet(null); } catch (NullPointerException e) { testSucceeded = true; } - if(!testSucceeded) + if (!testSucceeded) throw new Exception("synchronizedSortedSet"); testSucceeded = false; - try{ + try { SortedMap c = Collections.synchronizedSortedMap(null); } catch (NullPointerException e) { testSucceeded = true; } - if(!testSucceeded) + if (!testSucceeded) throw new Exception("synchronizedSortedMap"); // Make sure that non-null arguments don't throw exc. diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/Hashtable/IllegalLoadFactor.java --- a/jdk/test/java/util/Hashtable/IllegalLoadFactor.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/Hashtable/IllegalLoadFactor.java Wed Jul 05 20:54:48 2017 +0200 @@ -26,8 +26,6 @@ @summary Test for an illegalargumentexception on loadFactor */ - - import java.util.*; /** @@ -38,92 +36,92 @@ public static void main(String argv[]) throws Exception { boolean testSucceeded = false; - try{ + try { // this should generate an IllegalArgumentException Hashtable bad1 = new Hashtable(100, -3); } catch (IllegalArgumentException e1) { testSucceeded = true; } - if(!testSucceeded) + if (!testSucceeded) throw new Exception("Hashtable, negative load factor"); testSucceeded = false; - try{ + try { // this should generate an IllegalArgumentException Hashtable bad1 = new Hashtable(100, Float.NaN); } catch (IllegalArgumentException e1) { testSucceeded = true; } - if(!testSucceeded) + if (!testSucceeded) throw new Exception("Hashtable, NaN load factor"); testSucceeded = false; - try{ + try { // this should generate an IllegalArgumentException HashMap bad1 = new HashMap(100, -3); } catch (IllegalArgumentException e1) { testSucceeded = true; } - if(!testSucceeded) + if (!testSucceeded) throw new Exception("HashMap, negative load factor"); testSucceeded = false; - try{ + try { // this should generate an IllegalArgumentException HashMap bad1 = new HashMap(100, Float.NaN); } catch (IllegalArgumentException e1) { testSucceeded = true; } - if(!testSucceeded) + if (!testSucceeded) throw new Exception("HashMap, NaN load factor"); testSucceeded = false; - try{ + try { // this should generate an IllegalArgumentException HashSet bad1 = new HashSet(100, -3); } catch (IllegalArgumentException e1) { testSucceeded = true; } - if(!testSucceeded) + if (!testSucceeded) throw new Exception("HashSet, negative load factor"); testSucceeded = false; - try{ + try { // this should generate an IllegalArgumentException HashSet bad1 = new HashSet(100, Float.NaN); } catch (IllegalArgumentException e1) { testSucceeded = true; } - if(!testSucceeded) + if (!testSucceeded) throw new Exception("HashSet, NaN load factor"); testSucceeded = false; - try{ + try { // this should generate an IllegalArgumentException WeakHashMap bad1 = new WeakHashMap(100, -3); } catch (IllegalArgumentException e1) { testSucceeded = true; } - if(!testSucceeded) + if (!testSucceeded) throw new Exception("WeakHashMap, negative load factor"); testSucceeded = false; - try{ + try { // this should generate an IllegalArgumentException WeakHashMap bad1 = new WeakHashMap(100, Float.NaN); } catch (IllegalArgumentException e1) { testSucceeded = true; } - if(!testSucceeded) + if (!testSucceeded) throw new Exception("WeakHashMap, NaN load factor"); // Make sure that legal creates don't throw exceptions diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/Hashtable/ReadObject.java --- a/jdk/test/java/util/Hashtable/ReadObject.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/Hashtable/ReadObject.java Wed Jul 05 20:54:48 2017 +0200 @@ -57,13 +57,13 @@ public Object get(Object key) { ValueWrapper valueWrapper = (ValueWrapper)super.get(key); Object value = valueWrapper.getValue(); - if(value instanceof ValueWrapper) + if (value instanceof ValueWrapper) throw new RuntimeException("Hashtable.get bug"); return value; } public Object put(Object key, Object value) { - if(value instanceof ValueWrapper) + if (value instanceof ValueWrapper) throw new RuntimeException( "Hashtable.put bug: value is already wrapped"); ValueWrapper valueWrapper = new ValueWrapper(value); @@ -98,4 +98,4 @@ ReadObject myHashtableCopy = (ReadObject)copyObject(myHashtable); String value = (String)myHashtableCopy.get("key"); } -}; +} diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/IdentityHashMap/ToString.java --- a/jdk/test/java/util/IdentityHashMap/ToString.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/IdentityHashMap/ToString.java Wed Jul 05 20:54:48 2017 +0200 @@ -28,7 +28,6 @@ * @author Josh Bloch */ - import java.util.*; public class ToString { diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/LinkedHashMap/Basic.java --- a/jdk/test/java/util/LinkedHashMap/Basic.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/LinkedHashMap/Basic.java Wed Jul 05 20:54:48 2017 +0200 @@ -83,12 +83,12 @@ Map m = new LinkedHashMap(); for (int i=0; i m2 = new LinkedHashMap(); m2.putAll(m); if (!m.equals(m2)) throw new Exception("Clone not equal to original. (1)"); @@ -129,7 +129,7 @@ throw new Exception("clear failed."); Iterator it = m.entrySet().iterator(); - while(it.hasNext()) { + while (it.hasNext()) { it.next(); it.remove(); } @@ -240,8 +240,8 @@ for (int i=0; i MAP_SIZE; diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/LinkedHashSet/Basic.java --- a/jdk/test/java/util/LinkedHashSet/Basic.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/LinkedHashSet/Basic.java Wed Jul 05 20:54:48 2017 +0200 @@ -99,7 +99,7 @@ static Set clone(Set s) throws Exception { Set clone; int method = rnd.nextInt(3); - clone = (method==0 ? (Set) ((LinkedHashSet)s).clone() : + clone = (method==0 ? (Set) ((LinkedHashSet)s).clone() : (method==1 ? new LinkedHashSet(Arrays.asList(s.toArray())) : serClone(s))); if (!s.equals(clone)) @@ -126,7 +126,7 @@ ObjectInputStream in = new ObjectInputStream(bis); result = (Set)in.readObject(); in.close(); - } catch(Exception e) { + } catch (Exception e) { e.printStackTrace(); } return result; diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/LinkedList/Clone.java --- a/jdk/test/java/util/LinkedList/Clone.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/LinkedList/Clone.java Wed Jul 05 20:54:48 2017 +0200 @@ -74,7 +74,7 @@ throw new RuntimeException("TreeMap.clone() is broken."); } - private static class LinkedList2 extends LinkedList {}; - private static class TreeSet2 extends TreeSet {}; - private static class TreeMap2 extends TreeMap {}; + private static class LinkedList2 extends LinkedList {} + private static class TreeSet2 extends TreeSet {} + private static class TreeMap2 extends TreeMap {} } diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/LinkedList/ComodifiedRemove.java --- a/jdk/test/java/util/LinkedList/ComodifiedRemove.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/LinkedList/ComodifiedRemove.java Wed Jul 05 20:54:48 2017 +0200 @@ -41,10 +41,10 @@ list.add(o1); ListIterator e = list.listIterator(); e.next(); - Object o2 = new Integer (2); + Object o2 = new Integer(2); list.add(o2); - try{ + try { e.remove(); } catch (ConcurrentModificationException cme) { return; diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/List/ListDefaults.java --- a/jdk/test/java/util/List/ListDefaults.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/List/ListDefaults.java Wed Jul 05 20:54:48 2017 +0200 @@ -381,7 +381,7 @@ minBitCount = bitCount; } - // Resuse the supplier to store AtomicInteger instead of Integer + // Reuse the supplier to store AtomicInteger instead of Integer // Hence the use of raw type and cast List incomparablesData = new ArrayList<>(); for (int i = 0; i < test.expected.size(); i++) { diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/Map/Defaults.java --- a/jdk/test/java/util/Map/Defaults.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/Map/Defaults.java Wed Jul 05 20:54:48 2017 +0200 @@ -464,7 +464,7 @@ @Test(dataProvider = "MergeCases") private void testMerge(String description, Map map, Merging.Value oldValue, Merging.Value newValue, Merging.Merger merger, Merging.Value put, Merging.Value result) { // add and check initial conditions. - switch(oldValue) { + switch (oldValue) { case ABSENT : map.remove(EXTRA_KEY); assertFalse(map.containsKey(EXTRA_KEY), "key not absent"); @@ -490,7 +490,7 @@ // check result - switch(result) { + switch (result) { case NULL : assertNull(returned, "wrong value"); break; @@ -505,7 +505,7 @@ } // check map - switch(put) { + switch (put) { case ABSENT : assertFalse(map.containsKey(EXTRA_KEY), "key not absent"); break; @@ -610,7 +610,6 @@ return all; } - private static Collection makeRWMapsNoNulls() { Collection all = new ArrayList<>(); @@ -656,8 +655,8 @@ return all; } + /** - * * @param nullKeys include null keys * @param nullValues include null values * @return @@ -674,7 +673,6 @@ } /** - * * @param nulls include null values * @return */ @@ -702,7 +700,6 @@ } /** - * * @param nulls include nulls * @return */ @@ -712,8 +709,7 @@ }); } - /** - * + /** * @param supplier a supplier of mutable map instances. * * @param nullKeys include null keys @@ -774,15 +770,15 @@ static Collection makeMergeTestCases() { Collection cases = new ArrayList<>(); - for( Object[] mapParams : makeAllRWMaps() ) { + for (Object[] mapParams : makeAllRWMaps() ) { cases.add(new Object[] { mapParams[0], mapParams[1], Merging.Value.ABSENT, Merging.Value.NEWVALUE, Merging.Merger.UNUSED, Merging.Value.NEWVALUE, Merging.Value.NEWVALUE }); } - for( Object[] mapParams : makeAllRWMaps() ) { + for (Object[] mapParams : makeAllRWMaps() ) { cases.add(new Object[] { mapParams[0], mapParams[1], Merging.Value.OLDVALUE, Merging.Value.NEWVALUE, Merging.Merger.NULL, Merging.Value.ABSENT, Merging.Value.NULL }); } - for( Object[] mapParams : makeAllRWMaps() ) { + for (Object[] mapParams : makeAllRWMaps() ) { cases.add(new Object[] { mapParams[0], mapParams[1], Merging.Value.OLDVALUE, Merging.Value.NEWVALUE, Merging.Merger.RESULT, Merging.Value.RESULT, Merging.Value.RESULT }); } @@ -813,7 +809,7 @@ } public static void assertThrows(Class throwable, String message, Thrower... throwers) { - for(Thrower thrower : throwers) { + for (Thrower thrower : throwers) { assertThrows(thrower, throwable, message); } } @@ -834,7 +830,7 @@ * @param Type of keys * @param Type of values */ - public static class ExtendsAbstractMap, K, V> extends AbstractMap { + public static class ExtendsAbstractMap, K, V> extends AbstractMap { protected final M map; @@ -842,8 +838,8 @@ protected ExtendsAbstractMap(M map) { this.map = map; } - public Set> entrySet() { - return new AbstractSet>() { + public Set> entrySet() { + return new AbstractSet>() { public int size() { return map.size(); } @@ -876,7 +872,7 @@ * @param Type of keys * @param Type of values */ - public static class ImplementsConcurrentMap extends ExtendsAbstractMap, K, V> implements ConcurrentMap { + public static class ImplementsConcurrentMap extends ExtendsAbstractMap, K, V> implements ConcurrentMap { public ImplementsConcurrentMap() { super(new ConcurrentHashMap()); } // ConcurrentMap reabstracts these methods diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/Map/Get.java --- a/jdk/test/java/util/Map/Get.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/Map/Get.java Wed Jul 05 20:54:48 2017 +0200 @@ -119,7 +119,7 @@ static void check(boolean cond) { if (cond) pass(); else fail(); } static void check(String desc, boolean cond) { if (cond) pass(); else fail(desc); } static void equal(Object x, Object y) { - if(Objects.equals(x,y)) pass(); else fail(x + " not equal to " + y); + if (Objects.equals(x,y)) pass(); else fail(x + " not equal to " + y); } public static void main(String[] args) throws Throwable { diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/NavigableMap/LockStep.java --- a/jdk/test/java/util/NavigableMap/LockStep.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/NavigableMap/LockStep.java Wed Jul 05 20:54:48 2017 +0200 @@ -228,8 +228,8 @@ if (maybe(4) && s instanceof Serializable) { try { equal2(s, serialClone(s)); - } catch(RuntimeException uhoh) { - if(!(uhoh.getCause() instanceof NotSerializableException)) { + } catch (RuntimeException uhoh) { + if (!(uhoh.getCause() instanceof NotSerializableException)) { throw uhoh; } } diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/Spliterator/SpliteratorCharacteristics.java --- a/jdk/test/java/util/Spliterator/SpliteratorCharacteristics.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/Spliterator/SpliteratorCharacteristics.java Wed Jul 05 20:54:48 2017 +0200 @@ -126,19 +126,19 @@ } { - Spliterator s = Spliterators.spliterator(l.iterator( ), 1, 0); + Spliterator s = Spliterators.spliterator(l.iterator(), 1, 0); assertCharacteristics(s, Spliterator.SIZED | Spliterator.SUBSIZED); assertHasNotCharacteristics(s, Spliterator.CONCURRENT); } { - Spliterator s = Spliterators.spliterator(l.iterator( ), 1, Spliterator.CONCURRENT); + Spliterator s = Spliterators.spliterator(l.iterator(), 1, Spliterator.CONCURRENT); assertHasNotCharacteristics(s, Spliterator.SIZED | Spliterator.SUBSIZED); assertCharacteristics(s, Spliterator.CONCURRENT); } { - Spliterator s = Spliterators.spliteratorUnknownSize(l.iterator( ), 0); + Spliterator s = Spliterators.spliteratorUnknownSize(l.iterator(), 0); assertHasNotCharacteristics(s, Spliterator.SIZED | Spliterator.SUBSIZED); } diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/Spliterator/SpliteratorLateBindingFailFastTest.java --- a/jdk/test/java/util/Spliterator/SpliteratorLateBindingFailFastTest.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/Spliterator/SpliteratorLateBindingFailFastTest.java Wed Jul 05 20:54:48 2017 +0200 @@ -214,7 +214,7 @@ db.addMap(LinkedHashMap::new); - // This fails when run through jrteg but passes when run though + // This fails when run through jtreg but passes when run through // ant // db.addMap(IdentityHashMap::new); diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/TimSort/Sorter.java --- a/jdk/test/java/util/TimSort/Sorter.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/TimSort/Sorter.java Wed Jul 05 20:54:48 2017 +0200 @@ -46,7 +46,7 @@ for (int i=0; i < 10000; i++) { for (Sorter s : values()) { - Integer[] test= gold.clone(); + Integer[] test = gold.clone(); s.sort(test); } } diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/TreeMap/ContainsValue.java --- a/jdk/test/java/util/TreeMap/ContainsValue.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/TreeMap/ContainsValue.java Wed Jul 05 20:54:48 2017 +0200 @@ -30,7 +30,7 @@ import java.util.*; public class ContainsValue { - public static void main (String[] args) { + public static void main(String[] args) { Map map = new TreeMap(); if (map.containsValue ("gemutlichkeit")) diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/TreeMap/HeadTailTypeError.java --- a/jdk/test/java/util/TreeMap/HeadTailTypeError.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/TreeMap/HeadTailTypeError.java Wed Jul 05 20:54:48 2017 +0200 @@ -32,71 +32,70 @@ public class HeadTailTypeError { public static void main(String argv[]) throws Exception { - try{ + try { SortedMap m = new TreeMap(); m.headMap(new Object()); throw new Exception("headMap, natural ordering"); } catch (ClassCastException e) { } - try{ + try { SortedMap m = new TreeMap(); m.tailMap(new Object()); throw new Exception("tailMap, natural ordering"); } catch (ClassCastException e) { } - - try{ + try { SortedMap m = new TreeMap(String.CASE_INSENSITIVE_ORDER); m.headMap(new Integer(0)); throw new Exception("headMap, explicit comparator"); } catch (ClassCastException e) { } - try{ + try { SortedMap m = new TreeMap(String.CASE_INSENSITIVE_ORDER); m.tailMap(new Integer(0)); throw new Exception("tailMap, explicit comparator"); } catch (ClassCastException e) { } - try{ + try { SortedSet m = new TreeSet(); m.headSet(new Object()); throw new Exception("headSet, natural ordering"); } catch (ClassCastException e) { } - try{ + try { SortedSet m = new TreeSet(); m.tailSet(new Object()); throw new Exception("tailSet, natural ordering"); } catch (ClassCastException e) { } - try{ + try { SortedSet m = new TreeSet(String.CASE_INSENSITIVE_ORDER); m.headSet(new Integer(0)); throw new Exception("headSet, explicit comparator"); } catch (ClassCastException e) { } - try{ + try { SortedSet m = new TreeSet(String.CASE_INSENSITIVE_ORDER); m.tailSet(new Integer(0)); throw new Exception("tailSet, explicit comparator"); } catch (ClassCastException e) { } - try{ + try { SortedMap m = new TreeMap(); m.headMap(null); throw new Exception("(null endpoint)headMap, natural ordering"); } catch (NullPointerException e) { } - try{ + try { SortedMap m = new TreeMap(); m.tailMap(null); throw new Exception("(null endpoint)tailMap, natural ordering"); @@ -104,42 +103,42 @@ } - try{ + try { SortedMap m = new TreeMap(String.CASE_INSENSITIVE_ORDER); m.headMap(null); throw new Exception("(null endpoint)headMap, explicit comparator"); } catch (NullPointerException e) { } - try{ + try { SortedMap m = new TreeMap(String.CASE_INSENSITIVE_ORDER); m.tailMap(null); throw new Exception("(null endpoint)tailMap, explicit comparator"); } catch (NullPointerException e) { } - try{ + try { SortedSet m = new TreeSet(); m.headSet(null); throw new Exception("(null endpoint)headSet, natural ordering"); } catch (NullPointerException e) { } - try{ + try { SortedSet m = new TreeSet(); m.tailSet(null); throw new Exception("(null endpoint)tailSet, natural ordering"); } catch (NullPointerException e) { } - try{ + try { SortedSet m = new TreeSet(String.CASE_INSENSITIVE_ORDER); m.headSet(null); throw new Exception("(null endpoint)headSet, explicit comparator"); } catch (NullPointerException e) { } - try{ + try { SortedSet m = new TreeSet(String.CASE_INSENSITIVE_ORDER); m.tailSet(null); throw new Exception("(null endpoint)tailSet, explicit comparator"); diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/TreeMap/SubMap.java --- a/jdk/test/java/util/TreeMap/SubMap.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/TreeMap/SubMap.java Wed Jul 05 20:54:48 2017 +0200 @@ -40,7 +40,7 @@ boolean exc = false; try { m2.firstKey(); - } catch(NoSuchElementException e) { + } catch (NoSuchElementException e) { exc = true; } if (!exc) @@ -49,7 +49,7 @@ exc = false; try { m2.lastKey(); - } catch(NoSuchElementException e) { + } catch (NoSuchElementException e) { exc = true; } if (!exc) @@ -70,7 +70,7 @@ exc = false; try { s2.first(); - } catch(NoSuchElementException e) { + } catch (NoSuchElementException e) { exc = true; } if (!exc) @@ -79,7 +79,7 @@ exc = false; try { s2.last(); - } catch(NoSuchElementException e) { + } catch (NoSuchElementException e) { exc = true; } if (!exc) diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/Vector/ComodifiedRemoveAllElements.java --- a/jdk/test/java/util/Vector/ComodifiedRemoveAllElements.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/Vector/ComodifiedRemoveAllElements.java Wed Jul 05 20:54:48 2017 +0200 @@ -25,7 +25,7 @@ * @test * @bug 4298133 * @summary Due to a bug in Vector's removeAllElements(), - * the modification counter would not get incremented. + * the modification counter would not get incremented. * @author Konstantin Kladko */ @@ -37,7 +37,7 @@ v.addElement(null); Iterator it = v.iterator(); v.removeAllElements(); - try{ + try { it.next(); } catch (ConcurrentModificationException cme) { return; diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/Vector/IllegalConstructorArgs.java --- a/jdk/test/java/util/Vector/IllegalConstructorArgs.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/Vector/IllegalConstructorArgs.java Wed Jul 05 20:54:48 2017 +0200 @@ -39,7 +39,7 @@ public static void main(String argv[]) { int testSucceeded=0; - try{ + try { // this should generate an IllegalArgumentException Vector bad1 = new Vector(-100, 10); } @@ -50,8 +50,8 @@ testSucceeded =0; } - if(testSucceeded == 0) - throw new RuntimeException("Wrong exception thrown."); + if (testSucceeded == 0) + throw new RuntimeException("Wrong exception thrown."); } diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/Vector/LastIndexOf.java --- a/jdk/test/java/util/Vector/LastIndexOf.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/Vector/LastIndexOf.java Wed Jul 05 20:54:48 2017 +0200 @@ -34,7 +34,7 @@ public static void main(String argv[]) throws Exception { Vector v = new Vector(10); - try{ + try { int i = v.lastIndexOf(null, 5); throw new Exception("lastIndexOf(5/10) " + i); } catch (IndexOutOfBoundsException e) { diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/Vector/SyncLastIndexOf.java --- a/jdk/test/java/util/Vector/SyncLastIndexOf.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/Vector/SyncLastIndexOf.java Wed Jul 05 20:54:48 2017 +0200 @@ -36,7 +36,7 @@ static class RemovingThread extends Thread { public void run() { - synchronized(v) { + synchronized (v) { try { sleep(200); } catch (InterruptedException e) { diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/WeakHashMap/GCDuringIteration.java --- a/jdk/test/java/util/WeakHashMap/GCDuringIteration.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/WeakHashMap/GCDuringIteration.java Wed Jul 05 20:54:48 2017 +0200 @@ -24,7 +24,6 @@ /* * @test * @bug 6499848 - * @ignore until 6842353 is resolved * @summary Check that iterators work properly in the presence of * concurrent finalization and removal of elements. * @key randomness @@ -116,7 +115,7 @@ it.next(); // protects first entry System.out.println(map.values()); foos[first] = null; - tryWaitForFinalizersToRun() + tryWaitForFinalizersToRun(); equal(map.size(), first+1); System.out.println(map.values()); checkIterator(it, first-1); diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/concurrent/ArrayBlockingQueue/IteratorConsistency.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/util/concurrent/ArrayBlockingQueue/IteratorConsistency.java Wed Jul 05 20:54:48 2017 +0200 @@ -0,0 +1,755 @@ +/* + * 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Martin Buchholz with assistance from members of JCP + * JSR-166 Expert Group and released to the public domain, as + * explained at http://creativecommons.org/publicdomain/zero/1.0/ + */ + +import java.lang.ref.WeakReference; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.ArrayDeque; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Queue; +import java.util.Random; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.CountDownLatch; + +/* + * @test + * @bug 7014263 + * @summary White box testing of ArrayBlockingQueue iterators. + */ + +/** + * Highly coupled to the implementation of ArrayBlockingQueue. + * Uses reflection to inspect queue and iterator state. + */ +@SuppressWarnings({"unchecked", "rawtypes"}) +public class IteratorConsistency { + final Random rnd = new Random(); + final int CAPACITY = 20; + Field itrsField; + Field itemsField; + Field takeIndexField; + Field headField; + Field nextField; + Field prevTakeIndexField; + + void test(String[] args) throws Throwable { + itrsField = ArrayBlockingQueue.class.getDeclaredField("itrs"); + itemsField = ArrayBlockingQueue.class.getDeclaredField("items"); + takeIndexField = ArrayBlockingQueue.class.getDeclaredField("takeIndex"); + headField = Class.forName("java.util.concurrent.ArrayBlockingQueue$Itrs").getDeclaredField("head"); + nextField = Class.forName("java.util.concurrent.ArrayBlockingQueue$Itrs$Node").getDeclaredField("next"); + prevTakeIndexField = Class.forName("java.util.concurrent.ArrayBlockingQueue$Itr").getDeclaredField("prevTakeIndex"); + itrsField.setAccessible(true); + itemsField.setAccessible(true); + takeIndexField.setAccessible(true); + headField.setAccessible(true); + nextField.setAccessible(true); + prevTakeIndexField.setAccessible(true); + test(CAPACITY, true); + test(CAPACITY, false); + } + + Object itrs(ArrayBlockingQueue q) { + try { + return itrsField.get(q); + } catch (Throwable t) { throw new Error(); } + } + + int takeIndex(ArrayBlockingQueue q) { + try { + return takeIndexField.getInt(q); + } catch (Throwable t) { throw new Error(); } + } + + List trackedIterators(Object itrs) { + try { + List its = new ArrayList(); + if (itrs != null) + for (Object p = headField.get(itrs); p != null; p = nextField.get(p)) + its.add(((WeakReference)(p)).get()); + Collections.reverse(its); + return its; + } catch (Throwable t) { throw new Error(); } + } + + List trackedIterators(ArrayBlockingQueue q) { + return trackedIterators(itrs(q)); + } + + List attachedIterators(Object itrs) { + try { + List its = new ArrayList(); + if (itrs != null) + for (Object p = headField.get(itrs); p != null; p = nextField.get(p)) { + Iterator it = ((WeakReference)(p)).get(); + if (it != null && !isDetached(it)) + its.add(it); + } + Collections.reverse(its); + return its; + } catch (Throwable t) { unexpected(t); return null; } + } + + List attachedIterators(ArrayBlockingQueue q) { + return attachedIterators(itrs(q)); + } + + Object[] internalArray(ArrayBlockingQueue q) { + try { + return (Object[]) itemsField.get(q); + } catch (Throwable t) { throw new Error(t); } + } + + void printInternalArray(ArrayBlockingQueue q) { + System.err.println(Arrays.toString(internalArray(q))); + } + + void checkExhausted(Iterator it) { + if (rnd.nextBoolean()) { + check(!it.hasNext()); + check(isDetached(it)); + } + if (rnd.nextBoolean()) + try { it.next(); fail("should throw"); } + catch (NoSuchElementException success) {} + } + + boolean isDetached(Iterator it) { + try { + return prevTakeIndexField.getInt(it) < 0; + } catch (IllegalAccessException t) { unexpected(t); return false; } + } + + void checkDetached(Iterator it) { + check(isDetached(it)); + } + + void removeUsingIterator(ArrayBlockingQueue q, Object element) { + Iterator it = q.iterator(); + while (it.hasNext()) { + Object x = it.next(); + if (element.equals(x)) + it.remove(); + checkRemoveThrowsISE(it); + } + } + + void checkRemoveThrowsISE(Iterator it) { + if (rnd.nextBoolean()) + return; + try { it.remove(); fail("should throw"); } + catch (IllegalStateException success) {} + } + + void checkRemoveHasNoEffect(Iterator it, Collection c) { + if (rnd.nextBoolean()) + return; + int size = c.size(); + it.remove(); // no effect + equal(c.size(), size); + checkRemoveThrowsISE(it); + } + + void checkIterationSanity(Queue q) { + if (rnd.nextBoolean()) + return; + int size = q.size(); + Object[] a = q.toArray(); + Object[] b = new Object[size+2]; + Arrays.fill(b, Boolean.TRUE); + Object[] c = q.toArray(b); + equal(a.length, size); + check(b == c); + check(b[size] == null); + check(b[size+1] == Boolean.TRUE); + equal(q.toString(), Arrays.toString(a)); + Integer[] xx = null, yy = null; + if (size > 0) { + xx = new Integer[size - 1]; + Arrays.fill(xx, 42); + yy = ((Queue)q).toArray(xx); + for (Integer zz : xx) + equal(42, zz); + } + Iterator it = q.iterator(); + for (int i = 0; i < size; i++) { + check(it.hasNext()); + Object x = it.next(); + check(x == a[i]); + check(x == b[i]); + if (xx != null) check(x == yy[i]); + } + check(!it.hasNext()); + } + + private static void waitForFinalizersToRun() { + for (int i = 0; i < 2; i++) + tryWaitForFinalizersToRun(); + } + + private static void tryWaitForFinalizersToRun() { + System.gc(); + final CountDownLatch fin = new CountDownLatch(1); + new Object() { protected void finalize() { fin.countDown(); }}; + System.gc(); + try { fin.await(); } + catch (InterruptedException ie) { throw new Error(ie); } + } + + void test(int capacity, boolean fair) { + //---------------------------------------------------------------- + // q.clear will clear out itrs. + //---------------------------------------------------------------- + try { + ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair); + List its = new ArrayList(); + for (int i = 0; i < capacity; i++) + check(q.add(i)); + check(itrs(q) == null); + for (int i = 0; i < capacity; i++) { + its.add(q.iterator()); + equal(trackedIterators(q), its); + q.poll(); + q.add(capacity+i); + } + q.clear(); + check(itrs(q) == null); + int j = 0; + for (Iterator it : its) { + if (rnd.nextBoolean()) + check(it.hasNext()); + equal(it.next(), j++); + checkExhausted(it); + } + } catch (Throwable t) { unexpected(t); } + + //---------------------------------------------------------------- + // q emptying will clear out itrs. + //---------------------------------------------------------------- + try { + ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair); + List its = new ArrayList(); + for (int i = 0; i < capacity; i++) + q.add(i); + check(itrs(q) == null); + for (int i = 0; i < capacity; i++) { + its.add(q.iterator()); + equal(trackedIterators(q), its); + q.poll(); + q.add(capacity+i); + } + for (int i = 0; i < capacity; i++) + q.poll(); + check(itrs(q) == null); + int j = 0; + for (Iterator it : its) { + if (rnd.nextBoolean()) + check(it.hasNext()); + equal(it.next(), j++); + checkExhausted(it); + } + } catch (Throwable t) { unexpected(t); } + + //---------------------------------------------------------------- + // Advancing 2 cycles will remove iterators. + //---------------------------------------------------------------- + try { + ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair); + List its = new ArrayList(); + for (int i = 0; i < capacity; i++) + q.add(i); + check(itrs(q) == null); + for (int i = capacity; i < 3 * capacity; i++) { + its.add(q.iterator()); + equal(trackedIterators(q), its); + q.poll(); + q.add(i); + } + for (int i = 3 * capacity; i < 4 * capacity; i++) { + equal(trackedIterators(q), its.subList(capacity,2*capacity)); + q.poll(); + q.add(i); + } + check(itrs(q) == null); + int j = 0; + for (Iterator it : its) { + if (rnd.nextBoolean()) + check(it.hasNext()); + equal(it.next(), j++); + checkExhausted(it); + } + } catch (Throwable t) { unexpected(t); } + + //---------------------------------------------------------------- + // Interior removal of elements used by an iterator will cause + // it to be untracked. + //---------------------------------------------------------------- + try { + ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair); + q.add(0); + for (int i = 1; i < 2 * capacity; i++) { + q.add(i); + Integer[] elts = { -1, -2, -3 }; + for (Integer elt : elts) q.add(elt); + equal(q.remove(), i - 1); + Iterator it = q.iterator(); + equal(it.next(), i); + equal(it.next(), elts[0]); + Collections.shuffle(Arrays.asList(elts)); + check(q.remove(elts[0])); + check(q.remove(elts[1])); + equal(trackedIterators(q), Collections.singletonList(it)); + check(q.remove(elts[2])); + check(itrs(q) == null); + equal(it.next(), -2); + if (rnd.nextBoolean()) checkExhausted(it); + if (rnd.nextBoolean()) checkDetached(it); + } + } catch (Throwable t) { unexpected(t); } + + //---------------------------------------------------------------- + // Check iterators on an empty q + //---------------------------------------------------------------- + try { + ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair); + for (int i = 0; i < 4; i++) { + Iterator it = q.iterator(); + check(itrs(q) == null); + if (rnd.nextBoolean()) checkExhausted(it); + if (rnd.nextBoolean()) checkDetached(it); + checkRemoveThrowsISE(it); + } + } catch (Throwable t) { unexpected(t); } + + //---------------------------------------------------------------- + // Check "interior" removal of iterator's last element + //---------------------------------------------------------------- + try { + ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair); + List its = new ArrayList(); + for (int i = 0; i < capacity; i++) + q.add(i); + for (int i = 0; i < capacity; i++) { + Iterator it = q.iterator(); + its.add(it); + for (int j = 0; j < i; j++) + equal(j, it.next()); + equal(attachedIterators(q), its); + } + q.remove(capacity - 1); + equal(attachedIterators(q), its); + for (int i = 1; i < capacity - 1; i++) { + q.remove(capacity - i - 1); + Iterator it = its.get(capacity - i); + checkDetached(it); + equal(attachedIterators(q), its.subList(0, capacity - i)); + if (rnd.nextBoolean()) check(it.hasNext()); + equal(it.next(), capacity - i); + checkExhausted(it); + } + equal(attachedIterators(q), its.subList(0, 2)); + q.remove(0); + check(q.isEmpty()); + check(itrs(q) == null); + Iterator it = its.get(0); + equal(it.next(), 0); + checkRemoveHasNoEffect(it, q); + checkExhausted(it); + checkDetached(it); + checkRemoveHasNoEffect(its.get(1), q); + } catch (Throwable t) { unexpected(t); } + + //---------------------------------------------------------------- + // Check "interior" removal of alternating elements, straddling 2 cycles + //---------------------------------------------------------------- + try { + ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair); + List its = new ArrayList(); + // Move takeIndex to middle + for (int i = 0; i < capacity/2; i++) { + check(q.add(i)); + equal(q.poll(), i); + } + check(takeIndex(q) == capacity/2); + for (int i = 0; i < capacity; i++) + q.add(i); + for (int i = 0; i < capacity; i++) { + Iterator it = q.iterator(); + its.add(it); + for (int j = 0; j < i; j++) + equal(j, it.next()); + equal(attachedIterators(q), its); + } + // Remove all even elements, in either direction using + // q.remove(), or iterator.remove() + switch (rnd.nextInt(3)) { + case 0: + for (int i = 0; i < capacity; i+=2) { + check(q.remove(i)); + equal(attachedIterators(q), its); + } + break; + case 1: + for (int i = capacity - 2; i >= 0; i-=2) { + check(q.remove(i)); + equal(attachedIterators(q), its); + } + break; + case 2: + Iterator it = q.iterator(); + while (it.hasNext()) { + int i = (Integer) it.next(); + if ((i & 1) == 0) + it.remove(); + } + equal(attachedIterators(q), its); + break; + default: throw new Error(); + } + + for (int i = 0; i < capacity; i++) { + Iterator it = its.get(i); + boolean even = ((i & 1) == 0); + if (even) { + if (rnd.nextBoolean()) check(it.hasNext()); + equal(i, it.next()); + for (int j = i+1; j < capacity; j += 2) + equal(j, it.next()); + check(!isDetached(it)); + check(!it.hasNext()); + check(isDetached(it)); + } else { /* odd */ + if (rnd.nextBoolean()) check(it.hasNext()); + checkRemoveHasNoEffect(it, q); + equal(i, it.next()); + for (int j = i+2; j < capacity; j += 2) + equal(j, it.next()); + check(!isDetached(it)); + check(!it.hasNext()); + check(isDetached(it)); + } + } + equal(trackedIterators(q), Collections.emptyList()); + check(itrs(q) == null); + } catch (Throwable t) { unexpected(t); } + + //---------------------------------------------------------------- + // Check garbage collection of discarded iterators + //---------------------------------------------------------------- + try { + ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair); + List its = new ArrayList(); + for (int i = 0; i < capacity; i++) + q.add(i); + for (int i = 0; i < capacity; i++) { + its.add(q.iterator()); + equal(attachedIterators(q), its); + } + its = null; + waitForFinalizersToRun(); + List trackedIterators = trackedIterators(q); + equal(trackedIterators.size(), capacity); + for (Iterator x : trackedIterators) + check(x == null); + Iterator it = q.iterator(); + equal(trackedIterators(q), Collections.singletonList(it)); + } catch (Throwable t) { unexpected(t); } + + //---------------------------------------------------------------- + // Check garbage collection of discarded iterators, + // with a randomly retained subset. + //---------------------------------------------------------------- + try { + ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair); + List its = new ArrayList(); + List retained = new ArrayList(); + final int size = 1 + rnd.nextInt(capacity); + for (int i = 0; i < size; i++) + q.add(i); + for (int i = 0; i < size; i++) { + Iterator it = q.iterator(); + its.add(it); + equal(attachedIterators(q), its); + } + // Leave sufficient gaps in retained + for (int i = 0; i < size; i+= 2+rnd.nextInt(3)) + retained.add(its.get(i)); + its = null; + waitForFinalizersToRun(); + List trackedIterators = trackedIterators(q); + equal(trackedIterators.size(), size); + for (Iterator it : trackedIterators) + check((it == null) ^ retained.contains(it)); + Iterator it = q.iterator(); // trigger another sweep + retained.add(it); + equal(trackedIterators(q), retained); + } catch (Throwable t) { unexpected(t); } + + //---------------------------------------------------------------- + // Check incremental sweeping of discarded iterators. + // Excessively white box?! + //---------------------------------------------------------------- + try { + final int SHORT_SWEEP_PROBES = 4; + final int LONG_SWEEP_PROBES = 16; + final int PROBE_HOP = LONG_SWEEP_PROBES + 6 * SHORT_SWEEP_PROBES; + final int PROBE_HOP_COUNT = 10; + // Expect around 8 sweeps per PROBE_HOP + final int SWEEPS_PER_PROBE_HOP = 8; + ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair); + List its = new ArrayList(); + for (int i = 0; i < capacity; i++) + q.add(i); + for (int i = 0; i < PROBE_HOP_COUNT * PROBE_HOP; i++) { + its.add(q.iterator()); + equal(attachedIterators(q), its); + } + // make some garbage, separated by PROBE_HOP + for (int i = 0; i < its.size(); i += PROBE_HOP) + its.set(i, null); + waitForFinalizersToRun(); + int retries; + for (retries = 0; + trackedIterators(q).contains(null) && retries < 1000; + retries++) + // one round of sweeping + its.add(q.iterator()); + check(retries >= PROBE_HOP_COUNT * (SWEEPS_PER_PROBE_HOP - 2)); + check(retries <= PROBE_HOP_COUNT * (SWEEPS_PER_PROBE_HOP + 2)); + Iterator itsit = its.iterator(); + while (itsit.hasNext()) + if (itsit.next() == null) + itsit.remove(); + equal(trackedIterators(q), its); + } catch (Throwable t) { unexpected(t); } + + //---------------------------------------------------------------- + // Check safety of iterator.remove while in detached mode. + //---------------------------------------------------------------- + try { + ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair); + List its = new ArrayList(); + for (int i = 0; i < capacity/2; i++) { + q.add(i); + q.remove(); + } + check(takeIndex(q) == capacity/2); + for (int i = 0; i < capacity; i++) + q.add(i); + for (int i = 0; i < capacity; i++) { + Iterator it = q.iterator(); + its.add(it); + for (int j = 0; j < i; j++) + equal(j, it.next()); + equal(attachedIterators(q), its); + } + for (int i = capacity - 1; i >= 0; i--) { + Iterator it = its.get(i); + equal(i, it.next()); // last element + check(!isDetached(it)); + check(!it.hasNext()); // first hasNext failure + check(isDetached(it)); + int size = q.size(); + check(q.contains(i)); + switch (rnd.nextInt(3)) { + case 0: + it.remove(); + check(!q.contains(i)); + equal(q.size(), size - 1); + break; + case 1: + // replace i with impostor + if (q.remainingCapacity() == 0) { + check(q.remove(i)); + check(q.add(-1)); + } else { + check(q.add(-1)); + check(q.remove(i)); + } + it.remove(); // should have no effect + equal(size, q.size()); + check(q.contains(-1)); + check(q.remove(-1)); + break; + case 2: + // replace i with true impostor + if (i != 0) { + check(q.remove(i)); + check(q.add(i)); + } + it.remove(); + check(!q.contains(i)); + equal(q.size(), size - 1); + break; + default: throw new Error(); + } + checkRemoveThrowsISE(it); + check(isDetached(it)); + check(!trackedIterators(q).contains(it)); + } + check(q.isEmpty()); + check(itrs(q) == null); + for (Iterator it : its) + checkExhausted(it); + } catch (Throwable t) { unexpected(t); } + + //---------------------------------------------------------------- + // Check dequeues bypassing iterators' current positions. + //---------------------------------------------------------------- + try { + ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair); + Queue its0 + = new ArrayDeque(); + Queue itsMid + = new ArrayDeque(); + List its = new ArrayList(); + for (int i = 0; i < capacity; i++) + q.add(i); + for (int i = 0; i < 2 * capacity + 1; i++) { + Iterator it = q.iterator(); + its.add(it); + its0.add(it); + } + for (int i = 0; i < 2 * capacity + 1; i++) { + Iterator it = q.iterator(); + for (int j = 0; j < capacity/2; j++) + equal(j, it.next()); + its.add(it); + itsMid.add(it); + } + for (int i = capacity; i < 3 * capacity; i++) { + Iterator it; + + it = its0.remove(); + checkRemoveThrowsISE(it); + if (rnd.nextBoolean()) check(it.hasNext()); + equal(0, it.next()); + int victim = i - capacity; + for (int j = victim + (victim == 0 ? 1 : 0); j < i; j++) { + if (rnd.nextBoolean()) check(it.hasNext()); + equal(j, it.next()); + } + checkExhausted(it); + + it = itsMid.remove(); + if (victim >= capacity/2) + checkRemoveHasNoEffect(it, q); + equal(capacity/2, it.next()); + if (victim > capacity/2) + checkRemoveHasNoEffect(it, q); + for (int j = Math.max(victim, capacity/2 + 1); j < i; j++) { + if (rnd.nextBoolean()) check(it.hasNext()); + equal(j, it.next()); + } + checkExhausted(it); + + if (rnd.nextBoolean()) { + equal(victim, q.remove()); + } else { + ArrayList list = new ArrayList(1); + q.drainTo(list, 1); + equal(list.size(), 1); + equal(victim, list.get(0)); + } + check(q.add(i)); + } + // takeIndex has wrapped twice. + Iterator it0 = its0.remove(); + Iterator itMid = itsMid.remove(); + check(isDetached(it0)); + check(isDetached(itMid)); + if (rnd.nextBoolean()) check(it0.hasNext()); + if (rnd.nextBoolean()) check(itMid.hasNext()); + checkRemoveThrowsISE(it0); + checkRemoveHasNoEffect(itMid, q); + if (rnd.nextBoolean()) equal(0, it0.next()); + if (rnd.nextBoolean()) equal(capacity/2, itMid.next()); + check(isDetached(it0)); + check(isDetached(itMid)); + equal(capacity, q.size()); + equal(0, q.remainingCapacity()); + } catch (Throwable t) { unexpected(t); } + + //---------------------------------------------------------------- + // Check collective sanity of iteration, toArray() and toString() + //---------------------------------------------------------------- + try { + ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair); + for (int i = 0; i < capacity; i++) { + checkIterationSanity(q); + equal(capacity, q.size() + q.remainingCapacity()); + q.add(i); + } + for (int i = 0; i < (capacity + (capacity >> 1)); i++) { + checkIterationSanity(q); + equal(capacity, q.size() + q.remainingCapacity()); + equal(i, q.peek()); + equal(i, q.poll()); + checkIterationSanity(q); + equal(capacity, q.size() + q.remainingCapacity()); + q.add(capacity + i); + } + for (int i = 0; i < capacity; i++) { + checkIterationSanity(q); + equal(capacity, q.size() + q.remainingCapacity()); + int expected = i + capacity + (capacity >> 1); + equal(expected, q.peek()); + equal(expected, q.poll()); + } + checkIterationSanity(q); + } catch (Throwable t) { unexpected(t); } + + } + + //--------------------- Infrastructure --------------------------- + volatile int passed = 0, failed = 0; + void pass() {passed++;} + void fail() {failed++; Thread.dumpStack();} + void fail(String msg) {System.err.println(msg); fail();} + void unexpected(Throwable t) {failed++; t.printStackTrace();} + void check(boolean cond) {if (cond) pass(); else fail();} + void equal(Object x, Object y) { + if (x == null ? y == null : x.equals(y)) pass(); + else fail(x + " not equal to " + y);} + public static void main(String[] args) throws Throwable { + new IteratorConsistency().instanceMain(args);} + public void instanceMain(String[] args) throws Throwable { + try {test(args);} catch (Throwable t) {unexpected(t);} + System.err.printf("%nPassed = %d, failed = %d%n%n", passed, failed); + if (failed > 0) throw new AssertionError("Some tests failed");} +} diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/concurrent/BlockingQueue/DrainToFails.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/util/concurrent/BlockingQueue/DrainToFails.java Wed Jul 05 20:54:48 2017 +0200 @@ -0,0 +1,207 @@ +/* + * 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea and Martin Buchholz with assistance from + * members of JCP JSR-166 Expert Group and released to the public + * domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +/* + * @test + * @summary Test drainTo failing due to c.add throwing + */ + +import java.util.*; +import java.util.concurrent.*; + +@SuppressWarnings({"unchecked", "rawtypes"}) +public class DrainToFails { + final int CAPACITY = 10; + final int SMALL = 2; + + void test(String[] args) throws Throwable { + testDelayQueue(new DelayQueue()); + testDelayQueue(new ScheduledThreadPoolExecutor(1).getQueue()); + + testUnbounded(new LinkedBlockingQueue()); + testUnbounded(new LinkedBlockingDeque()); + testUnbounded(new PriorityBlockingQueue()); + + testBounded(new LinkedBlockingQueue(CAPACITY)); + testBounded(new LinkedBlockingDeque(CAPACITY)); + testBounded(new ArrayBlockingQueue(CAPACITY)); + } + + static class PDelay + extends FutureTask + implements Delayed, RunnableScheduledFuture { + int pseudodelay; + PDelay(int i) { + super(new Runnable() { public void run() {}}, null); + pseudodelay = i; + } + public int compareTo(PDelay other) { + int a = this.pseudodelay; + int b = other.pseudodelay; + return (a < b) ? -1 : (a > b) ? 1 : 0; + } + public int compareTo(Delayed y) { + return compareTo((PDelay)y); + } + public boolean equals(Object other) { + return (other instanceof PDelay) && + this.pseudodelay == ((PDelay)other).pseudodelay; + } + public long getDelay(TimeUnit ignore) { + return Integer.MIN_VALUE + pseudodelay; + } + public String toString() { + return String.valueOf(pseudodelay); + } + public boolean isPeriodic() { return false; } + } + + void testDelayQueue(final BlockingQueue q) throws Throwable { + System.err.println(q.getClass().getSimpleName()); + for (int i = 0; i < CAPACITY; i++) + q.add(new PDelay(i)); + ArrayBlockingQueue q2 = new ArrayBlockingQueue(SMALL); + try { + q.drainTo(q2, SMALL + 3); + fail("should throw"); + } catch (IllegalStateException success) { + equal(SMALL, q2.size()); + equal(new PDelay(0), q2.poll()); + equal(new PDelay(1), q2.poll()); + check(q2.isEmpty()); + for (int i = SMALL; i < CAPACITY; i++) + equal(new PDelay(i), q.poll()); + equal(0, q.size()); + } + } + + void testUnbounded(final BlockingQueue q) throws Throwable { + System.err.println(q.getClass().getSimpleName()); + for (int i = 0; i < CAPACITY; i++) + q.add(i); + ArrayBlockingQueue q2 = new ArrayBlockingQueue(SMALL); + try { + q.drainTo(q2, 7); + fail("should throw"); + } catch (IllegalStateException success) { + assertContentsInOrder(q2, 0, 1); + q2.clear(); + equal(q.size(), CAPACITY - SMALL); + equal(SMALL, q.peek()); + } + + try { + q.drainTo(q2); + fail("should throw"); + } catch (IllegalStateException success) { + assertContentsInOrder(q2, 2, 3); + equal(q.size(), CAPACITY - 2 * SMALL); + for (int i = 2 * SMALL; i < CAPACITY; i++) + equal(i, q.poll()); + equal(0, q.size()); + } + } + + void testBounded(final BlockingQueue q) throws Throwable { + System.err.println(q.getClass().getSimpleName()); + for (int i = 0; i < CAPACITY; i++) + q.add(i); + List putters = new ArrayList(); + for (int i = 0; i < 4; i++) { + Thread putter = new Thread(putter(q, 42 + i)); + putters.add(putter); + putter.setDaemon(true); + putter.start(); + } + ArrayBlockingQueue q2 = new ArrayBlockingQueue(SMALL); + try { + q.drainTo(q2, 7); + fail("should throw"); + } catch (IllegalStateException success) { + while (q.size() < CAPACITY) + Thread.yield(); + assertContentsInOrder(q2, 0, 1); + q2.clear(); + } + + try { + q.drainTo(q2); + fail("should throw"); + } catch (IllegalStateException success) { + for (Thread putter : putters) { + putter.join(2000L); + check(! putter.isAlive()); + } + assertContentsInOrder(q2, 2, 3); + for (int i = 2 * SMALL; i < CAPACITY; i++) + equal(i, q.poll()); + equal(4, q.size()); + check(q.contains(42)); + check(q.contains(43)); + check(q.contains(44)); + check(q.contains(45)); + } + } + + Runnable putter(final BlockingQueue q, final int elt) { + return new Runnable() { + public void run() { + try { q.put(elt); } + catch (Throwable t) { unexpected(t); }}}; + } + + void assertContentsInOrder(Iterable it, Object... contents) { + int i = 0; + for (Object e : it) + equal(contents[i++], e); + equal(contents.length, i); + } + + //--------------------- Infrastructure --------------------------- + volatile int passed = 0, failed = 0; + void pass() {passed++;} + void fail() {failed++; Thread.dumpStack();} + void fail(String msg) {System.err.println(msg); fail();} + void unexpected(Throwable t) {failed++; t.printStackTrace();} + void check(boolean cond) {if (cond) pass(); else fail();} + void equal(Object x, Object y) { + if (x == null ? y == null : x.equals(y)) pass(); + else fail(x + " not equal to " + y);} + public static void main(String[] args) throws Throwable { + new DrainToFails().instanceMain(args);} + public void instanceMain(String[] args) throws Throwable { + try {test(args);} catch (Throwable t) {unexpected(t);} + System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed); + if (failed > 0) throw new AssertionError("Some tests failed");} +} diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/concurrent/BlockingQueue/Interrupt.java --- a/jdk/test/java/util/concurrent/BlockingQueue/Interrupt.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/concurrent/BlockingQueue/Interrupt.java Wed Jul 05 20:54:48 2017 +0200 @@ -48,7 +48,8 @@ } } - static void checkInterrupted(Iterable fs) { + static void checkInterrupted(Iterable fs) + throws InterruptedException { final Executor immediateExecutor = new Executor() { public void execute(Runnable r) { r.run(); }}; @@ -60,6 +61,7 @@ checkInterrupted0(fs, immediateExecutor); checkInterrupted0(fs, delayedExecutor); stpe.shutdown(); + check(stpe.awaitTermination(10, SECONDS)); } static void testQueue(final BlockingQueue q) { @@ -96,8 +98,10 @@ } checkInterrupted(fs); } catch (Throwable t) { - System.out.printf("Failed: %s%n", q.getClass().getSimpleName()); - unexpected(t); + System.out.printf("Failed: %s%n", q.getClass().getSimpleName()); + unexpected(t); + } finally { + Thread.interrupted(); // clear interrupts, just in case } } diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/concurrent/CompletableFuture/Basic.java --- a/jdk/test/java/util/concurrent/CompletableFuture/Basic.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/concurrent/CompletableFuture/Basic.java Wed Jul 05 20:54:48 2017 +0200 @@ -53,7 +53,6 @@ import static java.util.concurrent.ForkJoinPool.*; import java.util.concurrent.atomic.AtomicInteger; - public class Basic { static void checkCompletedNormally(CompletableFuture cf, Object value) { @@ -66,6 +65,7 @@ try { equalAnyOf(cf.get(), values); } catch (Throwable x) { unexpected(x); } try { equalAnyOf(cf.get(0L, SECONDS), values); } catch (Throwable x) { unexpected(x); } check(cf.isDone(), "Expected isDone to be true, got:" + cf); + check(!cf.isCompletedExceptionally(), "Expected isCompletedExceptionally to return false"); check(!cf.isCancelled(), "Expected isCancelled to be false"); check(!cf.cancel(true), "Expected cancel to return false"); check(cf.toString().contains("[Completed normally]")); @@ -97,6 +97,7 @@ catch (CancellationException x) { if (cancelled) pass(); else fail(); } catch (ExecutionException x) { if (cancelled) check(x.getCause() instanceof CancellationException); else pass(); } check(cf.isDone(), "Expected isDone to be true, got:" + cf); + check(cf.isCompletedExceptionally(), "Expected isCompletedExceptionally"); check(cf.isCancelled() == cancelled, "Expected isCancelled: " + cancelled + ", got:" + cf.isCancelled()); check(cf.cancel(true) == cancelled, "Expected cancel: " + cancelled + ", got:" + cf.cancel(true)); check(cf.toString().contains("[Completed exceptionally]")); // ## TODO: 'E'xceptionally @@ -805,6 +806,49 @@ cf2 = cf1.handle((x,t) -> { check(t.getCause() == ex); return 2;}); checkCompletedExceptionally(cf1); checkCompletedNormally(cf2, 2); + + cf1 = supplyAsync(() -> 1); + cf2 = cf1.handleAsync((x,t) -> x+1); + checkCompletedNormally(cf1, 1); + checkCompletedNormally(cf2, 2); + + cf1 = supplyAsync(() -> { throw ex; }); + cf2 = cf1.handleAsync((x,t) -> { check(t.getCause() == ex); return 2;}); + checkCompletedExceptionally(cf1); + checkCompletedNormally(cf2, 2); + } catch (Throwable t) { unexpected(t); } + + //---------------------------------------------------------------- + // whenComplete tests + //---------------------------------------------------------------- + try { + AtomicInteger count = new AtomicInteger(); + CompletableFuture cf2; + CompletableFuture cf1 = supplyAsync(() -> 1); + cf2 = cf1.whenComplete((x,t) -> count.getAndIncrement()); + checkCompletedNormally(cf1, 1); + checkCompletedNormally(cf2, 1); + check(count.get() == 1, "action count should be incremented"); + + final RuntimeException ex = new RuntimeException(); + cf1 = supplyAsync(() -> { throw ex; }); + cf2 = cf1.whenComplete((x,t) -> count.getAndIncrement()); + checkCompletedExceptionally(cf1); + checkCompletedExceptionally(cf2); + check(count.get() == 2, "action count should be incremented"); + + cf1 = supplyAsync(() -> 1); + cf2 = cf1.whenCompleteAsync((x,t) -> count.getAndIncrement()); + checkCompletedNormally(cf1, 1); + checkCompletedNormally(cf2, 1); + check(count.get() == 3, "action count should be incremented"); + + cf1 = supplyAsync(() -> { throw ex; }); + cf2 = cf1.whenCompleteAsync((x,t) -> count.getAndIncrement()); + checkCompletedExceptionally(cf1); + checkCompletedExceptionally(cf2); + check(count.get() == 4, "action count should be incremented"); + } catch (Throwable t) { unexpected(t); } } diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/concurrent/CompletableFuture/ThenComposeAsyncTest.java --- a/jdk/test/java/util/concurrent/CompletableFuture/ThenComposeAsyncTest.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/concurrent/CompletableFuture/ThenComposeAsyncTest.java Wed Jul 05 20:54:48 2017 +0200 @@ -27,7 +27,6 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; - /** * @test * @bug 8029164 diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/concurrent/CompletableFuture/ThenComposeExceptionTest.java --- a/jdk/test/java/util/concurrent/CompletableFuture/ThenComposeExceptionTest.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/concurrent/CompletableFuture/ThenComposeExceptionTest.java Wed Jul 05 20:54:48 2017 +0200 @@ -33,7 +33,6 @@ import java.util.function.BiFunction; import java.util.function.Consumer; - /** * @test * @bug 8068432 8072030 diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/concurrent/ConcurrentHashMap/ConcurrentAssociateTest.java --- a/jdk/test/java/util/concurrent/ConcurrentHashMap/ConcurrentAssociateTest.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/concurrent/ConcurrentHashMap/ConcurrentAssociateTest.java Wed Jul 05 20:54:48 2017 +0200 @@ -23,14 +23,18 @@ import org.testng.annotations.Test; +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.function.BiConsumer; import java.util.function.Supplier; import java.util.stream.IntStream; @@ -39,48 +43,52 @@ /** * @test * @bug 8028564 - * @run testng ConcurrentAssociateTest + * @run testng/timeout=1200 ConcurrentAssociateTest * @summary Test that association operations, such as put and compute, * place entries in the map */ @Test public class ConcurrentAssociateTest { - // The number of entries for each thread to place in a map + /** Maximum time (in seconds) to wait for a test method to complete. */ + private static final int TIMEOUT = Integer.getInteger("timeout", 200); + + /** The number of entries for each thread to place in a map. */ private static final int N = Integer.getInteger("n", 128); - // The number of iterations of the test - private static final int I = Integer.getInteger("i", 256); - // Object to be placed in the concurrent map + /** The number of iterations of the test. */ + private static final int I = Integer.getInteger("i", 64); + + /** Objects to be placed in the concurrent map. */ static class X { // Limit the hash code to trigger collisions - int hc = ThreadLocalRandom.current().nextInt(1, 9); + final int hc = ThreadLocalRandom.current().nextInt(1, 9); public int hashCode() { return hc; } } @Test - public void testPut() { + public void testPut() throws Throwable { test("CHM.put", (m, o) -> m.put(o, o)); } @Test - public void testCompute() { + public void testCompute() throws Throwable { test("CHM.compute", (m, o) -> m.compute(o, (k, v) -> o)); } @Test - public void testComputeIfAbsent() { + public void testComputeIfAbsent() throws Throwable { test("CHM.computeIfAbsent", (m, o) -> m.computeIfAbsent(o, (k) -> o)); } @Test - public void testMerge() { + public void testMerge() throws Throwable { test("CHM.merge", (m, o) -> m.merge(o, o, (v1, v2) -> v1)); } @Test - public void testPutAll() { + public void testPutAll() throws Throwable { test("CHM.putAll", (m, o) -> { Map hm = new HashMap<>(); hm.put(o, o); @@ -88,7 +96,7 @@ }); } - private static void test(String desc, BiConsumer, Object> associator) { + private static void test(String desc, BiConsumer, Object> associator) throws Throwable { for (int i = 0; i < I; i++) { testOnce(desc, associator); } @@ -100,13 +108,16 @@ } } - private static void testOnce(String desc, BiConsumer, Object> associator) { + private static void testOnce(String desc, BiConsumer, Object> associator) throws Throwable { ConcurrentHashMap m = new ConcurrentHashMap<>(); CountDownLatch s = new CountDownLatch(1); Supplier sr = () -> () -> { try { - s.await(); + if (!s.await(TIMEOUT, TimeUnit.SECONDS)) { + dumpTestThreads(); + throw new AssertionError("timed out"); + } } catch (InterruptedException e) { } @@ -121,7 +132,7 @@ }; // Bound concurrency to avoid degenerate performance - int ps = Math.min(Runtime.getRuntime().availableProcessors(), 32); + int ps = Math.min(Runtime.getRuntime().availableProcessors(), 8); Stream runners = IntStream.range(0, ps) .mapToObj(i -> sr.get()) .map(CompletableFuture::runAsync); @@ -131,16 +142,41 @@ // Trigger the runners to start associating s.countDown(); + try { - all.join(); - } catch (CompletionException e) { - Throwable t = e.getCause(); - if (t instanceof AssociationFailure) { - throw (AssociationFailure) t; + all.get(TIMEOUT, TimeUnit.SECONDS); + } catch (TimeoutException e) { + dumpTestThreads(); + throw e; + } catch (Throwable e) { + dumpTestThreads(); + Throwable cause = e.getCause(); + if (cause instanceof AssociationFailure) { + throw cause; } - else { - throw e; - } + throw e; } } + + /** + * A debugging tool to print stack traces of most threads, as jstack does. + * Uninteresting threads are filtered out. + */ + static void dumpTestThreads() { + ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); + System.err.println("------ stacktrace dump start ------"); + for (ThreadInfo info : threadMXBean.dumpAllThreads(true, true)) { + String name = info.getThreadName(); + if ("Signal Dispatcher".equals(name)) + continue; + if ("Reference Handler".equals(name) + && info.getLockName().startsWith("java.lang.ref.Reference$Lock")) + continue; + if ("Finalizer".equals(name) + && info.getLockName().startsWith("java.lang.ref.ReferenceQueue$Lock")) + continue; + System.err.print(info); + } + System.err.println("------ stacktrace dump end ------"); + } } diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/concurrent/ConcurrentHashMap/ConcurrentContainsKeyTest.java --- a/jdk/test/java/util/concurrent/ConcurrentHashMap/ConcurrentContainsKeyTest.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/concurrent/ConcurrentHashMap/ConcurrentContainsKeyTest.java Wed Jul 05 20:54:48 2017 +0200 @@ -80,7 +80,6 @@ test(content, m); } - private static void test(X[] content, ConcurrentHashMap m) { for (int i = 0; i < I; i++) { testOnce(content, m); diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/concurrent/ConcurrentHashMap/MapCheck.java --- a/jdk/test/java/util/concurrent/ConcurrentHashMap/MapCheck.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/concurrent/ConcurrentHashMap/MapCheck.java Wed Jul 05 20:54:48 2017 +0200 @@ -68,7 +68,6 @@ } } - if (args.length > 1) numTests = Integer.parseInt(args[1]); @@ -92,7 +91,6 @@ TestTimer.printStats(); - if (doSerializeTest) stest(newMap(mapClass), size); } diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/concurrent/ConcurrentHashMap/MapLoops.java --- a/jdk/test/java/util/concurrent/ConcurrentHashMap/MapLoops.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/concurrent/ConcurrentHashMap/MapLoops.java Wed Jul 05 20:54:48 2017 +0200 @@ -175,7 +175,7 @@ int position; int total; - Runner(Map map, Integer[] key, CyclicBarrier barrier) { + Runner(Map map, Integer[] key, CyclicBarrier barrier) { this.map = map; this.key = key; this.barrier = barrier; @@ -183,7 +183,7 @@ } int step() { - // random-walk around key positions, bunching accesses + // random-walk around key positions, bunching accesses int r = rng.next(); position += (r & 7) - 3; while (position >= key.length) position -= key.length; diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/concurrent/ConcurrentLinkedQueue/RemoveLeak.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/util/concurrent/ConcurrentLinkedQueue/RemoveLeak.java Wed Jul 05 20:54:48 2017 +0200 @@ -0,0 +1,60 @@ +/* + * 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Martin Buchholz with assistance from members of JCP + * JSR-166 Expert Group and released to the public domain, as + * explained at http://creativecommons.org/publicdomain/zero/1.0/ + */ + +/* + * @test + * @bug 8054446 8137184 8137185 + * @summary Regression test for memory leak in remove(Object) + * @run main/othervm -Xmx2200k RemoveLeak + */ + +import java.util.concurrent.ConcurrentLinkedQueue; + +public class RemoveLeak { + public static void main(String[] args) { + int i = 0; + // Without bug fix, OutOfMemoryError was observed at iteration 65120 + int iterations = 10 * 65120; + try { + ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue<>(); + queue.add(0L); + while (i++ < iterations) { + queue.add(1L); + queue.remove(1L); + } + } catch (Error t) { + System.err.printf("failed at iteration %d/%d%n", i, iterations); + throw t; + } + } +} diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/concurrent/ConcurrentQueues/ConcurrentQueueLoops.java --- a/jdk/test/java/util/concurrent/ConcurrentQueues/ConcurrentQueueLoops.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/concurrent/ConcurrentQueues/ConcurrentQueueLoops.java Wed Jul 05 20:54:48 2017 +0200 @@ -47,10 +47,10 @@ AtomicInteger totalItems; boolean print; - // Suitable for benchmarking. Overriden by args[0] for testing. + // Suitable for benchmarking. Overridden by args[0] for testing. int maxStages = 20; - // Suitable for benchmarking. Overriden by args[1] for testing. + // Suitable for benchmarking. Overridden by args[1] for testing. int items = 1024 * 1024; Collection> concurrentQueues() { diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/concurrent/ConcurrentQueues/GCRetention.java --- a/jdk/test/java/util/concurrent/ConcurrentQueues/GCRetention.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/concurrent/ConcurrentQueues/GCRetention.java Wed Jul 05 20:54:48 2017 +0200 @@ -56,7 +56,7 @@ import java.util.Map; public class GCRetention { - // Suitable for benchmarking. Overriden by args[0] for testing. + // Suitable for benchmarking. Overridden by args[0] for testing. int count = 1024 * 1024; final Map results = new ConcurrentHashMap(); diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/concurrent/ConcurrentQueues/IteratorWeakConsistency.java --- a/jdk/test/java/util/concurrent/ConcurrentQueues/IteratorWeakConsistency.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/concurrent/ConcurrentQueues/IteratorWeakConsistency.java Wed Jul 05 20:54:48 2017 +0200 @@ -42,6 +42,7 @@ @SuppressWarnings({"unchecked", "rawtypes"}) public class IteratorWeakConsistency { + final Random rnd = new Random(); void test(String[] args) throws Throwable { test(new LinkedBlockingQueue()); @@ -51,14 +52,46 @@ test(new ConcurrentLinkedDeque()); test(new ConcurrentLinkedQueue()); test(new LinkedTransferQueue()); - // Other concurrent queues (e.g. ArrayBlockingQueue) do not - // currently have weakly consistent iterators. - // As of 2010-09, ArrayBlockingQueue passes this test, but - // does not fully implement weak consistency. test(new ArrayBlockingQueue(20)); } + void checkExhausted(Iterator it) { + if (rnd.nextBoolean()) { + check(!it.hasNext()); + } + if (rnd.nextBoolean()) + try { it.next(); fail("should throw"); } + catch (NoSuchElementException success) {} + } + + void checkRemoveThrowsISE(Iterator it) { + if (rnd.nextBoolean()) { + try { it.remove(); fail("should throw"); } + catch (IllegalStateException success) {} + } + } + + void checkRemoveHasNoEffect(Iterator it, Collection c) { + if (rnd.nextBoolean()) { + int size = c.size(); + it.remove(); // no effect + equal(c.size(), size); + checkRemoveThrowsISE(it); + } + } + void test(Queue q) { + //---------------------------------------------------------------- + // Check iterators on an empty q + //---------------------------------------------------------------- + try { + for (int i = 0; i < 4; i++) { + Iterator it = q.iterator(); + if (rnd.nextBoolean()) checkExhausted(it); + checkRemoveThrowsISE(it); + } + } catch (Throwable t) { unexpected(t); } + // TODO: make this more general try { for (int i = 0; i < 10; i++) @@ -73,7 +106,7 @@ list.add(it.next()); equal(list, Arrays.asList(0, 3, 4, 5, 6, 8, 9)); check(! list.contains(null)); - System.out.printf("%s: %s%n", + System.err.printf("%s: %s%n", q.getClass().getSimpleName(), list); } catch (Throwable t) { unexpected(t); } @@ -96,6 +129,118 @@ check(found4); } catch (Throwable t) { unexpected(t); } + try { + q.clear(); + Object x = new Object(); + for (int i = 0; i < 20; i++) + q.add(x); + equal(20, q.size()); + Iterator it1 = q.iterator(); + Iterator it2 = q.iterator(); + try { it1.remove(); fail(); } + catch (IllegalStateException success) {} + try { it2.remove(); fail(); } + catch (IllegalStateException success) {} + + check(it1.next() == x); + check(it2.hasNext()); + check(it2.next() == x); + it1.remove(); + it2.remove(); + equal(19, q.size()); + try { it1.remove(); fail(); } + catch (IllegalStateException success) {} + try { it2.remove(); fail(); } + catch (IllegalStateException success) {} + equal(19, q.size()); + + it1.next(); + check(it2.hasNext()); + it2.next(); + it2.remove(); + it1.remove(); + equal(18, q.size()); + + it1.next(); + it2.next(); + check(q.remove() == x); + equal(17, q.size()); + it1.remove(); + it2.remove(); + equal(17, q.size()); + } catch (Throwable t) { unexpected(t); } + + //---------------------------------------------------------------- + // Check "interior" removal of alternating elements + //---------------------------------------------------------------- + try { + q.clear(); + final int remainingCapacity = (q instanceof BlockingQueue) ? + ((BlockingQueue)q).remainingCapacity() : + Integer.MAX_VALUE; + final int capacity = Math.min(20, remainingCapacity); + List its = new ArrayList(); + // Move to "middle" + for (int i = 0; i < capacity/2; i++) { + check(q.add(i)); + equal(q.poll(), i); + } + for (int i = 0; i < capacity; i++) + check(q.add(i)); + for (int i = 0; i < capacity; i++) { + Iterator it = q.iterator(); + its.add(it); + for (int j = 0; j < i; j++) + equal(j, it.next()); + } + + // Remove all even elements, in either direction using + // q.remove(), or iterator.remove() + switch (rnd.nextInt(3)) { + case 0: + for (int i = 0; i < capacity; i+=2) + check(q.remove(i)); + break; + case 1: + for (int i = capacity - 2; i >= 0; i-=2) + check(q.remove(i)); + break; + case 2: + Iterator it = q.iterator(); + while (it.hasNext()) { + int i = (Integer) it.next(); + if ((i & 1) == 0) + it.remove(); + } + break; + default: throw new Error(); + } + + for (int i = 0; i < capacity; i++) { + Iterator it = its.get(i); + boolean even = ((i & 1) == 0); + if (even) { + if (rnd.nextBoolean()) check(it.hasNext()); + equal(i, it.next()); + for (int j = i+1; j < capacity; j += 2) + equal(j, it.next()); + check(!it.hasNext()); + } else { /* odd */ + if (rnd.nextBoolean()) check(it.hasNext()); + checkRemoveHasNoEffect(it, q); + equal(i, it.next()); + for (int j = i+2; j < capacity; j += 2) + equal(j, it.next()); + check(!it.hasNext()); + } + } + + // q only contains odd elements + for (int i = 0; i < capacity; i++) + check(q.contains(i) ^ ((i & 1) == 0)); + + } catch (Throwable t) { unexpected(t); } + } //--------------------- Infrastructure --------------------------- @@ -112,6 +257,6 @@ new IteratorWeakConsistency().instanceMain(args);} public void instanceMain(String[] args) throws Throwable { try {test(args);} catch (Throwable t) {unexpected(t);} - System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed); + System.err.printf("%nPassed = %d, failed = %d%n%n", passed, failed); if (failed > 0) throw new AssertionError("Some tests failed");} } diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/concurrent/ConcurrentQueues/OfferRemoveLoops.java --- a/jdk/test/java/util/concurrent/ConcurrentQueues/OfferRemoveLoops.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/concurrent/ConcurrentQueues/OfferRemoveLoops.java Wed Jul 05 20:54:48 2017 +0200 @@ -32,6 +32,7 @@ import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.*; +import static java.util.concurrent.TimeUnit.*; @SuppressWarnings({"unchecked", "rawtypes", "deprecation"}) public class OfferRemoveLoops { @@ -65,15 +66,16 @@ } void testQueue(final Queue q) throws Throwable { - System.out.println(q.getClass().getSimpleName()); + System.err.println(q.getClass().getSimpleName()); final long testDurationNanos = testDurationMillis * 1000L * 1000L; final long quittingTimeNanos = System.nanoTime() + testDurationNanos; final long timeoutMillis = 10L * 1000L; final int maxChunkSize = 1042; final int maxQueueSize = 10 * maxChunkSize; + final CountDownLatch done = new CountDownLatch(3); - /** Poor man's bounded buffer. */ - final AtomicLong approximateCount = new AtomicLong(0L); + /** Poor man's bounded buffer; prevents unbounded queue expansion. */ + final Semaphore offers = new Semaphore(maxQueueSize); abstract class CheckedThread extends Thread { CheckedThread(String name) { @@ -89,42 +91,44 @@ protected boolean quittingTime(long i) { return (i % 1024) == 0 && quittingTime(); } - protected abstract void realRun(); + protected abstract void realRun() throws Exception; public void run() { try { realRun(); } catch (Throwable t) { unexpected(t); } } } Thread offerer = new CheckedThread("offerer") { - protected void realRun() { - final long chunkSize = getRandom().nextInt(maxChunkSize) + 2; + protected void realRun() throws InterruptedException { + final int chunkSize = getRandom().nextInt(maxChunkSize) + 20; long c = 0; - for (long i = 0; ! quittingTime(i); i++) { + while (! quittingTime()) { if (q.offer(Long.valueOf(c))) { if ((++c % chunkSize) == 0) { - approximateCount.getAndAdd(chunkSize); - while (approximateCount.get() > maxQueueSize) - Thread.yield(); + offers.acquire(chunkSize); } } else { Thread.yield(); - }}}}; + } + } + done.countDown(); + }}; Thread remover = new CheckedThread("remover") { protected void realRun() { - final long chunkSize = getRandom().nextInt(maxChunkSize) + 2; + final int chunkSize = getRandom().nextInt(maxChunkSize) + 20; long c = 0; - for (long i = 0; ! quittingTime(i); i++) { + while (! quittingTime()) { if (q.remove(Long.valueOf(c))) { if ((++c % chunkSize) == 0) { - approximateCount.getAndAdd(-chunkSize); + offers.release(chunkSize); } } else { Thread.yield(); } } q.clear(); - approximateCount.set(0); // Releases waiting offerer thread + offers.release(1<<30); // Releases waiting offerer thread + done.countDown(); }}; Thread scanner = new CheckedThread("scanner") { @@ -139,18 +143,19 @@ break; } Thread.yield(); - }}}; + } + done.countDown(); + }}; - for (Thread thread : new Thread[] { offerer, remover, scanner }) { - thread.join(timeoutMillis + testDurationMillis); - if (thread.isAlive()) { - System.err.printf("Hung thread: %s%n", thread.getName()); - failed++; - for (StackTraceElement e : thread.getStackTrace()) - System.err.println(e); - // Kludge alert - thread.stop(); - thread.join(timeoutMillis); + if (! done.await(timeoutMillis + testDurationMillis, MILLISECONDS)) { + for (Thread thread : new Thread[] { offerer, remover, scanner }) { + if (thread.isAlive()) { + System.err.printf("Hung thread: %s%n", thread.getName()); + failed++; + for (StackTraceElement e : thread.getStackTrace()) + System.err.println(e); + thread.interrupt(); + } } } } diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/concurrent/ConcurrentQueues/RemovePollRace.java --- a/jdk/test/java/util/concurrent/ConcurrentQueues/RemovePollRace.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/concurrent/ConcurrentQueues/RemovePollRace.java Wed Jul 05 20:54:48 2017 +0200 @@ -36,7 +36,7 @@ * @bug 6785442 * @summary Checks race between poll and remove(Object), while * occasionally moonlighting as a microbenchmark. - * @run main RemovePollRace 12345 + * @run main RemovePollRace 1234 */ import java.util.concurrent.ArrayBlockingQueue; @@ -56,7 +56,7 @@ import java.util.Map; public class RemovePollRace { - // Suitable for benchmarking. Overriden by args[0] for testing. + // Suitable for benchmarking. Overridden by args[0] for testing. int count = 1024 * 1024; final Map results = new ConcurrentHashMap(); @@ -100,6 +100,7 @@ void test(String[] args) throws Throwable { if (args.length > 0) count = Integer.valueOf(args[0]); + // Warmup for (Queue queue : concurrentQueues()) test(queue); diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/concurrent/CopyOnWriteArrayList/COWSubList.java --- a/jdk/test/java/util/concurrent/CopyOnWriteArrayList/COWSubList.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/concurrent/CopyOnWriteArrayList/COWSubList.java Wed Jul 05 20:54:48 2017 +0200 @@ -51,8 +51,7 @@ r.run(); throw new RuntimeException("Failed: expected IOOBE to be thrown"); } catch (IndexOutOfBoundsException x) { - // ok, excpeted + // ok, expected } } } - diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/concurrent/CountDownLatch/Basic.java --- a/jdk/test/java/util/concurrent/CountDownLatch/Basic.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/concurrent/CountDownLatch/Basic.java Wed Jul 05 20:54:48 2017 +0200 @@ -120,7 +120,7 @@ //---------------------------------------------------------------- // One thread interrupted //---------------------------------------------------------------- - public static void threadInterrupted() throws Throwable{ + public static void threadInterrupted() throws Throwable { int count = 0; Basic test = new Basic(); CountDownLatch latch = new CountDownLatch(3); diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/concurrent/Exchanger/ExchangeLoops.java --- a/jdk/test/java/util/concurrent/Exchanger/ExchangeLoops.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/concurrent/Exchanger/ExchangeLoops.java Wed Jul 05 20:54:48 2017 +0200 @@ -49,7 +49,6 @@ Int(int i) { value = i; } } - public static void main(String[] args) throws Exception { int maxStages = 5; int iters = 10000; diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/concurrent/ExecutorCompletionService/ExecutorCompletionServiceLoops.java --- a/jdk/test/java/util/concurrent/ExecutorCompletionService/ExecutorCompletionServiceLoops.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/concurrent/ExecutorCompletionService/ExecutorCompletionServiceLoops.java Wed Jul 05 20:54:48 2017 +0200 @@ -111,8 +111,6 @@ if (r == 0) // avoid overoptimization System.out.println("useless result: " + r); - - } } diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/concurrent/Executors/AutoShutdown.java --- a/jdk/test/java/util/concurrent/Executors/AutoShutdown.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/concurrent/Executors/AutoShutdown.java Wed Jul 05 20:54:48 2017 +0200 @@ -24,55 +24,71 @@ /* * @test * @bug 6399443 - * @run main/othervm AutoShutdown + * @run main/othervm/timeout=1000 AutoShutdown * @summary Check for auto-shutdown and gc of singleThreadExecutors * @author Martin Buchholz */ -import java.io.*; -import java.util.*; -import java.util.concurrent.*; -import static java.util.concurrent.Executors.*; -import java.util.concurrent.Phaser; +import java.lang.ref.WeakReference; +import java.util.Arrays; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; +import static java.util.concurrent.Executors.defaultThreadFactory; +import static java.util.concurrent.Executors.newFixedThreadPool; +import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor; +import static java.util.concurrent.Executors.newSingleThreadExecutor; public class AutoShutdown { - private static void waitForFinalizersToRun() { - for (int i = 0; i < 2; i++) - tryWaitForFinalizersToRun(); - } - private static void tryWaitForFinalizersToRun() { - System.gc(); - final CountDownLatch fin = new CountDownLatch(1); - new Object() { protected void finalize() { fin.countDown(); }}; - System.gc(); - try { fin.await(); } - catch (InterruptedException ie) { throw new Error(ie); } + static void await(CountDownLatch latch) throws InterruptedException { + if (!latch.await(100L, TimeUnit.SECONDS)) + throw new AssertionError("timed out waiting for latch"); } private static void realMain(String[] args) throws Throwable { - final Phaser phaser = new Phaser(3); - Runnable trivialRunnable = new Runnable() { - public void run() { - phaser.arriveAndAwaitAdvance(); - } + final Executor[] executors = { + newSingleThreadExecutor(), + newSingleThreadExecutor(defaultThreadFactory()), + // TODO: should these executors also auto-shutdown? + //newFixedThreadPool(1), + //newSingleThreadScheduledExecutor(), + //newSingleThreadScheduledExecutor(defaultThreadFactory()), }; - int count0 = Thread.activeCount(); - Executor e1 = newSingleThreadExecutor(); - Executor e2 = newSingleThreadExecutor(defaultThreadFactory()); - e1.execute(trivialRunnable); - e2.execute(trivialRunnable); - phaser.arriveAndAwaitAdvance(); - equal(Thread.activeCount(), count0 + 2); - e1 = e2 = null; - for (int i = 0; i < 10 && Thread.activeCount() > count0; i++) - tryWaitForFinalizersToRun(); - for (int i = 0; i < 10; ++i) { // give JVM a chance to settle. - if (Thread.activeCount() == count0) - return; - Thread.sleep(1000); + final ConcurrentLinkedQueue> poolThreads + = new ConcurrentLinkedQueue<>(); + final CountDownLatch threadStarted + = new CountDownLatch(executors.length); + final CountDownLatch pleaseProceed + = new CountDownLatch(1); + Runnable task = new Runnable() { public void run() { + try { + poolThreads.add(new WeakReference<>(Thread.currentThread())); + threadStarted.countDown(); + await(pleaseProceed); + } catch (Throwable t) { unexpected(t); } + }}; + for (Executor executor : executors) + executor.execute(task); + await(threadStarted); + pleaseProceed.countDown(); + Arrays.fill(executors, null); // make executors unreachable + boolean done = false; + for (long timeout = 1L; !done && timeout <= 128L; timeout *= 2) { + System.gc(); + done = true; + for (WeakReference ref : poolThreads) { + Thread thread = ref.get(); + if (thread != null) { + TimeUnit.SECONDS.timedJoin(thread, timeout); + if (thread.isAlive()) + done = false; + } + } } - equal(Thread.activeCount(), count0); + if (!done) + throw new AssertionError("pool threads did not terminate"); } //--------------------- Infrastructure --------------------------- diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/concurrent/FutureTask/DoneMeansDone.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/util/concurrent/FutureTask/DoneMeansDone.java Wed Jul 05 20:54:48 2017 +0200 @@ -0,0 +1,100 @@ +/* + * 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Martin Buchholz with assistance from members of JCP + * JSR-166 Expert Group and released to the public domain, as + * explained at http://creativecommons.org/publicdomain/zero/1.0/ + */ + +/* + * @test + * @bug 8073704 + * @summary Checks that once isDone() returns true, + * get() never throws InterruptedException or TimeoutException + */ + +import java.util.ArrayList; +import java.util.concurrent.Callable; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.FutureTask; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +public class DoneMeansDone { + public static void main(String[] args) throws Throwable { + final int iters = 1000; + final int nThreads = 2; + final AtomicBoolean done = new AtomicBoolean(false); + final AtomicReference> a = new AtomicReference<>(); + final CountDownLatch threadsStarted = new CountDownLatch(nThreads); + final Callable alwaysTrue = new Callable() { + public Boolean call() { + return true; + }}; + + final Runnable observer = new Runnable() { public void run() { + threadsStarted.countDown(); + final ThreadLocalRandom rnd = ThreadLocalRandom.current(); + try { + for (FutureTask f; !done.get();) { + f = a.get(); + if (f != null) { + do {} while (!f.isDone()); + Thread.currentThread().interrupt(); + if (!(rnd.nextBoolean() + ? f.get() + : f.get(-1L, TimeUnit.DAYS))) + throw new AssertionError(); + } + } + } catch (Exception t) { throw new AssertionError(t); } + }}; + + final ArrayList> futures = new ArrayList<>(nThreads); + final ExecutorService pool = Executors.newCachedThreadPool(); + for (int i = 0; i < nThreads; i++) + futures.add(pool.submit(observer)); + threadsStarted.await(); + for (int i = 0; i < iters; i++) { + FutureTask f = new FutureTask<>(alwaysTrue); + a.set(f); + f.run(); + } + done.set(true); + pool.shutdown(); + if (!pool.awaitTermination(10L, TimeUnit.SECONDS)) + throw new AssertionError(); + for (Future future : futures) + future.get(); + } +} diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/concurrent/FutureTask/DoneTimedGetLoops.java --- a/jdk/test/java/util/concurrent/FutureTask/DoneTimedGetLoops.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/concurrent/FutureTask/DoneTimedGetLoops.java Wed Jul 05 20:54:48 2017 +0200 @@ -1,5 +1,4 @@ /* - * Copyright (c) 2012, 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 @@ -22,6 +21,11 @@ */ /* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * * Written by Martin Buchholz with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at * http://creativecommons.org/publicdomain/zero/1.0/ diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/concurrent/FutureTask/ExplicitSet.java --- a/jdk/test/java/util/concurrent/FutureTask/ExplicitSet.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/concurrent/FutureTask/ExplicitSet.java Wed Jul 05 20:54:48 2017 +0200 @@ -58,7 +58,7 @@ SettableTask() { super(new Callable() { public Boolean call() { - fail ("The task should never be run!"); + fail("The task should never be run!"); return null; }; }); diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/concurrent/FutureTask/NegativeTimeout.java --- a/jdk/test/java/util/concurrent/FutureTask/NegativeTimeout.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/concurrent/FutureTask/NegativeTimeout.java Wed Jul 05 20:54:48 2017 +0200 @@ -44,4 +44,3 @@ } catch (TimeoutException success) {} } } - diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/concurrent/Phaser/Basic.java --- a/jdk/test/java/util/concurrent/Phaser/Basic.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/concurrent/Phaser/Basic.java Wed Jul 05 20:54:48 2017 +0200 @@ -97,7 +97,8 @@ equal(phase, atTheStartingGate.arrive()); int awaitPhase = atTheStartingGate.awaitAdvanceInterruptibly (phase, 30, SECONDS); - if (expectNextPhase) check(awaitPhase == (phase + 1)); + if (expectNextPhase) check(awaitPhase == phase + 1); + else check(awaitPhase == phase || awaitPhase == phase + 1); pass(); } catch (Throwable t) { @@ -249,13 +250,11 @@ Phaser phaser = new Phaser(3); Iterator arrivers = arriverIterator(phaser); int phase = phaser.getPhase(); - for (int i = 0; i < 4; i++) { + for (int i = 0; i < 10; i++) { check(phaser.getPhase() == phase); Awaiter a1 = awaiter(phaser, 30, SECONDS); a1.start(); Arriver a2 = arrivers.next(); a2.start(); toTheStartingGate(); - // allow a1 to block in awaitAdvanceInterruptibly - Thread.sleep(2000); a1.interrupt(); a1.join(); phaser.arriveAndAwaitAdvance(); @@ -273,7 +272,7 @@ // Phaser is terminated while threads are waiting //---------------------------------------------------------------- try { - for (int i = 0; i < 4; i++) { + for (int i = 0; i < 10; i++) { Phaser phaser = new Phaser(3); Iterator awaiters = awaiterIterator(phaser); Arriver a1 = awaiters.next(); a1.start(); diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/concurrent/ScheduledThreadPoolExecutor/DelayOverflow.java --- a/jdk/test/java/util/concurrent/ScheduledThreadPoolExecutor/DelayOverflow.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/concurrent/ScheduledThreadPoolExecutor/DelayOverflow.java Wed Jul 05 20:54:48 2017 +0200 @@ -88,7 +88,7 @@ /** * Attempts to test exhaustively and deterministically, all 20 * possible ways that one task can be scheduled in the maximal - * distant future, while at the same time an existing tasks's time + * distant future, while at the same time an existing task's time * has already expired. */ void test(String[] args) throws Throwable { diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/concurrent/ScheduledThreadPoolExecutor/GCRetention.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/util/concurrent/ScheduledThreadPoolExecutor/GCRetention.java Wed Jul 05 20:54:48 2017 +0200 @@ -0,0 +1,136 @@ +/* + * 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea and Martin Buchholz with assistance from members of + * JCP JSR-166 Expert Group and released to the public domain, as explained + * at http://creativecommons.org/publicdomain/zero/1.0/ + */ + +/* + * @test + * @summary Ensure that waiting pool threads don't retain refs to tasks. + */ + +import java.lang.ref.*; +import java.util.concurrent.*; + +public class GCRetention { + /** + * A custom thread pool with a custom RunnableScheduledFuture, for the + * sole purpose of ensuring that the task retains a strong reference to + * the Runnable even after cancellation. + */ + static class CustomPool extends ScheduledThreadPoolExecutor { + CustomPool(int n) { super(n); } + protected RunnableScheduledFuture decorateTask( + final Runnable r, + final RunnableScheduledFuture task) { + return new RunnableScheduledFuture() { + public void run() { System.err.println(r); task.run(); } + public boolean isPeriodic() { return task.isPeriodic(); } + public V get() + throws InterruptedException,ExecutionException + { return task.get(); } + public V get(long x, TimeUnit y) + throws InterruptedException,ExecutionException,TimeoutException + { return task.get(x, y); } + public boolean isDone() { return task.isDone(); } + public boolean isCancelled() { return task.isCancelled(); } + public boolean cancel(boolean x) { return task.cancel(x); } + public long getDelay(TimeUnit x) { return task.getDelay(x); } + public int compareTo(Delayed x) { return task.compareTo(x); } + }; + } + } + + int countRefsCleared(WeakReference[] refs) { + int count = 0; + for (WeakReference ref : refs) + if (ref.get() == null) + count++; + return count; + } + + void test(String[] args) throws Throwable { + CustomPool pool = new CustomPool(10); + final int size = 100; + WeakReference[] refs = new WeakReference[size]; + Future[] futures = new Future[size]; + for (int i = 0; i < size; i++) { + final Object x = new Object(); + refs[i] = new WeakReference(x); + // Create a Runnable with a strong ref to x. + Runnable r = new Runnable() { + public void run() { System.out.println(x); } + }; + // Schedule a custom task with a strong reference to r. + // Later tasks have earlier expiration, to ensure multiple + // residents of queue head. + futures[i] = pool.schedule(r, size*2-i, TimeUnit.MINUTES); + } + Thread.sleep(10); + for (int i = 0; i < size; i++) { + if (futures[i] != null) { + futures[i].cancel(false); + futures[i] = null; + } + } + pool.purge(); + int cleared = 0; + for (int i = 0; + i < 10 && (cleared = countRefsCleared(refs)) < size; + i++) { + System.gc(); + System.runFinalization(); + Thread.sleep(10); + } + pool.shutdown(); + pool.awaitTermination(10, TimeUnit.SECONDS); + if (cleared < size) + throw new Error(String.format + ("references to %d/%d tasks retained (\"leaked\")", + size - cleared, size)); + } + + //--------------------- Infrastructure --------------------------- + volatile int passed = 0, failed = 0; + void pass() {passed++;} + void fail() {failed++; Thread.dumpStack();} + void fail(String msg) {System.err.println(msg); fail();} + void unexpected(Throwable t) {failed++; t.printStackTrace();} + void check(boolean cond) {if (cond) pass(); else fail();} + void equal(Object x, Object y) { + if (x == null ? y == null : x.equals(y)) pass(); + else fail(x + " not equal to " + y);} + public static void main(String[] args) throws Throwable { + new GCRetention().instanceMain(args);} + void instanceMain(String[] args) throws Throwable { + try {test(args);} catch (Throwable t) {unexpected(t);} + System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed); + if (failed > 0) throw new AssertionError("Some tests failed");} +} diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/concurrent/ScheduledThreadPoolExecutor/ZeroCoreThreads.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/util/concurrent/ScheduledThreadPoolExecutor/ZeroCoreThreads.java Wed Jul 05 20:54:48 2017 +0200 @@ -0,0 +1,117 @@ +/* + * 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Martin Buchholz with assistance from members of JCP + * JSR-166 Expert Group and released to the public domain, as + * explained at http://creativecommons.org/publicdomain/zero/1.0/ + */ + +/* + * @test + * @bug 8022642 8065320 8129861 + * @summary Ensure relative sanity when zero core threads + */ + +import java.util.concurrent.*; +import static java.util.concurrent.TimeUnit.*; +import java.util.concurrent.locks.*; +import java.lang.reflect.*; + +public class ZeroCoreThreads { + static boolean hasWaiters(ReentrantLock lock, Condition condition) { + lock.lock(); + try { + return lock.hasWaiters(condition); + } finally { + lock.unlock(); + } + } + + static T getField(Object x, String fieldName) { + try { + Field field = x.getClass().getDeclaredField(fieldName); + field.setAccessible(true); + return (T) field.get(x); + } catch (ReflectiveOperationException ex) { + throw new AssertionError(ex); + } + } + + void test(String[] args) throws Throwable { + ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(0); + try { + test(p); + } finally { + p.shutdownNow(); + check(p.awaitTermination(10L, SECONDS)); + } + } + + void test(ScheduledThreadPoolExecutor p) throws Throwable { + Runnable dummy = new Runnable() { public void run() { + throw new AssertionError("shouldn't get here"); }}; + BlockingQueue q = p.getQueue(); + ReentrantLock lock = getField(q, "lock"); + Condition available = getField(q, "available"); + + equal(0, p.getPoolSize()); + equal(0, p.getLargestPoolSize()); + equal(0L, p.getTaskCount()); + equal(0L, p.getCompletedTaskCount()); + p.schedule(dummy, 1L, HOURS); + // Ensure one pool thread actually waits in timed queue poll + long t0 = System.nanoTime(); + while (!hasWaiters(lock, available)) { + if (System.nanoTime() - t0 > SECONDS.toNanos(10L)) + throw new AssertionError + ("timed out waiting for a waiter to show up"); + Thread.yield(); + } + equal(1, p.getPoolSize()); + equal(1, p.getLargestPoolSize()); + equal(1L, p.getTaskCount()); + equal(0L, p.getCompletedTaskCount()); + } + + //--------------------- Infrastructure --------------------------- + volatile int passed = 0, failed = 0; + void pass() {passed++;} + void fail() {failed++; Thread.dumpStack();} + void fail(String msg) {System.err.println(msg); fail();} + void unexpected(Throwable t) {failed++; t.printStackTrace();} + void check(boolean cond) {if (cond) pass(); else fail();} + void equal(Object x, Object y) { + if (x == null ? y == null : x.equals(y)) pass(); + else fail(x + " not equal to " + y);} + public static void main(String[] args) throws Throwable { + new ZeroCoreThreads().instanceMain(args);} + void instanceMain(String[] args) throws Throwable { + try {test(args);} catch (Throwable t) {unexpected(t);} + System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed); + if (failed > 0) throw new AssertionError("Some tests failed");} +} diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/concurrent/ThreadPoolExecutor/CoreThreadTimeOut.java --- a/jdk/test/java/util/concurrent/ThreadPoolExecutor/CoreThreadTimeOut.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/concurrent/ThreadPoolExecutor/CoreThreadTimeOut.java Wed Jul 05 20:54:48 2017 +0200 @@ -55,7 +55,7 @@ return count; } - long millisElapsedSince(long t0) { + static long millisElapsedSince(long t0) { return (System.nanoTime() - t0) / (1000L * 1000L); } diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/concurrent/ThreadPoolExecutor/Custom.java --- a/jdk/test/java/util/concurrent/ThreadPoolExecutor/Custom.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/concurrent/ThreadPoolExecutor/Custom.java Wed Jul 05 20:54:48 2017 +0200 @@ -41,7 +41,6 @@ if (x == null ? y == null : x.equals(y)) pass(); else {System.out.println(x + " not equal to " + y); fail(); }} - private static class CustomTask extends FutureTask { public static final AtomicInteger births = new AtomicInteger(0); CustomTask(Callable c) { super(c); births.getAndIncrement(); } diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/concurrent/ThreadPoolExecutor/FlakyThreadFactory.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/util/concurrent/ThreadPoolExecutor/FlakyThreadFactory.java Wed Jul 05 20:54:48 2017 +0200 @@ -0,0 +1,108 @@ +/* + * 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Martin Buchholz and Doug Lea with assistance from + * members of JCP JSR-166 Expert Group and released to the public + * domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +/* + * @test + * @summary Should be able to shutdown a pool when worker creation failed. + */ + +import java.util.concurrent.*; + +public class FlakyThreadFactory { + void test(String[] args) throws Throwable { + test(NullPointerException.class, + new ThreadFactory() { + public Thread newThread(Runnable r) { + throw new NullPointerException(); + }}); + test(OutOfMemoryError.class, + new ThreadFactory() { + public Thread newThread(Runnable r) { + new Thread(null, r, "a natural OOME", 1L << 60); + // """On some platforms, the value of the stackSize + // parameter may have no effect whatsoever.""" + throw new OutOfMemoryError("artificial OOME"); + }}); + test(null, + new ThreadFactory() { + public Thread newThread(Runnable r) { + return null; + }}); + } + + void test(final Class exceptionClass, + final ThreadFactory failingThreadFactory) + throws Throwable { + ThreadFactory flakyThreadFactory = new ThreadFactory() { + int seq = 0; + public Thread newThread(Runnable r) { + if (seq++ < 4) + return new Thread(r); + else + return failingThreadFactory.newThread(r); + }}; + ThreadPoolExecutor pool = + new ThreadPoolExecutor(10, 10, + 0L, TimeUnit.SECONDS, + new LinkedBlockingQueue(), + flakyThreadFactory); + try { + for (int i = 0; i < 8; i++) + pool.submit(new Runnable() { public void run() {} }); + check(exceptionClass == null); + } catch (Throwable t) { + /* t.printStackTrace(); */ + check(exceptionClass.isInstance(t)); + } + pool.shutdown(); + check(pool.awaitTermination(10L, TimeUnit.SECONDS)); + } + + //--------------------- Infrastructure --------------------------- + volatile int passed = 0, failed = 0; + void pass() {passed++;} + void fail() {failed++; Thread.dumpStack();} + void fail(String msg) {System.err.println(msg); fail();} + void unexpected(Throwable t) {failed++; t.printStackTrace();} + void check(boolean cond) {if (cond) pass(); else fail();} + void equal(Object x, Object y) { + if (x == null ? y == null : x.equals(y)) pass(); + else fail(x + " not equal to " + y);} + public static void main(String[] args) throws Throwable { + new FlakyThreadFactory().instanceMain(args);} + public void instanceMain(String[] args) throws Throwable { + try {test(args);} catch (Throwable t) {unexpected(t);} + System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed); + if (failed > 0) throw new AssertionError("Some tests failed");} +} diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/concurrent/ThreadPoolExecutor/ThreadRestarts.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/util/concurrent/ThreadPoolExecutor/ThreadRestarts.java Wed Jul 05 20:54:48 2017 +0200 @@ -0,0 +1,80 @@ +/* + * 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Martin Buchholz and Jason Mehrens with assistance from + * members of JCP JSR-166 Expert Group and released to the public + * domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +/* + * @test + * @summary Only one thread should be created when a thread needs to + * be kept alive to service a delayed task waiting in the queue. + */ + +import java.util.concurrent.*; +import static java.util.concurrent.TimeUnit.*; +import java.util.concurrent.atomic.*; + +public class ThreadRestarts { + public static void main(String[] args) throws Exception { + test(false); + test(true); + } + + private static void test(boolean allowTimeout) throws Exception { + CountingThreadFactory ctf = new CountingThreadFactory(); + ScheduledThreadPoolExecutor stpe + = new ScheduledThreadPoolExecutor(10, ctf); + try { + Runnable nop = new Runnable() { public void run() {}}; + stpe.schedule(nop, 10*1000L, MILLISECONDS); + stpe.setKeepAliveTime(1L, MILLISECONDS); + stpe.allowCoreThreadTimeOut(allowTimeout); + MILLISECONDS.sleep(100L); + } finally { + stpe.shutdownNow(); + stpe.awaitTermination(Long.MAX_VALUE, MILLISECONDS); + } + if (ctf.count.get() > 1) + throw new AssertionError( + String.format("%d threads created, 1 expected", + ctf.count.get())); + } + + static class CountingThreadFactory implements ThreadFactory { + final AtomicLong count = new AtomicLong(0L); + + public Thread newThread(Runnable r) { + Thread t = new Thread(r); + count.getAndIncrement(); + return t; + } + } +} diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/concurrent/forkjoin/FJExceptionTableLeak.java --- a/jdk/test/java/util/concurrent/forkjoin/FJExceptionTableLeak.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/concurrent/forkjoin/FJExceptionTableLeak.java Wed Jul 05 20:54:48 2017 +0200 @@ -36,11 +36,12 @@ * @author Doug Lea * @bug 8004138 * @summary Check if ForkJoinPool table leaks thrown exceptions. - * @run main/othervm -Xmx32m FJExceptionTableLeak + * @run main/othervm/timeout=1200 -Xmx32m FJExceptionTableLeak */ import java.util.concurrent.*; public class FJExceptionTableLeak { + // TODO: make this test use less time! // Run with TASKS_PER_STEP * 40 < Xmx < STEPS * TASKS_PER_STEP * 40 // These work for Xmx32m: diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/concurrent/forkjoin/Integrate.java --- a/jdk/test/java/util/concurrent/forkjoin/Integrate.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/concurrent/forkjoin/Integrate.java Wed Jul 05 20:54:48 2017 +0200 @@ -59,14 +59,15 @@ static final int DYNAMIC = 0; static final int FORK = 1; - // the function to integrate - static double computeFunction(double x) { + /** the function to integrate */ + static double computeFunction(double x) { return (x * x + 1.0) * x; } static final double start = 0.0; static final double end = 1536.0; - /* + + /** * The number of recursive calls for * integrate from start to end. * (Empirically determined) @@ -127,7 +128,6 @@ g.shutdown(); } - // Sequential version static final class SQuad extends RecursiveAction { static double computeArea(ForkJoinPool pool, double l, double r) { diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/concurrent/locks/Lock/CheckedLockLoops.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/util/concurrent/locks/Lock/CheckedLockLoops.java Wed Jul 05 20:54:48 2017 +0200 @@ -0,0 +1,412 @@ +/* + * 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +/* + * @test + * @bug 4486658 + * @run main/timeout=7200 CheckedLockLoops + * @summary basic safety and liveness of ReentrantLocks, and other locks based on them + */ + +import java.util.concurrent.*; +import java.util.concurrent.locks.*; +import java.util.*; + +public final class CheckedLockLoops { + static final ExecutorService pool = Executors.newCachedThreadPool(); + static final LoopHelpers.SimpleRandom rng = new LoopHelpers.SimpleRandom(); + static boolean print = false; + static boolean doBuiltin = false; + + public static void main(String[] args) throws Exception { + int maxThreads = 5; + int iters = 100000; + + if (args.length > 0) + maxThreads = Integer.parseInt(args[0]); + + rng.setSeed(3122688L); + + print = false; + System.out.println("Warmup..."); + oneTest(3, 10000); + Thread.sleep(1000); + oneTest(2, 10000); + Thread.sleep(100); + oneTest(1, 100000); + Thread.sleep(100); + oneTest(1, 100000); + Thread.sleep(1000); + print = true; + + for (int i = 1; i <= maxThreads; i += (i+1) >>> 1) { + System.out.println("Threads:" + i); + oneTest(i, iters / i); + Thread.sleep(100); + } + pool.shutdown(); + if (! pool.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS)) + throw new Error(); + } + + static void oneTest(int nthreads, int iters) throws Exception { + int v = rng.next(); + if (doBuiltin) { + if (print) + System.out.print("builtin lock "); + new BuiltinLockLoop().test(v, nthreads, iters); + Thread.sleep(10); + } + + if (print) + System.out.print("ReentrantLock "); + new ReentrantLockLoop().test(v, nthreads, iters); + Thread.sleep(10); + + if (print) + System.out.print("Mutex "); + new MutexLoop().test(v, nthreads, iters); + Thread.sleep(10); + + if (print) + System.out.print("ReentrantWriteLock "); + new ReentrantWriteLockLoop().test(v, nthreads, iters); + Thread.sleep(10); + + if (print) + System.out.print("ReentrantReadWriteLock"); + new ReentrantReadWriteLockLoop().test(v, nthreads, iters); + Thread.sleep(10); + + if (print) + System.out.print("Semaphore "); + new SemaphoreLoop().test(v, nthreads, iters); + Thread.sleep(10); + + if (print) + System.out.print("fair Semaphore "); + new FairSemaphoreLoop().test(v, nthreads, iters); + Thread.sleep(10); + + if (print) + System.out.print("FairReentrantLock "); + new FairReentrantLockLoop().test(v, nthreads, iters); + Thread.sleep(10); + + if (print) + System.out.print("FairRWriteLock "); + new FairReentrantWriteLockLoop().test(v, nthreads, iters); + Thread.sleep(10); + + if (print) + System.out.print("FairRReadWriteLock "); + new FairReentrantReadWriteLockLoop().test(v, nthreads, iters); + Thread.sleep(10); + } + + abstract static class LockLoop implements Runnable { + int value; + int checkValue; + int iters; + volatile int result; + final LoopHelpers.BarrierTimer timer = new LoopHelpers.BarrierTimer(); + CyclicBarrier barrier; + + final int setValue(int v) { + checkValue = v ^ 0x55555555; + value = v; + return v; + } + + final int getValue() { + int v = value; + if (checkValue != ~(v ^ 0xAAAAAAAA)) + throw new Error("lock protection failure"); + return v; + } + + final void test(int initialValue, int nthreads, int iters) throws Exception { + setValue(initialValue); + this.iters = iters; + barrier = new CyclicBarrier(nthreads+1, timer); + for (int i = 0; i < nthreads; ++i) + pool.execute(this); + barrier.await(); + barrier.await(); + long time = timer.getTime(); + if (print) { + long tpi = time / (iters * nthreads); + System.out.print("\t" + LoopHelpers.rightJustify(tpi) + " ns per update"); + // double secs = (double)(time) / 1000000000.0; + // System.out.print("\t " + secs + "s run time"); + System.out.println(); + } + + if (result == 0) // avoid overoptimization + System.out.println("useless result: " + result); + } + abstract int loop(int n); + public final void run() { + try { + barrier.await(); + result += loop(iters); + barrier.await(); + } + catch (Exception ie) { + return; + } + } + + } + + private static class BuiltinLockLoop extends LockLoop { + final int loop(int n) { + int sum = 0; + int x = 0; + while (n-- > 0) { + synchronized (this) { + x = setValue(LoopHelpers.compute1(getValue())); + } + sum += LoopHelpers.compute2(x); + } + return sum; + } + } + + private static class ReentrantLockLoop extends LockLoop { + private final ReentrantLock lock = new ReentrantLock(); + final int loop(int n) { + final ReentrantLock lock = this.lock; + int sum = 0; + int x = 0; + while (n-- > 0) { + lock.lock(); + try { + x = setValue(LoopHelpers.compute1(getValue())); + } + finally { + lock.unlock(); + } + sum += LoopHelpers.compute2(x); + } + return sum; + } + } + + private static class MutexLoop extends LockLoop { + private final Mutex lock = new Mutex(); + final int loop(int n) { + final Mutex lock = this.lock; + int sum = 0; + int x = 0; + while (n-- > 0) { + lock.lock(); + try { + x = setValue(LoopHelpers.compute1(getValue())); + } + finally { + lock.unlock(); + } + sum += LoopHelpers.compute2(x); + } + return sum; + } + } + + private static class FairReentrantLockLoop extends LockLoop { + private final ReentrantLock lock = new ReentrantLock(true); + final int loop(int n) { + final ReentrantLock lock = this.lock; + int sum = 0; + int x = 0; + while (n-- > 0) { + lock.lock(); + try { + x = setValue(LoopHelpers.compute1(getValue())); + } + finally { + lock.unlock(); + } + sum += LoopHelpers.compute2(x); + } + return sum; + } + } + + private static class ReentrantWriteLockLoop extends LockLoop { + private final Lock lock = new ReentrantReadWriteLock().writeLock(); + final int loop(int n) { + final Lock lock = this.lock; + int sum = 0; + int x = 0; + while (n-- > 0) { + lock.lock(); + try { + x = setValue(LoopHelpers.compute1(getValue())); + } + finally { + lock.unlock(); + } + sum += LoopHelpers.compute2(x); + } + return sum; + } + } + + private static class FairReentrantWriteLockLoop extends LockLoop { + final Lock lock = new ReentrantReadWriteLock(true).writeLock(); + final int loop(int n) { + final Lock lock = this.lock; + int sum = 0; + int x = 0; + while (n-- > 0) { + lock.lock(); + try { + x = setValue(LoopHelpers.compute1(getValue())); + } + finally { + lock.unlock(); + } + sum += LoopHelpers.compute2(x); + } + return sum; + } + } + + private static class SemaphoreLoop extends LockLoop { + private final Semaphore sem = new Semaphore(1, false); + final int loop(int n) { + final Semaphore sem = this.sem; + int sum = 0; + int x = 0; + while (n-- > 0) { + sem.acquireUninterruptibly(); + try { + x = setValue(LoopHelpers.compute1(getValue())); + } + finally { + sem.release(); + } + sum += LoopHelpers.compute2(x); + } + return sum; + } + } + private static class FairSemaphoreLoop extends LockLoop { + private final Semaphore sem = new Semaphore(1, true); + final int loop(int n) { + final Semaphore sem = this.sem; + int sum = 0; + int x = 0; + while (n-- > 0) { + sem.acquireUninterruptibly(); + try { + x = setValue(LoopHelpers.compute1(getValue())); + } + finally { + sem.release(); + } + sum += LoopHelpers.compute2(x); + } + return sum; + } + } + + private static class ReentrantReadWriteLockLoop extends LockLoop { + private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + final int loop(int n) { + final Lock rlock = lock.readLock(); + final Lock wlock = lock.writeLock(); + int sum = 0; + int x = 0; + while (n-- > 0) { + if ((n & 16) != 0) { + rlock.lock(); + try { + x = LoopHelpers.compute1(getValue()); + x = LoopHelpers.compute2(x); + } + finally { + rlock.unlock(); + } + } + else { + wlock.lock(); + try { + setValue(x); + } + finally { + wlock.unlock(); + } + sum += LoopHelpers.compute2(x); + } + } + return sum; + } + + } + + private static class FairReentrantReadWriteLockLoop extends LockLoop { + private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true); + final int loop(int n) { + final Lock rlock = lock.readLock(); + final Lock wlock = lock.writeLock(); + int sum = 0; + int x = 0; + while (n-- > 0) { + if ((n & 16) != 0) { + rlock.lock(); + try { + x = LoopHelpers.compute1(getValue()); + x = LoopHelpers.compute2(x); + } + finally { + rlock.unlock(); + } + } + else { + wlock.lock(); + try { + setValue(x); + } + finally { + wlock.unlock(); + } + sum += LoopHelpers.compute2(x); + } + } + return sum; + } + + } +} diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/concurrent/locks/Lock/LoopHelpers.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/util/concurrent/locks/Lock/LoopHelpers.java Wed Jul 05 20:54:48 2017 +0200 @@ -0,0 +1,129 @@ +/* + * 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +/** + * Misc utilities in JSR166 performance tests + */ + +import java.util.concurrent.*; +import java.util.concurrent.atomic.*; + +class LoopHelpers { + + // Some mindless computation to do between synchronizations... + + /** + * generates 32 bit pseudo-random numbers. + * Adapted from http://www.snippets.org + */ + public static int compute1(int x) { + int lo = 16807 * (x & 0xFFFF); + int hi = 16807 * (x >>> 16); + lo += (hi & 0x7FFF) << 16; + if ((lo & 0x80000000) != 0) { + lo &= 0x7fffffff; + ++lo; + } + lo += hi >>> 15; + if (lo == 0 || (lo & 0x80000000) != 0) { + lo &= 0x7fffffff; + ++lo; + } + return lo; + } + + /** + * Computes a linear congruential random number a random number + * of times. + */ + public static int compute2(int x) { + int loops = (x >>> 4) & 7; + while (loops-- > 0) { + x = (x * 2147483647) % 16807; + } + return x; + } + + /** + * An actually useful random number generator, but unsynchronized. + * Basically same as java.util.Random. + */ + public static class SimpleRandom { + private static final long multiplier = 0x5DEECE66DL; + private static final long addend = 0xBL; + private static final long mask = (1L << 48) - 1; + static final AtomicLong seq = new AtomicLong(1); + private long seed = System.nanoTime() + seq.getAndIncrement(); + + public void setSeed(long s) { + seed = s; + } + + public int next() { + long nextseed = (seed * multiplier + addend) & mask; + seed = nextseed; + return ((int)(nextseed >>> 17)) & 0x7FFFFFFF; + } + } + + public static class BarrierTimer implements Runnable { + public volatile long startTime; + public volatile long endTime; + public void run() { + long t = System.nanoTime(); + if (startTime == 0) + startTime = t; + else + endTime = t; + } + public void clear() { + startTime = 0; + endTime = 0; + } + public long getTime() { + return endTime - startTime; + } + } + + public static String rightJustify(long n) { + // There's probably a better way to do this... + String field = " "; + String num = Long.toString(n); + if (num.length() >= field.length()) + return num; + StringBuffer b = new StringBuffer(field); + b.replace(b.length()-num.length(), b.length(), num); + return b.toString(); + } + +} diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/concurrent/locks/Lock/Mutex.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/util/concurrent/locks/Lock/Mutex.java Wed Jul 05 20:54:48 2017 +0200 @@ -0,0 +1,82 @@ +/* + * 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.locks.*; +import java.util.concurrent.atomic.*; +import java.io.*; + +/** + * A sample user extension of AbstractQueuedSynchronizer. + */ +public class Mutex implements Lock, java.io.Serializable { + private static class Sync extends AbstractQueuedSynchronizer { + public boolean isHeldExclusively() { return getState() == 1; } + + public boolean tryAcquire(int acquires) { + assert acquires == 1; // Does not use multiple acquires + return compareAndSetState(0, 1); + } + + public boolean tryRelease(int releases) { + setState(0); + return true; + } + + Condition newCondition() { return new ConditionObject(); } + + private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { + s.defaultReadObject(); + setState(0); // reset to unlocked state + } + } + + private final Sync sync = new Sync(); + public void lock() { + sync.acquire(1); + } + public boolean tryLock() { + return sync.tryAcquire(1); + } + public void lockInterruptibly() throws InterruptedException { + sync.acquireInterruptibly(1); + } + public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { + return sync.tryAcquireNanos(1, unit.toNanos(timeout)); + } + public void unlock() { sync.release(1); } + public Condition newCondition() { return sync.newCondition(); } + public boolean isLocked() { return sync.isHeldExclusively(); } + public boolean hasQueuedThreads() { return sync.hasQueuedThreads(); } +} diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/concurrent/locks/Lock/TimedAcquireLeak.java --- a/jdk/test/java/util/concurrent/locks/Lock/TimedAcquireLeak.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/concurrent/locks/Lock/TimedAcquireLeak.java Wed Jul 05 20:54:48 2017 +0200 @@ -28,14 +28,32 @@ * @author Martin Buchholz */ -// Note: this file is now out of sync with the jsr166 CVS repository due to the fix for 7092140 +import static java.util.concurrent.TimeUnit.NANOSECONDS; +import static java.util.concurrent.TimeUnit.SECONDS; -import java.util.*; -import java.util.regex.*; -import java.util.concurrent.*; -import java.util.concurrent.locks.*; -import static java.util.concurrent.TimeUnit.*; -import java.io.*; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintStream; +import java.io.Reader; +import java.lang.ref.WeakReference; +import java.util.Random; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.Callable; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.Semaphore; +import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class TimedAcquireLeak { static String javahome() { @@ -96,18 +114,34 @@ Callable callable) throws Throwable { p.getInputStream().read(); T result = callable.call(); - OutputStream os = p.getOutputStream(); - os.write((byte)'\n'); os.flush(); + sendByte(p.getOutputStream()); return result; } + /** No guarantees, but effective in practice. */ + private static void forceFullGc() { + CountDownLatch finalizeDone = new CountDownLatch(1); + WeakReference ref = new WeakReference(new Object() { + protected void finalize() { finalizeDone.countDown(); }}); + try { + for (int i = 0; i < 10; i++) { + System.gc(); + if (finalizeDone.await(1L, SECONDS) && ref.get() == null) { + System.runFinalization(); // try to pick up stragglers + return; + } + } + } catch (InterruptedException unexpected) { + throw new AssertionError("unexpected InterruptedException"); + } + throw new AssertionError("failed to do a \"full\" gc"); + } + // To be called exactly twice by the child process public static void rendezvousChild() { try { - for (int i = 0; i < 100; i++) { - System.gc(); System.runFinalization(); Thread.sleep(50); - } - System.out.write((byte)'\n'); System.out.flush(); + forceFullGc(); + sendByte(System.out); System.in.read(); } catch (Throwable t) { throw new Error(t); } } @@ -118,6 +152,12 @@ return matcher.group(group); } + /** It's all about sending a message! */ + static void sendByte(OutputStream s) throws IOException { + s.write('!'); + s.flush(); + } + static int objectsInUse(final Process child, final String childPid, final String className) { @@ -155,6 +195,9 @@ childClassName, uniqueID }; final Process p = new ProcessBuilder(jobCmd).start(); + // Ensure subprocess jvm has started, so that jps can find it + p.getInputStream().read(); + sendByte(p.getOutputStream()); final String childPid = match(commandOutputOf(jps, "-m"), @@ -167,10 +210,19 @@ failed += p.exitValue(); // Check that no objects were leaked. + // + // TODO: This test is very brittle, depending on current JDK + // implementation, and needing occasional adjustment. System.out.printf("%d -> %d%n", n0, n1); - check(Math.abs(n1 - n0) < 2); // Almost always n0 == n1 - check(n1 < 20); + // Almost always n0 == n1 + // Maximum jitter observed in practice is 10 -> 17 + check(Math.abs(n1 - n0) < 10); + check(n1 < 25); drainers.shutdown(); + if (!drainers.awaitTermination(10L, SECONDS)) { + drainers.shutdownNow(); // last resort + throw new AssertionError("thread pool did not terminate"); + } } //---------------------------------------------------------------- @@ -187,6 +239,10 @@ } public static void main(String[] args) throws Throwable { + // Synchronize with parent process, so that jps can find us + sendByte(System.out); + System.in.read(); + final ReentrantLock lock = new ReentrantLock(); lock.lock(); diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/concurrent/locks/LockSupport/ParkLoops.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/util/concurrent/locks/LockSupport/ParkLoops.java Wed Jul 05 20:54:48 2017 +0200 @@ -0,0 +1,163 @@ +/* + * 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Martin Buchholz with assistance from members of JCP + * JSR-166 Expert Group and released to the public domain, as + * explained at http://creativecommons.org/publicdomain/zero/1.0/ + */ + +/* + * @test + * @bug 8074773 + * @summary Stress test looks for lost unparks + * @run main/timeout=1200 ParkLoops + */ + +import static java.util.concurrent.TimeUnit.SECONDS; +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReferenceArray; +import java.util.concurrent.locks.LockSupport; + +public final class ParkLoops { + static final int THREADS = 4; // must be power of two + // static final int ITERS = 2_000_000; + // static final int TIMEOUT = 3500; // in seconds + static final int ITERS = 100_000; + static final int TIMEOUT = 1000; // in seconds + + static class Parker implements Runnable { + static { + // Reduce the risk of rare disastrous classloading in first call to + // LockSupport.park: https://bugs.openjdk.java.net/browse/JDK-8074773 + Class ensureLoaded = LockSupport.class; + } + + private final AtomicReferenceArray threads; + private final CountDownLatch done; + + Parker(AtomicReferenceArray threads, CountDownLatch done) { + this.threads = threads; + this.done = done; + } + + public void run() { + final SimpleRandom rng = new SimpleRandom(); + final Thread current = Thread.currentThread(); + for (int k = ITERS, j; k > 0; k--) { + do { + j = rng.next() & (THREADS - 1); + } while (!threads.compareAndSet(j, null, current)); + do { // handle spurious wakeups + LockSupport.park(); + } while (threads.get(j) == current); + } + done.countDown(); + } + } + + static class Unparker implements Runnable { + static { + // Reduce the risk of rare disastrous classloading in first call to + // LockSupport.park: https://bugs.openjdk.java.net/browse/JDK-8074773 + Class ensureLoaded = LockSupport.class; + } + + private final AtomicReferenceArray threads; + private final CountDownLatch done; + + Unparker(AtomicReferenceArray threads, CountDownLatch done) { + this.threads = threads; + this.done = done; + } + + public void run() { + final SimpleRandom rng = new SimpleRandom(); + for (int n = 0; (n++ & 0xff) != 0 || done.getCount() > 0;) { + int j = rng.next() & (THREADS - 1); + Thread parker = threads.get(j); + if (parker != null && + threads.compareAndSet(j, parker, null)) { + LockSupport.unpark(parker); + } + } + } + } + + public static void main(String[] args) throws Exception { + final ExecutorService pool = Executors.newCachedThreadPool(); + final AtomicReferenceArray threads + = new AtomicReferenceArray<>(THREADS); + final CountDownLatch done = new CountDownLatch(THREADS); + final Runnable parker = new Parker(threads, done); + final Runnable unparker = new Unparker(threads, done); + for (int i = 0; i < THREADS; i++) { + pool.submit(parker); + pool.submit(unparker); + } + try { + if (!done.await(TIMEOUT, SECONDS)) { + dumpAllStacks(); + throw new AssertionError("lost unpark"); + } + } finally { + pool.shutdown(); + pool.awaitTermination(10L, SECONDS); + } + } + + static void dumpAllStacks() { + ThreadInfo[] threadInfos = + ManagementFactory.getThreadMXBean().dumpAllThreads(true, true); + for (ThreadInfo threadInfo : threadInfos) { + System.err.print(threadInfo); + } + } + + /** + * An actually useful random number generator, but unsynchronized. + * Basically same as java.util.Random. + */ + public static class SimpleRandom { + private static final long multiplier = 0x5DEECE66DL; + private static final long addend = 0xBL; + private static final long mask = (1L << 48) - 1; + static final AtomicLong seq = new AtomicLong(1); + private long seed = System.nanoTime() + seq.getAndIncrement(); + + public int next() { + long nextseed = (seed * multiplier + addend) & mask; + seed = nextseed; + return ((int)(nextseed >>> 17)) & 0x7FFFFFFF; + } + } +} diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/concurrent/locks/ReentrantLock/CancelledLockLoops.java --- a/jdk/test/java/util/concurrent/locks/ReentrantLock/CancelledLockLoops.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/concurrent/locks/ReentrantLock/CancelledLockLoops.java Wed Jul 05 20:54:48 2017 +0200 @@ -35,10 +35,10 @@ * @test * @bug 4486658 * @run main/timeout=2800 CancelledLockLoops - * @summary tests lockInterruptibly. - * Checks for responsiveness of locks to interrupts. Runs under that - * assumption that ITERS_VALUE computations require more than TIMEOUT - * msecs to complete. + * @summary tests ReentrantLock.lockInterruptibly. + * Checks for responsiveness of locks to interrupts. Runs under the + * assumption that ITERS computations require more than TIMEOUT msecs + * to complete. */ import java.util.concurrent.*; @@ -48,11 +48,12 @@ public final class CancelledLockLoops { static final Random rng = new Random(); static boolean print = false; - static final int ITERS = 5000000; + static final int ITERS = 1000000; static final long TIMEOUT = 100; public static void main(String[] args) throws Exception { int maxThreads = (args.length > 0) ? Integer.parseInt(args[0]) : 5; + print = true; for (int i = 2; i <= maxThreads; i += (i+1) >>> 1) { @@ -112,7 +113,7 @@ lock.unlock(); } if (c != 2) - throw new Error("Completed != 2"); + throw new Error("Completed == " + c + "; expected 2"); int r = result; if (r == 0) // avoid overoptimization System.out.println("useless result: " + r); diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/concurrent/locks/ReentrantLock/TimeoutLockLoops.java --- a/jdk/test/java/util/concurrent/locks/ReentrantLock/TimeoutLockLoops.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/concurrent/locks/ReentrantLock/TimeoutLockLoops.java Wed Jul 05 20:54:48 2017 +0200 @@ -57,7 +57,6 @@ if (args.length > 0) maxThreads = Integer.parseInt(args[0]); - print = true; for (int i = 1; i <= maxThreads; i += (i+1) >>> 1) { @@ -140,6 +139,4 @@ } } } - - } diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/concurrent/locks/ReentrantReadWriteLock/MapLoops.java --- a/jdk/test/java/util/concurrent/locks/ReentrantReadWriteLock/MapLoops.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/concurrent/locks/ReentrantReadWriteLock/MapLoops.java Wed Jul 05 20:54:48 2017 +0200 @@ -134,7 +134,7 @@ int position; int total; - Runner(Map map, Integer[] key, CyclicBarrier barrier) { + Runner(Map map, Integer[] key, CyclicBarrier barrier) { this.map = map; this.key = key; this.barrier = barrier; @@ -142,7 +142,7 @@ } int step() { - // random-walk around key positions, bunching accesses + // random-walk around key positions, bunching accesses int r = rng.next(); position += (r & 7) - 3; while (position >= key.length) position -= key.length; @@ -156,7 +156,7 @@ throw new Error("bad mapping: " + x + " to " + k); if (r < removesPerMaxRandom) { - // get awy from this position + // get away from this position position = r % key.length; map.remove(k); return 2; diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/concurrent/locks/ReentrantReadWriteLock/RWMap.java --- a/jdk/test/java/util/concurrent/locks/ReentrantReadWriteLock/RWMap.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/concurrent/locks/ReentrantReadWriteLock/RWMap.java Wed Jul 05 20:54:48 2017 +0200 @@ -35,13 +35,11 @@ import java.util.concurrent.*; import java.util.concurrent.locks.*; - /** * This is an incomplete implementation of a wrapper class * that places read-write locks around unsynchronized Maps. * Exists as a sample input for MapLoops test. */ - public class RWMap implements Map { private final Map m; private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); @@ -86,7 +84,6 @@ finally { rwl.readLock().unlock(); } } - public Set keySet() { // Not implemented return null; } diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/concurrent/locks/StampedLock/Basic.java --- a/jdk/test/java/util/concurrent/locks/StampedLock/Basic.java Wed Oct 21 15:15:26 2015 -0700 +++ b/jdk/test/java/util/concurrent/locks/StampedLock/Basic.java Wed Jul 05 20:54:48 2017 +0200 @@ -78,7 +78,7 @@ abstract static class Locker extends Thread { static AtomicInteger count = new AtomicInteger(1); private volatile Throwable thrown; - private volatile long stamp;; + private volatile long stamp; protected void thrown(Throwable thrown) { this.thrown = thrown; } public Throwable thrown() { return thrown; } protected void stamp(long stamp) { this.stamp = stamp; } @@ -371,7 +371,7 @@ check(!sl.tryUnlockRead()); check(!sl.tryUnlockWrite()); check(sl.tryOptimisticRead() != 0L); - Locker[] wThreads = new Locker[100];; + Locker[] wThreads = new Locker[100]; for (int j=0; j<100; j++) wThreads[j] = writers.next(); for (int j=0; j<100; j++) @@ -401,7 +401,7 @@ check(!sl.tryUnlockRead()); check(!sl.tryUnlockWrite()); check(sl.tryOptimisticRead() != 0L); - Locker[] rThreads = new Locker[100];; + Locker[] rThreads = new Locker[100]; for (int j=0; j<100; j++) rThreads[j] = readers.next(); for (int j=0; j<100; j++) diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/logging/LogManager/Configuration/updateConfiguration/HandlersOnComplexResetUpdate.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/util/logging/LogManager/Configuration/updateConfiguration/HandlersOnComplexResetUpdate.java Wed Jul 05 20:54:48 2017 +0200 @@ -0,0 +1,548 @@ +/* + * Copyright (c) 2015, 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. + */ +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FilePermission; +import java.io.IOException; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.CodeSource; +import java.security.Permission; +import java.security.PermissionCollection; +import java.security.Permissions; +import java.security.Policy; +import java.security.ProtectionDomain; +import java.util.Arrays; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; +import java.util.Properties; +import java.util.UUID; +import java.util.concurrent.Callable; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.logging.FileHandler; +import java.util.logging.LogManager; +import java.util.logging.Logger; +import java.util.logging.LoggingPermission; + +/** + * @test + * @bug 8033661 + * @summary tests that FileHandlers configured on abstract nodes in logging.properties + * will be closed on reset and reopened on updateConfiguration(). + * Test a complex reconfiguration where a logger with handlers + * suddenly appears in the hierarchy between a child logger and the + * root logger. + * @run main/othervm HandlersOnComplexResetUpdate UNSECURE + * @run main/othervm HandlersOnComplexResetUpdate SECURE + * @author danielfuchs + */ +public class HandlersOnComplexResetUpdate { + + /** + * We will test the handling of abstract logger nodes with file handlers in + * two configurations: + * UNSECURE: No security manager. + * SECURE: With the security manager present - and the required + * permissions granted. + */ + public static enum TestCase { + UNSECURE, SECURE; + public void run(List properties) throws Exception { + System.out.println("Running test case: " + name()); + Configure.setUp(this, properties.get(0)); + test(this.name(), properties); + } + } + + + private static final String PREFIX = + "FileHandler-" + UUID.randomUUID() + ".log"; + private static final String userDir = System.getProperty("user.dir", "."); + private static final boolean userDirWritable = Files.isWritable(Paths.get(userDir)); + + private static final List properties; + static { + // The test will call reset() and updateConfiguration() with each of these + // properties in sequence. The child logger is not released between each + // configuration. What is interesting here is mostly what happens between + // props4 and props5: + // + // In step 4 (props4) the configuration defines a handler for the + // logger com.foo (the direct parent of com.foo.child - which is the + // logger we hold on to). + // + // In step 5 (props5) the configuration has nothing concerning + // 'com.foo', but the handler has been migrated to 'com'. + // Since there doesn't exist any logger for 'com' (the previous + // configuration didn't have any configuration for 'com'), then + // 'com' will not be found when we process the existing loggers named + // in the configuration. + // + // So if we didn't also process the existing loggers not named in the + // configuration (such as com.foo.child) then no logger for 'com' + // would be created, which means that com.foo.child would not be + // able to inherit its configuration for 'com' until someone explicitely + // creates a logger for 'com'. + // + // This test check that a logger for 'com' will be created because + // 'com.foo.child' still exists when updateConfiguration() is called. + + Properties props1 = new Properties(); + props1.setProperty("test.name", "parent logger with handler"); + props1.setProperty(FileHandler.class.getName() + ".pattern", PREFIX); + props1.setProperty(FileHandler.class.getName() + ".limit", String.valueOf(Integer.MAX_VALUE)); + props1.setProperty(FileHandler.class.getName() + ".level", "ALL"); + props1.setProperty(FileHandler.class.getName() + ".formatter", "java.util.logging.SimpleFormatter"); + props1.setProperty("com.foo.handlers", FileHandler.class.getName()); + props1.setProperty("test.checkHandlersOnParent", "true"); + props1.setProperty("test.checkHandlersOn", "com.foo"); + props1.setProperty("com.bar.level", "FINEST"); + + Properties props2 = new Properties(); + props2.setProperty("java.util.logging.LogManager.reconfigureHandlers", "true"); + props2.setProperty(FileHandler.class.getName() + ".pattern", PREFIX); + props2.setProperty(FileHandler.class.getName() + ".limit", String.valueOf(Integer.MAX_VALUE)); + props2.setProperty(FileHandler.class.getName() + ".level", "ALL"); + props2.setProperty(FileHandler.class.getName() + ".formatter", "java.util.logging.SimpleFormatter"); + props2.setProperty("com.foo.handlers", FileHandler.class.getName()); + props2.setProperty("test.checkHandlersOnParent", "true"); + props2.setProperty("test.checkHandlersOn", "com.foo"); + props2.setProperty("com.bar.level", "FINEST"); + + Properties props3 = new Properties(); + props3.setProperty("test.name", "parent logger with handler"); + props3.setProperty(FileHandler.class.getName() + ".pattern", PREFIX); + props3.setProperty(FileHandler.class.getName() + ".limit", String.valueOf(Integer.MAX_VALUE)); + props3.setProperty(FileHandler.class.getName() + ".level", "ALL"); + props3.setProperty(FileHandler.class.getName() + ".formatter", "java.util.logging.SimpleFormatter"); + props3.setProperty("com.foo.handlers", FileHandler.class.getName()); + props3.setProperty("test.checkHandlersOnParent", "true"); + props3.setProperty("test.checkHandlersOn", "com.foo"); + props3.setProperty("com.bar.level", "FINEST"); + + Properties props4 = new Properties(); + props4.setProperty("java.util.logging.LogManager.reconfigureHandlers", "true"); + props4.setProperty(FileHandler.class.getName() + ".pattern", PREFIX); + props4.setProperty(FileHandler.class.getName() + ".limit", String.valueOf(Integer.MAX_VALUE)); + props4.setProperty(FileHandler.class.getName() + ".level", "ALL"); + props4.setProperty(FileHandler.class.getName() + ".formatter", "java.util.logging.SimpleFormatter"); + props4.setProperty("test.checkHandlersOnParent", "true"); + props4.setProperty("test.checkHandlersOn", "com.foo"); + props4.setProperty("com.foo.handlers", FileHandler.class.getName()); + + Properties props5 = new Properties(); + props5.setProperty("test.name", "parent logger with handler"); + props5.setProperty(FileHandler.class.getName() + ".pattern", PREFIX); + props5.setProperty(FileHandler.class.getName() + ".limit", String.valueOf(Integer.MAX_VALUE)); + props5.setProperty(FileHandler.class.getName() + ".level", "ALL"); + props5.setProperty(FileHandler.class.getName() + ".formatter", "java.util.logging.SimpleFormatter"); + props5.setProperty("test.checkHandlersOnParent", "false"); + props5.setProperty("test.checkHandlersOn", "com"); + props5.setProperty("com.handlers", FileHandler.class.getName()); + + properties = Collections.unmodifiableList(Arrays.asList( + props1, props2, props3, props4, props5)); + } + + /** + * This is the main test method. The rest is infrastructure. + * Creates a child of the 'com.foo' logger (com.foo.child) and holds on to + * it. + *

          + * Then applies all given configurations in sequence and verifies assumptions + * about the handlers that com.foo should have, or not have. + * In the last configuration (props5) it also verifies that the + * logger 'com' has been created and has now the expected handler. + *

          + * Finally releases the child logger after all configurations have been + * applied. + * + * @param name + * @param properties + * @throws Exception + */ + static void test(String name, List properties) + throws Exception { + + System.out.println("\nTesting: " + name); + if (!userDirWritable) { + throw new RuntimeException("Not writable: "+userDir); + } + + // Then create a child of the com.foo logger. + Logger fooChild = Logger.getLogger("com.foo.child"); + fooChild.info("hello world"); + Logger barChild = Logger.getLogger("com.bar.child"); + barChild.info("hello world"); + + ReferenceQueue queue = new ReferenceQueue(); + WeakReference fooRef = new WeakReference<>(Logger.getLogger("com.foo"), queue); + if (fooRef.get() != fooChild.getParent()) { + throw new RuntimeException("Unexpected parent logger: " + + fooChild.getParent() +"\n\texpected: " + fooRef.get()); + } + WeakReference barRef = new WeakReference<>(Logger.getLogger("com.bar"), queue); + if (barRef.get() != barChild.getParent()) { + throw new RuntimeException("Unexpected parent logger: " + + barChild.getParent() +"\n\texpected: " + barRef.get()); + } + Reference ref2; + int max = 3; + barChild = null; + while ((ref2 = queue.poll()) == null) { + System.gc(); + Thread.sleep(100); + if (--max == 0) break; + } + + Throwable failed = null; + try { + if (ref2 != null) { + String refName = ref2 == fooRef ? "fooRef" : ref2 == barRef ? "barRef" : "unknown"; + if (ref2 != barRef) { + throw new RuntimeException("Unexpected logger reference cleared: " + refName); + } else { + System.out.println("Reference " + refName + " cleared as expected"); + } + } else if (ref2 == null) { + throw new RuntimeException("Expected 'barRef' to be cleared"); + } + // Now lets try to reset, check that ref2 has no handlers, and + // attempt to configure again. + Properties previousProps = properties.get(0); + int expectedHandlersCount = 1; + boolean checkHandlersOnParent = Boolean.parseBoolean( + previousProps.getProperty("test.checkHandlersOnParent", "true")); + String checkHandlersOn = previousProps.getProperty("test.checkHandlersOn", null); + for (int i=1; i LogManager.getLogManager().reset()); + assertEquals(0, fooChild.getParent().getHandlers().length, "fooChild.getParent().getHandlers().length"); + if (checkHandlersOn != null) { + Logger loggerWithHandlers = LogManager.getLogManager().getLogger(checkHandlersOn); + if (loggerWithHandlers == null) { + throw new RuntimeException("Logger with handlers not found: " + checkHandlersOn); + } + assertEquals(0, loggerWithHandlers.getHandlers().length, + checkHandlersOn + ".getHandlers().length"); + } + + if (i == 4) { + System.out.println("Last configuration..."); + } + // Read configuration + Configure.doPrivileged(() -> Configure.updateConfigurationWith(nextProps, false)); + + expectedHandlersCount = reconfigureHandlers ? 1 : 0; + checkHandlersOnParent = Boolean.parseBoolean( + nextProps.getProperty("test.checkHandlersOnParent", "true")); + checkHandlersOn = nextProps.getProperty("test.checkHandlersOn", null); + + if (checkHandlersOnParent) { + assertEquals(expectedHandlersCount, + fooChild.getParent().getHandlers().length, + "fooChild.getParent().getHandlers().length"); + } else { + assertEquals(0, + fooChild.getParent().getHandlers().length, + "fooChild.getParent().getHandlers().length"); + } + if (checkHandlersOn != null) { + Logger loggerWithHandlers = LogManager.getLogManager().getLogger(checkHandlersOn); + if (loggerWithHandlers == null) { + throw new RuntimeException("Logger with handlers not found: " + checkHandlersOn); + } + assertEquals(expectedHandlersCount, + loggerWithHandlers.getHandlers().length, + checkHandlersOn + ".getHandlers().length"); + } + } + } catch (Throwable t) { + failed = t; + } finally { + final Throwable suppressed = failed; + Configure.doPrivileged(() -> LogManager.getLogManager().reset()); + Configure.doPrivileged(() -> { + try { + StringBuilder builder = new StringBuilder(); + Files.list(Paths.get(userDir)) + .filter((f) -> f.toString().contains(PREFIX)) + .filter((f) -> f.toString().endsWith(".lck")) + .forEach((f) -> { + builder.append(f.toString()).append('\n'); + }); + if (!builder.toString().isEmpty()) { + throw new RuntimeException("Lock files not cleaned:\n" + + builder.toString()); + } + } catch(RuntimeException | Error x) { + if (suppressed != null) x.addSuppressed(suppressed); + throw x; + } catch(Exception x) { + if (suppressed != null) x.addSuppressed(suppressed); + throw new RuntimeException(x); + } + }); + fooChild = null; + System.out.println("Setting fooChild to: " + fooChild); + while ((ref2 = queue.poll()) == null) { + System.gc(); + Thread.sleep(1000); + } + if (ref2 != fooRef) { + throw new RuntimeException("Unexpected reference: " + + ref2 +"\n\texpected: " + fooRef); + } + if (ref2.get() != null) { + throw new RuntimeException("Referent not cleared: " + ref2.get()); + } + System.out.println("Got fooRef after reset(), fooChild is " + fooChild); + + } + if (failed != null) { + // should rarely happen... + throw new RuntimeException(failed); + } + + } + + public static void main(String... args) throws Exception { + + + if (args == null || args.length == 0) { + args = new String[] { + TestCase.UNSECURE.name(), + TestCase.SECURE.name(), + }; + } + + try { + for (String testName : args) { + TestCase test = TestCase.valueOf(testName); + test.run(properties); + } + } finally { + if (userDirWritable) { + Configure.doPrivileged(() -> { + // cleanup - delete files that have been created + try { + Files.list(Paths.get(userDir)) + .filter((f) -> f.toString().contains(PREFIX)) + .forEach((f) -> { + try { + System.out.println("deleting " + f); + Files.delete(f); + } catch(Throwable t) { + System.err.println("Failed to delete " + f + ": " + t); + } + }); + } catch(Throwable t) { + System.err.println("Cleanup failed to list files: " + t); + t.printStackTrace(); + } + }); + } + } + } + + static class Configure { + static Policy policy = null; + static final ThreadLocal allowAll = new ThreadLocal() { + @Override + protected AtomicBoolean initialValue() { + return new AtomicBoolean(false); + } + }; + static void setUp(TestCase test, Properties propertyFile) { + switch (test) { + case SECURE: + if (policy == null && System.getSecurityManager() != null) { + throw new IllegalStateException("SecurityManager already set"); + } else if (policy == null) { + policy = new SimplePolicy(TestCase.SECURE, allowAll); + Policy.setPolicy(policy); + System.setSecurityManager(new SecurityManager()); + } + if (System.getSecurityManager() == null) { + throw new IllegalStateException("No SecurityManager."); + } + if (policy == null) { + throw new IllegalStateException("policy not configured"); + } + break; + case UNSECURE: + if (System.getSecurityManager() != null) { + throw new IllegalStateException("SecurityManager already set"); + } + break; + default: + new InternalError("No such testcase: " + test); + } + doPrivileged(() -> { + updateConfigurationWith(propertyFile, false); + }); + } + + static void updateConfigurationWith(Properties propertyFile, boolean append) { + try { + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + propertyFile.store(bytes, propertyFile.getProperty("test.name")); + ByteArrayInputStream bais = new ByteArrayInputStream(bytes.toByteArray()); + Function> remapper = + append ? (x) -> ((o, n) -> n == null ? o : n) + : (x) -> ((o, n) -> n); + LogManager.getLogManager().updateConfiguration(bais, remapper); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + static void doPrivileged(Runnable run) { + final boolean old = allowAll.get().getAndSet(true); + try { + run.run(); + } finally { + allowAll.get().set(old); + } + } + static T callPrivileged(Callable call) throws Exception { + final boolean old = allowAll.get().getAndSet(true); + try { + return call.call(); + } finally { + allowAll.get().set(old); + } + } + } + + @FunctionalInterface + public static interface FileHandlerSupplier { + public FileHandler test() throws Exception; + } + + static final class TestAssertException extends RuntimeException { + TestAssertException(String msg) { + super(msg); + } + } + + private static void assertEquals(long expected, long received, String msg) { + if (expected != received) { + throw new TestAssertException("Unexpected result for " + msg + + ".\n\texpected: " + expected + + "\n\tactual: " + received); + } else { + System.out.println("Got expected " + msg + ": " + received); + } + } + + final static class PermissionsBuilder { + final Permissions perms; + public PermissionsBuilder() { + this(new Permissions()); + } + public PermissionsBuilder(Permissions perms) { + this.perms = perms; + } + public PermissionsBuilder add(Permission p) { + perms.add(p); + return this; + } + public PermissionsBuilder addAll(PermissionCollection col) { + if (col != null) { + for (Enumeration e = col.elements(); e.hasMoreElements(); ) { + perms.add(e.nextElement()); + } + } + return this; + } + public Permissions toPermissions() { + final PermissionsBuilder builder = new PermissionsBuilder(); + builder.addAll(perms); + return builder.perms; + } + } + + public static class SimplePolicy extends Policy { + + final Permissions permissions; + final Permissions allPermissions; + final ThreadLocal allowAll; // actually: this should be in a thread locale + public SimplePolicy(TestCase test, ThreadLocal allowAll) { + this.allowAll = allowAll; + permissions = new Permissions(); + permissions.add(new LoggingPermission("control", null)); + permissions.add(new FilePermission(PREFIX+".lck", "read,write,delete")); + permissions.add(new FilePermission(PREFIX, "read,write")); + + // these are used for configuring the test itself... + allPermissions = new Permissions(); + allPermissions.add(new java.security.AllPermission()); + + } + + @Override + public boolean implies(ProtectionDomain domain, Permission permission) { + if (allowAll.get().get()) return allPermissions.implies(permission); + return permissions.implies(permission); + } + + @Override + public PermissionCollection getPermissions(CodeSource codesource) { + return new PermissionsBuilder().addAll(allowAll.get().get() + ? allPermissions : permissions).toPermissions(); + } + + @Override + public PermissionCollection getPermissions(ProtectionDomain domain) { + return new PermissionsBuilder().addAll(allowAll.get().get() + ? allPermissions : permissions).toPermissions(); + } + } + +} diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/logging/LogManager/Configuration/updateConfiguration/HandlersOnComplexUpdate.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/util/logging/LogManager/Configuration/updateConfiguration/HandlersOnComplexUpdate.java Wed Jul 05 20:54:48 2017 +0200 @@ -0,0 +1,547 @@ +/* + * Copyright (c) 2015, 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. + */ +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FilePermission; +import java.io.IOException; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.CodeSource; +import java.security.Permission; +import java.security.PermissionCollection; +import java.security.Permissions; +import java.security.Policy; +import java.security.ProtectionDomain; +import java.util.Arrays; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; +import java.util.Properties; +import java.util.UUID; +import java.util.concurrent.Callable; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.logging.FileHandler; +import java.util.logging.LogManager; +import java.util.logging.Logger; +import java.util.logging.LoggingPermission; + +/** + * @test + * @bug 8033661 + * @summary tests that FileHandlers configured on abstract nodes in logging.properties + * will be properly closed and reopened on updateConfiguration(). + * Test a complex reconfiguration where a logger with handlers + * suddenly appears in the hierarchy between a child logger and the + * root logger. + * @run main/othervm HandlersOnComplexUpdate UNSECURE + * @run main/othervm HandlersOnComplexUpdate SECURE + * @author danielfuchs + */ +public class HandlersOnComplexUpdate { + + /** + * We will test the handling of abstract logger nodes with file handlers in + * two configurations: + * UNSECURE: No security manager. + * SECURE: With the security manager present - and the required + * permissions granted. + */ + public static enum TestCase { + UNSECURE, SECURE; + public void run(List properties) throws Exception { + System.out.println("Running test case: " + name()); + Configure.setUp(this, properties.get(0)); + test(this.name(), properties); + } + } + + + private static final String PREFIX = + "FileHandler-" + UUID.randomUUID() + ".log"; + private static final String userDir = System.getProperty("user.dir", "."); + private static final boolean userDirWritable = Files.isWritable(Paths.get(userDir)); + + private static final List properties; + static { + // The test will call updateConfiguration() with each of these + // properties in sequence. The child logger is not released between each + // configuration. What is interesting here is mostly what happens between + // props4 and props5: + // + // In step 4 (props4) the configuration defines a handler for the + // logger com.foo (the direct parent of com.foo.child - which is the + // logger we hold on to). + // + // In step 5 (props5) the configuration has nothing concerning + // 'com.foo', but the handler has been migrated to 'com'. + // Since there doesn't exist any logger for 'com' (the previous + // configuration didn't have any configuration for 'com'), then + // 'com' will not be found when we process the existing loggers named + // in the configuration. + // + // So if we didn't also process the existing loggers not named in the + // configuration (such as com.foo.child) then no logger for 'com' + // would be created, which means that com.foo.child would not be + // able to inherit its configuration for 'com' until someone explicitely + // creates a logger for 'com'. + // + // This test check that a logger for 'com' will be created because + // 'com.foo.child' still exists when updateConfiguration() is called. + + Properties props1 = new Properties(); + props1.setProperty("test.name", "parent logger with handler"); + props1.setProperty(FileHandler.class.getName() + ".pattern", PREFIX); + props1.setProperty(FileHandler.class.getName() + ".limit", String.valueOf(Integer.MAX_VALUE)); + props1.setProperty(FileHandler.class.getName() + ".level", "ALL"); + props1.setProperty(FileHandler.class.getName() + ".formatter", "java.util.logging.SimpleFormatter"); + props1.setProperty("com.foo.handlers", FileHandler.class.getName()); + props1.setProperty("test.checkHandlersOnParent", "true"); + props1.setProperty("test.checkHandlersOn", "com.foo"); + props1.setProperty("com.bar.level", "FINEST"); + + Properties props2 = new Properties(); + props2.setProperty("test.name", "parent logger with handler"); + props2.setProperty(FileHandler.class.getName() + ".pattern", PREFIX); + props2.setProperty(FileHandler.class.getName() + ".limit", String.valueOf(Integer.MAX_VALUE)); + props2.setProperty(FileHandler.class.getName() + ".level", "ALL"); + props2.setProperty(FileHandler.class.getName() + ".formatter", "java.util.logging.SimpleFormatter"); + props2.setProperty("com.foo.handlers", FileHandler.class.getName()); + props2.setProperty("test.checkHandlersOnParent", "true"); + props2.setProperty("test.checkHandlersOn", "com.foo"); + props2.setProperty("com.bar.level", "FINEST"); + + Properties props3 = new Properties(); + props3.setProperty("test.name", "parent logger with handler"); + props3.setProperty(FileHandler.class.getName() + ".pattern", PREFIX); + props3.setProperty(FileHandler.class.getName() + ".limit", String.valueOf(Integer.MAX_VALUE)); + props3.setProperty(FileHandler.class.getName() + ".level", "ALL"); + props3.setProperty(FileHandler.class.getName() + ".formatter", "java.util.logging.SimpleFormatter"); + props3.setProperty("com.foo.handlers", FileHandler.class.getName()); + props3.setProperty("test.checkHandlersOnParent", "true"); + props3.setProperty("test.checkHandlersOn", "com.foo"); + props3.setProperty("com.bar.level", "FINEST"); + + Properties props4 = new Properties(); + props4.setProperty("test.name", "parent logger with handler"); + props4.setProperty(FileHandler.class.getName() + ".pattern", PREFIX); + props4.setProperty(FileHandler.class.getName() + ".limit", String.valueOf(Integer.MAX_VALUE)); + props4.setProperty(FileHandler.class.getName() + ".level", "ALL"); + props4.setProperty(FileHandler.class.getName() + ".formatter", "java.util.logging.SimpleFormatter"); + props4.setProperty("test.checkHandlersOnParent", "true"); + props4.setProperty("test.checkHandlersOn", "com.foo"); + props4.setProperty("com.foo.handlers", FileHandler.class.getName()); + + Properties props5 = new Properties(); + props5.setProperty("test.name", "parent logger with handler"); + props5.setProperty(FileHandler.class.getName() + ".pattern", PREFIX); + props5.setProperty(FileHandler.class.getName() + ".limit", String.valueOf(Integer.MAX_VALUE)); + props5.setProperty(FileHandler.class.getName() + ".level", "ALL"); + props5.setProperty(FileHandler.class.getName() + ".formatter", "java.util.logging.SimpleFormatter"); + props5.setProperty("test.checkHandlersOnParent", "false"); + props5.setProperty("test.checkHandlersOn", "com"); + props5.setProperty("com.handlers", FileHandler.class.getName()); + + properties = Collections.unmodifiableList(Arrays.asList( + props1, props2, props3, props4, props5)); + } + + /** + * This is the main test method. The rest is infrastructure. + * Creates a child of the 'com.foo' logger (com.foo.child) and holds on to + * it. + *

          + * Then applies all given configurations in sequence and verifies assumptions + * about the handlers that com.foo should have, or not have. + * In the last configuration (props5) it also verifies that the + * logger 'com' has been created and has now the expected handler. + *

          + * Finally releases the child logger after all configurations have been + * applied. + * + * @param name + * @param properties + * @throws Exception + */ + static void test(String name, List properties) + throws Exception { + + System.out.println("\nTesting: " + name); + if (!userDirWritable) { + throw new RuntimeException("Not writable: "+userDir); + } + + // Then create a child of the com.foo logger. + Logger fooChild = Logger.getLogger("com.foo.child"); + fooChild.info("hello world"); + Logger barChild = Logger.getLogger("com.bar.child"); + barChild.info("hello world"); + + ReferenceQueue queue = new ReferenceQueue(); + WeakReference fooRef = new WeakReference<>(Logger.getLogger("com.foo"), queue); + if (fooRef.get() != fooChild.getParent()) { + throw new RuntimeException("Unexpected parent logger: " + + fooChild.getParent() +"\n\texpected: " + fooRef.get()); + } + WeakReference barRef = new WeakReference<>(Logger.getLogger("com.bar"), queue); + if (barRef.get() != barChild.getParent()) { + throw new RuntimeException("Unexpected parent logger: " + + barChild.getParent() +"\n\texpected: " + barRef.get()); + } + Reference ref2; + int max = 3; + barChild = null; + while ((ref2 = queue.poll()) == null) { + System.gc(); + Thread.sleep(100); + if (--max == 0) break; + } + + Throwable failed = null; + try { + if (ref2 != null) { + String refName = ref2 == fooRef ? "fooRef" : ref2 == barRef ? "barRef" : "unknown"; + if (ref2 != barRef) { + throw new RuntimeException("Unexpected logger reference cleared: " + refName); + } else { + System.out.println("Reference " + refName + " cleared as expected"); + } + } else if (ref2 == null) { + throw new RuntimeException("Expected 'barRef' to be cleared"); + } + // Now lets try to check handlers, and + // attempt to update the configuration again. + Properties previousProps = properties.get(0); + int expectedHandlersCount = 1; + boolean checkHandlersOnParent = Boolean.parseBoolean( + previousProps.getProperty("test.checkHandlersOnParent", "true")); + String checkHandlersOn = previousProps.getProperty("test.checkHandlersOn", null); + for (int i=1; i Configure.updateConfigurationWith(nextProps, false)); + + expectedHandlersCount = reconfigureHandlers ? 1 : 0; + checkHandlersOnParent = Boolean.parseBoolean( + nextProps.getProperty("test.checkHandlersOnParent", "true")); + checkHandlersOn = nextProps.getProperty("test.checkHandlersOn", null); + + if (checkHandlersOnParent) { + assertEquals(expectedHandlersCount, + fooChild.getParent().getHandlers().length, + "fooChild.getParent().getHandlers().length"); + } else { + assertEquals(0, + fooChild.getParent().getHandlers().length, + "fooChild.getParent().getHandlers().length"); + } + if (checkHandlersOn != null) { + Logger loggerWithHandlers = LogManager.getLogManager().getLogger(checkHandlersOn); + if (loggerWithHandlers == null) { + throw new RuntimeException("Logger with handlers not found: " + checkHandlersOn); + } + assertEquals(expectedHandlersCount, + loggerWithHandlers.getHandlers().length, + checkHandlersOn + ".getHandlers().length"); + } + } + } catch (Throwable t) { + failed = t; + } finally { + final Throwable suppressed = failed; + Configure.doPrivileged(() -> LogManager.getLogManager().reset()); + Configure.doPrivileged(() -> { + try { + StringBuilder builder = new StringBuilder(); + Files.list(Paths.get(userDir)) + .filter((f) -> f.toString().contains(PREFIX)) + .filter((f) -> f.toString().endsWith(".lck")) + .forEach((f) -> { + builder.append(f.toString()).append('\n'); + }); + if (!builder.toString().isEmpty()) { + throw new RuntimeException("Lock files not cleaned:\n" + + builder.toString()); + } + } catch(RuntimeException | Error x) { + if (suppressed != null) x.addSuppressed(suppressed); + throw x; + } catch(Exception x) { + if (suppressed != null) x.addSuppressed(suppressed); + throw new RuntimeException(x); + } + }); + fooChild = null; + System.out.println("Setting fooChild to: " + fooChild); + while ((ref2 = queue.poll()) == null) { + System.gc(); + Thread.sleep(1000); + } + if (ref2 != fooRef) { + throw new RuntimeException("Unexpected reference: " + + ref2 +"\n\texpected: " + fooRef); + } + if (ref2.get() != null) { + throw new RuntimeException("Referent not cleared: " + ref2.get()); + } + System.out.println("Got fooRef after reset(), fooChild is " + fooChild); + + } + if (failed != null) { + // should rarely happen... + throw new RuntimeException(failed); + } + + } + + public static void main(String... args) throws Exception { + + + if (args == null || args.length == 0) { + args = new String[] { + TestCase.UNSECURE.name(), + TestCase.SECURE.name(), + }; + } + + try { + for (String testName : args) { + TestCase test = TestCase.valueOf(testName); + test.run(properties); + } + } finally { + if (userDirWritable) { + Configure.doPrivileged(() -> { + // cleanup - delete files that have been created + try { + Files.list(Paths.get(userDir)) + .filter((f) -> f.toString().contains(PREFIX)) + .forEach((f) -> { + try { + System.out.println("deleting " + f); + Files.delete(f); + } catch(Throwable t) { + System.err.println("Failed to delete " + f + ": " + t); + } + }); + } catch(Throwable t) { + System.err.println("Cleanup failed to list files: " + t); + t.printStackTrace(); + } + }); + } + } + } + + static class Configure { + static Policy policy = null; + static final ThreadLocal allowAll = new ThreadLocal() { + @Override + protected AtomicBoolean initialValue() { + return new AtomicBoolean(false); + } + }; + static void setUp(TestCase test, Properties propertyFile) { + switch (test) { + case SECURE: + if (policy == null && System.getSecurityManager() != null) { + throw new IllegalStateException("SecurityManager already set"); + } else if (policy == null) { + policy = new SimplePolicy(TestCase.SECURE, allowAll); + Policy.setPolicy(policy); + System.setSecurityManager(new SecurityManager()); + } + if (System.getSecurityManager() == null) { + throw new IllegalStateException("No SecurityManager."); + } + if (policy == null) { + throw new IllegalStateException("policy not configured"); + } + break; + case UNSECURE: + if (System.getSecurityManager() != null) { + throw new IllegalStateException("SecurityManager already set"); + } + break; + default: + new InternalError("No such testcase: " + test); + } + doPrivileged(() -> { + configureWith(propertyFile); + }); + } + + static void configureWith(Properties propertyFile) { + try { + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + propertyFile.store(bytes, propertyFile.getProperty("test.name")); + ByteArrayInputStream bais = new ByteArrayInputStream(bytes.toByteArray()); + LogManager.getLogManager().readConfiguration(bais); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + static void updateConfigurationWith(Properties propertyFile, boolean append) { + try { + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + propertyFile.store(bytes, propertyFile.getProperty("test.name")); + ByteArrayInputStream bais = new ByteArrayInputStream(bytes.toByteArray()); + Function> remapper = + append ? (x) -> ((o, n) -> n == null ? o : n) + : (x) -> ((o, n) -> n); + LogManager.getLogManager().updateConfiguration(bais, remapper); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + static void doPrivileged(Runnable run) { + final boolean old = allowAll.get().getAndSet(true); + try { + run.run(); + } finally { + allowAll.get().set(old); + } + } + static T callPrivileged(Callable call) throws Exception { + final boolean old = allowAll.get().getAndSet(true); + try { + return call.call(); + } finally { + allowAll.get().set(old); + } + } + } + + @FunctionalInterface + public static interface FileHandlerSupplier { + public FileHandler test() throws Exception; + } + + static final class TestAssertException extends RuntimeException { + TestAssertException(String msg) { + super(msg); + } + } + + private static void assertEquals(long expected, long received, String msg) { + if (expected != received) { + throw new TestAssertException("Unexpected result for " + msg + + ".\n\texpected: " + expected + + "\n\tactual: " + received); + } else { + System.out.println("Got expected " + msg + ": " + received); + } + } + + final static class PermissionsBuilder { + final Permissions perms; + public PermissionsBuilder() { + this(new Permissions()); + } + public PermissionsBuilder(Permissions perms) { + this.perms = perms; + } + public PermissionsBuilder add(Permission p) { + perms.add(p); + return this; + } + public PermissionsBuilder addAll(PermissionCollection col) { + if (col != null) { + for (Enumeration e = col.elements(); e.hasMoreElements(); ) { + perms.add(e.nextElement()); + } + } + return this; + } + public Permissions toPermissions() { + final PermissionsBuilder builder = new PermissionsBuilder(); + builder.addAll(perms); + return builder.perms; + } + } + + public static class SimplePolicy extends Policy { + + final Permissions permissions; + final Permissions allPermissions; + final ThreadLocal allowAll; // actually: this should be in a thread locale + public SimplePolicy(TestCase test, ThreadLocal allowAll) { + this.allowAll = allowAll; + permissions = new Permissions(); + permissions.add(new LoggingPermission("control", null)); + permissions.add(new FilePermission(PREFIX+".lck", "read,write,delete")); + permissions.add(new FilePermission(PREFIX, "read,write")); + + // these are used for configuring the test itself... + allPermissions = new Permissions(); + allPermissions.add(new java.security.AllPermission()); + + } + + @Override + public boolean implies(ProtectionDomain domain, Permission permission) { + if (allowAll.get().get()) return allPermissions.implies(permission); + return permissions.implies(permission); + } + + @Override + public PermissionCollection getPermissions(CodeSource codesource) { + return new PermissionsBuilder().addAll(allowAll.get().get() + ? allPermissions : permissions).toPermissions(); + } + + @Override + public PermissionCollection getPermissions(ProtectionDomain domain) { + return new PermissionsBuilder().addAll(allowAll.get().get() + ? allPermissions : permissions).toPermissions(); + } + } + +} diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/logging/LogManager/Configuration/updateConfiguration/SimpleUpdateConfigWithInputStreamTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/util/logging/LogManager/Configuration/updateConfiguration/SimpleUpdateConfigWithInputStreamTest.java Wed Jul 05 20:54:48 2017 +0200 @@ -0,0 +1,685 @@ +/* + * Copyright (c) 2015, 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. + */ +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.CodeSource; +import java.security.Permission; +import java.security.PermissionCollection; +import java.security.Permissions; +import java.security.Policy; +import java.security.ProtectionDomain; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.Objects; +import java.util.Properties; +import java.util.concurrent.Callable; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.LogManager; +import java.util.logging.LogRecord; +import java.util.logging.Logger; +import java.util.logging.LoggingPermission; + +/** + * @test + * @bug 8033661 + * @summary tests LogManager.updateConfiguration(InputStream, Function) method + * @run main/othervm SimpleUpdateConfigWithInputStreamTest UNSECURE + * @run main/othervm SimpleUpdateConfigWithInputStreamTest SECURE + * @author danielfuchs + */ +public class SimpleUpdateConfigWithInputStreamTest { + + /** + * We will test updateConfiguration in + * two configurations: + * UNSECURE: No security manager. + * SECURE: With the security manager present - and the required + * permissions granted. + */ + public static enum TestCase { + UNSECURE, SECURE; + public void execute(Runnable run) { + System.out.println("Running test case: " + name()); + try { + Configure.setUp(this); + Configure.doPrivileged(run, SimplePolicy.allowControl); + } finally { + Configure.doPrivileged(() -> { + try { + setSystemProperty("java.util.logging.config.file", null); + LogManager.getLogManager().readConfiguration(); + System.gc(); + } catch (Exception x) { + throw new RuntimeException(x); + } + }, SimplePolicy.allowAll); + } + } + } + + public static class MyHandler extends Handler { + static final AtomicLong seq = new AtomicLong(); + long count = seq.incrementAndGet(); + + @Override + public void publish(LogRecord record) { + } + + @Override + public void flush() { + } + + @Override + public void close() throws SecurityException { + } + + @Override + public String toString() { + return super.toString() + "("+count+")"; + } + + } + + static String storePropertyToFile(String name, Properties props) + throws Exception { + return Configure.callPrivileged(() -> { + String scratch = System.getProperty("user.dir", "."); + Path p = Paths.get(scratch, name); + try (FileOutputStream fos = new FileOutputStream(p.toFile())) { + props.store(fos, name); + } + return p.toString(); + }, SimplePolicy.allowAll); + } + + static void setSystemProperty(String name, String value) + throws Exception { + Configure.doPrivileged(() -> { + if (value == null) + System.clearProperty(name); + else + System.setProperty(name, value); + }, SimplePolicy.allowAll); + } + + static String trim(String value) { + return value == null ? null : value.trim(); + } + + + /** + * Tests one of the configuration defined above. + *

          + * This is the main test method (the rest is infrastructure). + */ + static void testUpdateConfiguration() { + try { + // manager initialized with default configuration. + LogManager manager = LogManager.getLogManager(); + + // Test default configuration. It should not have + // any value for "com.foo.level" and "com.foo.handlers" + assertEquals(null, manager.getProperty("com.foo.level"), + "com.foo.level in default configuration"); + assertEquals(null, manager.getProperty("com.foo.handlers"), + "com.foo.handlers in default configuration"); + + // Create a logging configuration file that contains + // com.foo.level=FINEST + // and set "java.util.logging.config.file" to this file. + Properties props = new Properties(); + props.setProperty("com.foo.level", "FINEST"); + + // Update configuration with props + // then test that the new configuration has + // com.foo.level=FINEST + // and nothing for com.foo.handlers + Configure.updateConfigurationWith(props, null); + assertEquals("FINEST", manager.getProperty("com.foo.level"), + "com.foo.level in " + props); + assertEquals(null, manager.getProperty("com.foo.handlers"), + "com.foo.handlers in " + props); + + // call updateConfiguration with an empty stream. + // check that the new configuration no longer has + // any value for com.foo.level, and still no value + // for com.foo.handlers + Configure.updateConfigurationWith(new Properties(), null); + assertEquals(null, manager.getProperty("com.foo.level"), + "com.foo.level in default configuration"); + assertEquals(null, manager.getProperty("com.foo.handlers"), + "com.foo.handlers in default configuration"); + + // creates the com.foo logger, check it has + // the default config: no level, and no handlers + final Logger logger = Logger.getLogger("com.foo"); + assertEquals(null, logger.getLevel(), + "Logger.getLogger(\"com.foo\").getLevel()"); + assertDeepEquals(new Handler[0], logger.getHandlers(), + "Logger.getLogger(\"com.foo\").getHandlers()"); + + // call updateConfiguration with 'props' + // check that the configuration has + // com.foo.level=FINEST + // and nothing for com.foo.handlers + // check that the logger has now a FINEST level and still + // no handlers + Configure.updateConfigurationWith(props, null); + assertEquals("FINEST", manager.getProperty("com.foo.level"), + "com.foo.level in " + props); + assertEquals(Level.FINEST, logger.getLevel(), + "Logger.getLogger(\"com.foo\").getLevel()"); + assertDeepEquals(new Handler[0], logger.getHandlers(), + "Logger.getLogger(\"com.foo\").getHandlers()"); + assertEquals(null, manager.getProperty("com.foo.handlers"), + "com.foo.handlers in " + props); + + // Calls updateConfiguration with a lambda whose effect should + // be to set the FINER level on the "com.foo" logger. + // Check that the new configuration has + // com.foo.level=FINER + // and nothing for com.foo.handlers + // check that the logger has now a FINER level and still + // no handlers + Configure.updateConfigurationWith(props, + (k) -> ("com.foo.level".equals(k) ? (o, n) -> "FINER" : (o, n) -> n)); + assertEquals("FINER", manager.getProperty("com.foo.level"), + "com.foo.level set to FINER by updateConfiguration"); + assertEquals(Level.FINER, logger.getLevel(), + "Logger.getLogger(\"com.foo\").getLevel()"); + assertDeepEquals(new Handler[0], logger.getHandlers(), + "Logger.getLogger(\"com.foo\").getHandlers()"); + assertEquals(null, manager.getProperty("com.foo.handlers"), + "com.foo.handlers in " + props); + + // Calls updateConfiguration with a lambda whose effect is a noop. + // This should not change the configuration, so + // check that the new configuration still has + // com.foo.level=FINER + // and nothing for com.foo.handlers + // check that the logger still has FINER level and still + // no handlers + Configure.updateConfigurationWith(props, + (k) -> ((o, n) -> o)); + assertEquals("FINER", manager.getProperty("com.foo.level"), + "com.foo.level preserved by updateConfiguration"); + assertEquals(Level.FINER, logger.getLevel(), + "Logger.getLogger(\"com.foo\").getLevel()"); + assertDeepEquals(new Handler[0], logger.getHandlers(), + "Logger.getLogger(\"com.foo\").getHandlers()"); + assertEquals(null, manager.getProperty("com.foo.handlers"), + "com.foo.handlers in " + props); + + // Calls updateConfiguration with a lambda whose effect is to + // take all values from the new configuration. + // This should update the configuration to what is in props, so + // check that the new configuration has + // com.foo.level=FINEST + // and nothing for com.foo.handlers + // check that the logger now has FINEST level and still + // no handlers + Configure.updateConfigurationWith(props, + (k) -> ((o, n) -> n)); + assertEquals("FINEST", manager.getProperty("com.foo.level"), + "com.foo.level updated by updateConfiguration"); + assertEquals(Level.FINEST, logger.getLevel(), + "Logger.getLogger(\"com.foo\").getLevel()"); + assertDeepEquals(new Handler[0], logger.getHandlers(), + "Logger.getLogger(\"com.foo\").getHandlers()"); + assertEquals(null, manager.getProperty("com.foo.handlers"), + "com.foo.handlers in " + props); + + // now set a handler on the com.foo logger. + MyHandler h = new MyHandler(); + logger.addHandler(h); + assertDeepEquals(new Handler[] {h}, logger.getHandlers(), + "Logger.getLogger(\"com.foo\").getHandlers()"); + + // Calls updateConfiguration with a lambda whose effect should + // be to set the FINER level on the "com.foo" logger, and + // take the value from props for everything else. + // Check that the new configuration has + // com.foo.level=FINER + // and nothing for com.foo.handlers + // check that the logger has now a FINER level, but that its + // handlers are still present and have not been reset + // since neither the old nor new configuration defined them. + Configure.updateConfigurationWith(props, + (k) -> ("com.foo.level".equals(k) ? (o, n) -> "FINER" : (o, n) -> n)); + assertEquals("FINER", manager.getProperty("com.foo.level"), + "com.foo.level set to FINER by updateConfiguration"); + assertEquals(Level.FINER, logger.getLevel(), + "Logger.getLogger(\"com.foo\").getLevel()"); + assertDeepEquals(new Handler[] {h}, logger.getHandlers(), + "Logger.getLogger(\"com.foo\").getHandlers()"); + assertEquals(null, manager.getProperty("com.foo.handlers"), + "com.foo.handlers in " + props); + + // now add some configuration for com.foo.handlers + props.setProperty("com.foo.handlers", MyHandler.class.getName()); + + // we didn't call updateConfiguration, so just changing the + // content of props should have had no effect. + assertEquals("FINER", manager.getProperty("com.foo.level"), + "com.foo.level set to FINER by updateConfiguration"); + assertEquals(Level.FINER, logger.getLevel(), + "Logger.getLogger(\"com.foo\").getLevel()"); + assertEquals(null, + manager.getProperty("com.foo.handlers"), + "manager.getProperty(\"com.foo.handlers\")"); + assertDeepEquals(new Handler[] {h}, logger.getHandlers(), + "Logger.getLogger(\"com.foo\").getHandlers()"); + + // Calls updateConfiguration with a lambda whose effect is a noop. + // This should not change the current configuration, so + // check that the new configuration still has + // com.foo.level=FINER + // and nothing for com.foo.handlers + // check that the logger still has FINER level and still + // has its handlers and that they haven't been reset. + Configure.updateConfigurationWith(props, (k) -> ((o, n) -> o)); + assertEquals("FINER", manager.getProperty("com.foo.level"), + "com.foo.level set to FINER by updateConfiguration"); + assertEquals(Level.FINER, logger.getLevel(), + "Logger.getLogger(\"com.foo\").getLevel()"); + assertEquals(null, + manager.getProperty("com.foo.handlers"), + "manager.getProperty(\"com.foo.handlers\")"); + assertDeepEquals(new Handler[] {h}, logger.getHandlers(), + "Logger.getLogger(\"com.foo\").getHandlers()"); + + // Calls updateConfiguration with a lambda whose effect is to + // take all values from the new configuration. + // This should update the configuration to what is in props, so + // check that the new configuration has + // com.foo.level=FINEST + // com.foo.handlers=SimpleUpdateConfigWithInputStreamTest$MyHandler + // check that the logger now has FINEST level + // and a new handler instance, since the old config + // had no handlers for com.foo and the new config has one. + Configure.updateConfigurationWith(props, (k) -> ((o, n) -> n)); + assertEquals("FINEST", manager.getProperty("com.foo.level"), + "com.foo.level updated by updateConfiguration"); + assertEquals(Level.FINEST, logger.getLevel(), + "Logger.getLogger(\"com.foo\").getLevel()"); + assertEquals(MyHandler.class.getName(), + manager.getProperty("com.foo.handlers"), + "manager.getProperty(\"com.foo.handlers\")"); + Handler[] loggerHandlers = logger.getHandlers().clone(); + assertEquals(1, loggerHandlers.length, + "Logger.getLogger(\"com.foo\").getHandlers().length"); + assertEquals(MyHandler.class, loggerHandlers[0].getClass(), + "Logger.getLogger(\"com.foo\").getHandlers()[0].getClass()"); + assertEquals(h.count + 1, ((MyHandler)logger.getHandlers()[0]).count, + "Logger.getLogger(\"com.foo\").getHandlers()[0].count"); + + // Calls updateConfiguration with a lambda whose effect is a noop. + // This should not change the current configuration, so + // check that the new configuration still has + // com.foo.level=FINEST + // com.foo.handlers=SimpleUpdateConfigWithInputStreamTest$MyHandler + // check that the logger still has FINEST level and still + // has its handlers and that they haven't been reset. + Configure.updateConfigurationWith(props, (k) -> ((o, n) -> o)); + assertDeepEquals(loggerHandlers, logger.getHandlers(), + "Logger.getLogger(\"com.foo\").getHandlers()"); + assertEquals("FINEST", manager.getProperty("com.foo.level"), + "com.foo.level updated by updateConfiguration"); + assertEquals(Level.FINEST, logger.getLevel(), + "Logger.getLogger(\"com.foo\").getLevel()"); + assertEquals(MyHandler.class.getName(), + manager.getProperty("com.foo.handlers"), + "manager.getProperty(\"com.foo.handlers\")"); + + // Calls updateConfiguration with a lambda whose effect is to + // take all values from the new configuration. + // Because the content of the props hasn't changed, then + // it should also be a noop. + // check that the new configuration still has + // com.foo.level=FINEST + // com.foo.handlers=SimpleUpdateConfigWithInputStreamTest$MyHandler + // check that the logger still has FINEST level and still + // has its handlers and that they haven't been reset. + Configure.updateConfigurationWith(props, (k) -> ((o, n) -> n)); + assertDeepEquals(loggerHandlers, logger.getHandlers(), + "Logger.getLogger(\"com.foo\").getHandlers()"); + assertEquals("FINEST", manager.getProperty("com.foo.level"), + "com.foo.level updated by updateConfiguration"); + assertEquals(Level.FINEST, logger.getLevel(), + "Logger.getLogger(\"com.foo\").getLevel()"); + assertEquals(MyHandler.class.getName(), + manager.getProperty("com.foo.handlers"), + "manager.getProperty(\"com.foo.handlers\")"); + + // Calls updateConfiguration with a null lambda, whose effect is to + // take all values from the new configuration. + // Because the content of the props hasn't changed, then + // it should also be a noop. + // check that the new configuration still has + // com.foo.level=FINEST + // com.foo.handlers=SimpleUpdateConfigWithInputStreamTest$MyHandler + // check that the logger still has FINEST level and still + // has its handlers and that they haven't been reset. + Configure.updateConfigurationWith(props, (k) -> ((o, n) -> n)); + assertDeepEquals(loggerHandlers, logger.getHandlers(), + "Logger.getLogger(\"com.foo\").getHandlers()"); + assertEquals("FINEST", manager.getProperty("com.foo.level"), + "com.foo.level updated by updateConfiguration"); + assertEquals(Level.FINEST, logger.getLevel(), + "Logger.getLogger(\"com.foo\").getLevel()"); + assertEquals(MyHandler.class.getName(), + manager.getProperty("com.foo.handlers"), + "manager.getProperty(\"com.foo.handlers\")"); + + // now remove com.foo.handlers=SimpleUpdateConfigWithInputStreamTest$MyHandler + // from the configuration file. + props.remove("com.foo.handlers"); + + // Calls updateConfiguration with a lambda whose effect is a noop. + // This should not change the current configuration, so + // check that the new configuration still has + // com.foo.level=FINEST + // com.foo.handlers=SimpleUpdateConfigWithInputStreamTest$MyHandler + // check that the logger still has FINEST level and still + // has its handlers and that they haven't been reset. + Configure.updateConfigurationWith(props, (k) -> ((o, n) -> o)); + assertDeepEquals(loggerHandlers, logger.getHandlers(), + "Logger.getLogger(\"com.foo\").getHandlers()"); + assertEquals("FINEST", manager.getProperty("com.foo.level"), + "com.foo.level updated by updateConfiguration"); + assertEquals(Level.FINEST, logger.getLevel(), + "Logger.getLogger(\"com.foo\").getLevel()"); + assertEquals(MyHandler.class.getName(), + manager.getProperty("com.foo.handlers"), + "manager.getProperty(\"com.foo.handlers\")"); + + // Calls updateConfiguration with a lambda whose effect is to + // take all values from the new configuration. + // This should update the configuration to what is in props, so + // check that the new configuration has + // com.foo.level=FINEST + // and nothing for com.foo.handlers + // check that the logger still has FINEST level + // and no handlers, since the old config + // had an handler for com.foo and the new config doesn't. + Configure.updateConfigurationWith(props, (k) -> ((o, n) -> n)); + assertDeepEquals(new Handler[0], logger.getHandlers(), + "Logger.getLogger(\"com.foo\").getHandlers()"); + assertEquals("FINEST", manager.getProperty("com.foo.level"), + "com.foo.level updated by updateConfiguration"); + assertEquals(Level.FINEST, logger.getLevel(), + "Logger.getLogger(\"com.foo\").getLevel()"); + assertEquals(null, + manager.getProperty("com.foo.handlers"), + "manager.getProperty(\"com.foo.handlers\")"); + + + } catch (RuntimeException | Error r) { + throw r; + } catch (Exception x) { + throw new RuntimeException(x); + } + } + + public static void main(String[] args) throws Exception { + if (args == null || args.length == 0) { + args = new String[] { "UNSECURE", "SECURE" }; + } + for (String test : args) { + TestCase.valueOf(test).execute(SimpleUpdateConfigWithInputStreamTest::testUpdateConfiguration); + } + } + + static class Configure { + static Policy policy = null; + static void setUp(TestCase test) { + switch (test) { + case SECURE: + if (policy == null && System.getSecurityManager() != null) { + throw new IllegalStateException("SecurityManager already set"); + } else if (policy == null) { + policy = new SimplePolicy(TestCase.SECURE); + Policy.setPolicy(policy); + System.setSecurityManager(new SecurityManager()); + } + if (System.getSecurityManager() == null) { + throw new IllegalStateException("No SecurityManager."); + } + if (policy == null) { + throw new IllegalStateException("policy not configured"); + } + break; + case UNSECURE: + if (System.getSecurityManager() != null) { + throw new IllegalStateException("SecurityManager already set"); + } + break; + default: + throw new InternalError("No such testcase: " + test); + } + } + + static void updateConfigurationWith(Properties propertyFile, + Function> remapper) { + try { + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + propertyFile.store(bytes, propertyFile.getProperty("test.name")); + ByteArrayInputStream bais = new ByteArrayInputStream(bytes.toByteArray()); + LogManager.getLogManager().updateConfiguration(bais, remapper); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + static void doPrivileged(Runnable run, ThreadLocal granter) { + final boolean old = granter.get().getAndSet(true); + try { + run.run(); + } finally { + granter.get().set(old); + } + } + static T callPrivileged(Callable call, + ThreadLocal granter) throws Exception { + final boolean old = granter.get().getAndSet(true); + try { + return call.call(); + } finally { + granter.get().set(old); + } + } + } + + static final class TestAssertException extends RuntimeException { + TestAssertException(String msg) { + super(msg); + } + } + + private static void assertEquals(long expected, long received, String msg) { + if (expected != received) { + throw new TestAssertException("Unexpected result for " + msg + + ".\n\texpected: " + expected + + "\n\tactual: " + received); + } else { + System.out.println("Got expected " + msg + ": " + received); + } + } + + private static void assertEquals(String expected, String received, String msg) { + if (!Objects.equals(expected, received)) { + throw new TestAssertException("Unexpected result for " + msg + + ".\n\texpected: " + expected + + "\n\tactual: " + received); + } else { + System.out.println("Got expected " + msg + ": " + received); + } + } + + private static void assertEquals(Object expected, Object received, String msg) { + if (!Objects.equals(expected, received)) { + throw new TestAssertException("Unexpected result for " + msg + + ".\n\texpected: " + expected + + "\n\tactual: " + received); + } else { + System.out.println("Got expected " + msg + ": " + received); + } + } + + public static String deepToString(Object o) { + if (o == null) { + return "null"; + } else if (o.getClass().isArray()) { + String s; + if (o instanceof Object[]) + s = Arrays.deepToString((Object[]) o); + else if (o instanceof byte[]) + s = Arrays.toString((byte[]) o); + else if (o instanceof short[]) + s = Arrays.toString((short[]) o); + else if (o instanceof int[]) + s = Arrays.toString((int[]) o); + else if (o instanceof long[]) + s = Arrays.toString((long[]) o); + else if (o instanceof char[]) + s = Arrays.toString((char[]) o); + else if (o instanceof float[]) + s = Arrays.toString((float[]) o); + else if (o instanceof double[]) + s = Arrays.toString((double[]) o); + else if (o instanceof boolean[]) + s = Arrays.toString((boolean[]) o); + else + s = o.toString(); + return s; + } else { + return o.toString(); + } + } + + private static void assertDeepEquals(Object expected, Object received, String msg) { + if (!Objects.deepEquals(expected, received)) { + throw new TestAssertException("Unexpected result for " + msg + + ".\n\texpected: " + deepToString(expected) + + "\n\tactual: " + deepToString(received)); + } else { + System.out.println("Got expected " + msg + ": " + deepToString(received)); + } + } + + final static class PermissionsBuilder { + final Permissions perms; + public PermissionsBuilder() { + this(new Permissions()); + } + public PermissionsBuilder(Permissions perms) { + this.perms = perms; + } + public PermissionsBuilder add(Permission p) { + perms.add(p); + return this; + } + public PermissionsBuilder addAll(PermissionCollection col) { + if (col != null) { + for (Enumeration e = col.elements(); e.hasMoreElements(); ) { + perms.add(e.nextElement()); + } + } + return this; + } + public Permissions toPermissions() { + final PermissionsBuilder builder = new PermissionsBuilder(); + builder.addAll(perms); + return builder.perms; + } + } + + public static class SimplePolicy extends Policy { + + final Permissions basic; + final Permissions control; + final Permissions all; + public final static ThreadLocal allowAll = + new ThreadLocal() { + @Override + protected AtomicBoolean initialValue() { + return new AtomicBoolean(); + } + }; + public final static ThreadLocal allowControl = + new ThreadLocal() { + @Override + protected AtomicBoolean initialValue() { + return new AtomicBoolean(); + } + }; + public SimplePolicy(TestCase test) { + basic = new Permissions(); + control = new Permissions(); + control.add(new LoggingPermission("control", null)); + + // these are used for configuring the test itself... + all = new Permissions(); + all.add(new java.security.AllPermission()); + + } + + @Override + public boolean implies(ProtectionDomain domain, Permission permission) { + return getPermissions(domain).implies(permission); + } + + public PermissionCollection permissions() { + PermissionsBuilder builder = new PermissionsBuilder(); + if (allowAll.get().get()) { + builder.addAll(all); + } else { + builder.addAll(basic); + if (allowControl.get().get()) { + builder.addAll(control); + } + } + return builder.toPermissions(); + } + + @Override + public PermissionCollection getPermissions(CodeSource codesource) { + return permissions(); + } + + @Override + public PermissionCollection getPermissions(ProtectionDomain domain) { + return permissions(); + } + } + +} diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/logging/LogManager/Configuration/updateConfiguration/SimpleUpdateConfigurationTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/util/logging/LogManager/Configuration/updateConfiguration/SimpleUpdateConfigurationTest.java Wed Jul 05 20:54:48 2017 +0200 @@ -0,0 +1,720 @@ +/* + * Copyright (c) 2015, 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. + */ +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FileOutputStream; +import java.io.FilePermission; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.CodeSource; +import java.security.Permission; +import java.security.PermissionCollection; +import java.security.Permissions; +import java.security.Policy; +import java.security.ProtectionDomain; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.Objects; +import java.util.Properties; +import java.util.PropertyPermission; +import java.util.concurrent.Callable; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.LogManager; +import java.util.logging.LogRecord; +import java.util.logging.Logger; +import java.util.logging.LoggingPermission; + +/** + * @test + * @bug 8033661 + * @summary tests LogManager.updateConfiguration(Function) method + * @run main/othervm SimpleUpdateConfigurationTest UNSECURE + * @run main/othervm SimpleUpdateConfigurationTest SECURE + * @author danielfuchs + */ +public class SimpleUpdateConfigurationTest { + + /** + * We will test updateConfiguration in + * two configurations: + * UNSECURE: No security manager. + * SECURE: With the security manager present - and the required + * permissions granted. + */ + public static enum TestCase { + UNSECURE, SECURE; + public void execute(Runnable run) { + System.out.println("Running test case: " + name()); + try { + Configure.setUp(this); + Configure.doPrivileged(run, SimplePolicy.allowControl); + } finally { + Configure.doPrivileged(() -> { + try { + setSystemProperty("java.util.logging.config.file", null); + LogManager.getLogManager().readConfiguration(); + System.gc(); + } catch (Exception x) { + throw new RuntimeException(x); + } + }, SimplePolicy.allowAll); + } + } + } + + public static class MyHandler extends Handler { + static final AtomicLong seq = new AtomicLong(); + long count = seq.incrementAndGet(); + + @Override + public void publish(LogRecord record) { + } + + @Override + public void flush() { + } + + @Override + public void close() throws SecurityException { + } + + @Override + public String toString() { + return super.toString() + "("+count+")"; + } + + } + + static String storePropertyToFile(String name, Properties props) + throws Exception { + return Configure.callPrivileged(() -> { + String scratch = System.getProperty("user.dir", "."); + Path p = Paths.get(scratch, name); + try (FileOutputStream fos = new FileOutputStream(p.toFile())) { + props.store(fos, name); + } + return p.toString(); + }, SimplePolicy.allowAll); + } + + static void setSystemProperty(String name, String value) + throws Exception { + Configure.doPrivileged(() -> { + if (value == null) + System.clearProperty(name); + else + System.setProperty(name, value); + }, SimplePolicy.allowAll); + } + + static String trim(String value) { + return value == null ? null : value.trim(); + } + + + /** + * Tests one of the configuration defined above. + *

          + * This is the main test method (the rest is infrastructure). + */ + static void testUpdateConfiguration() { + String configFile = null; + try { + // manager initialized with default configuration. + LogManager manager = LogManager.getLogManager(); + + // Test default configuration. It should not have + // any value for "com.foo.level" and "com.foo.handlers" + assertEquals(null, manager.getProperty("com.foo.level"), + "com.foo.level in default configuration"); + assertEquals(null, manager.getProperty("com.foo.handlers"), + "com.foo.handlers in default configuration"); + + // Create a logging configuration file that contains + // com.foo.level=FINEST + // and set "java.util.logging.config.file" to this file. + Properties props = new Properties(); + props.setProperty("com.foo.level", "FINEST"); + configFile = storePropertyToFile("config1", props); + setSystemProperty("java.util.logging.config.file", configFile); + + // Update configuration with configFile + // then test that the new configuration has + // com.foo.level=FINEST + // and nothing for com.foo.handlers + manager.updateConfiguration(null); + assertEquals("FINEST", manager.getProperty("com.foo.level"), + "com.foo.level in " + configFile); + assertEquals(null, manager.getProperty("com.foo.handlers"), + "com.foo.handlers in " + configFile); + + // clear ("java.util.logging.config.file" system property, + // and call updateConfiguration again. + // check that the new configuration no longer has + // any value for com.foo.level, and still no value + // for com.foo.handlers + setSystemProperty("java.util.logging.config.file", null); + manager.updateConfiguration(null); + assertEquals(null, manager.getProperty("com.foo.level"), + "com.foo.level in default configuration"); + assertEquals(null, manager.getProperty("com.foo.handlers"), + "com.foo.handlers in default configuration"); + + // creates the com.foo logger, check it has + // the default config: no level, and no handlers + final Logger logger = Logger.getLogger("com.foo"); + assertEquals(null, logger.getLevel(), + "Logger.getLogger(\"com.foo\").getLevel()"); + assertDeepEquals(new Handler[0], logger.getHandlers(), + "Logger.getLogger(\"com.foo\").getHandlers()"); + + // set "java.util.logging.config.file" to configFile and + // call updateConfiguration. + // check that the configuration has + // com.foo.level=FINEST + // and nothing for com.foo.handlers + // check that the logger has now a FINEST level and still + // no handlers + setSystemProperty("java.util.logging.config.file", configFile); + manager.updateConfiguration(null); + assertEquals("FINEST", manager.getProperty("com.foo.level"), + "com.foo.level in " + configFile); + assertEquals(Level.FINEST, logger.getLevel(), + "Logger.getLogger(\"com.foo\").getLevel()"); + assertDeepEquals(new Handler[0], logger.getHandlers(), + "Logger.getLogger(\"com.foo\").getHandlers()"); + assertEquals(null, manager.getProperty("com.foo.handlers"), + "com.foo.handlers in " + configFile); + + // Calls updateConfiguration with a lambda whose effect should + // be to set the FINER level on the "com.foo" logger. + // Check that the new configuration has + // com.foo.level=FINER + // and nothing for com.foo.handlers + // check that the logger has now a FINER level and still + // no handlers + manager.updateConfiguration( + (k) -> ("com.foo.level".equals(k) ? (o, n) -> "FINER" : (o, n) -> n)); + assertEquals("FINER", manager.getProperty("com.foo.level"), + "com.foo.level set to FINER by updateConfiguration"); + assertEquals(Level.FINER, logger.getLevel(), + "Logger.getLogger(\"com.foo\").getLevel()"); + assertDeepEquals(new Handler[0], logger.getHandlers(), + "Logger.getLogger(\"com.foo\").getHandlers()"); + assertEquals(null, manager.getProperty("com.foo.handlers"), + "com.foo.handlers in " + configFile); + + // Calls updateConfiguration with a lambda whose effect is a noop. + // This should not change the configuration, so + // check that the new configuration still has + // com.foo.level=FINER + // and nothing for com.foo.handlers + // check that the logger still has FINER level and still + // no handlers + manager.updateConfiguration( + (k) -> ((o, n) -> o)); + assertEquals("FINER", manager.getProperty("com.foo.level"), + "com.foo.level preserved by updateConfiguration"); + assertEquals(Level.FINER, logger.getLevel(), + "Logger.getLogger(\"com.foo\").getLevel()"); + assertDeepEquals(new Handler[0], logger.getHandlers(), + "Logger.getLogger(\"com.foo\").getHandlers()"); + assertEquals(null, manager.getProperty("com.foo.handlers"), + "com.foo.handlers in " + configFile); + + // Calls updateConfiguration with a lambda whose effect is to + // take all values from the new configuration. + // This should update the configuration to what is in configFile, so + // check that the new configuration has + // com.foo.level=FINEST + // and nothing for com.foo.handlers + // check that the logger now has FINEST level and still + // no handlers + manager.updateConfiguration( + (k) -> ((o, n) -> n)); + assertEquals("FINEST", manager.getProperty("com.foo.level"), + "com.foo.level updated by updateConfiguration"); + assertEquals(Level.FINEST, logger.getLevel(), + "Logger.getLogger(\"com.foo\").getLevel()"); + assertDeepEquals(new Handler[0], logger.getHandlers(), + "Logger.getLogger(\"com.foo\").getHandlers()"); + assertEquals(null, manager.getProperty("com.foo.handlers"), + "com.foo.handlers in " + configFile); + + // now set a handler on the com.foo logger. + MyHandler h = new MyHandler(); + logger.addHandler(h); + assertDeepEquals(new Handler[] {h}, logger.getHandlers(), + "Logger.getLogger(\"com.foo\").getHandlers()"); + + // Calls updateConfiguration with a lambda whose effect should + // be to set the FINER level on the "com.foo" logger, and + // take the value from configFile for everything else. + // Check that the new configuration has + // com.foo.level=FINER + // and nothing for com.foo.handlers + // check that the logger has now a FINER level, but that its + // handlers are still present and have not been reset + // since neither the old nor new configuration defined them. + manager.updateConfiguration( + (k) -> ("com.foo.level".equals(k) ? (o, n) -> "FINER" : (o, n) -> n)); + assertEquals("FINER", manager.getProperty("com.foo.level"), + "com.foo.level set to FINER by updateConfiguration"); + assertEquals(Level.FINER, logger.getLevel(), + "Logger.getLogger(\"com.foo\").getLevel()"); + assertDeepEquals(new Handler[] {h}, logger.getHandlers(), + "Logger.getLogger(\"com.foo\").getHandlers()"); + assertEquals(null, manager.getProperty("com.foo.handlers"), + "com.foo.handlers in " + configFile); + + // now add some configuration for com.foo.handlers in the + // configuration file. + props.setProperty("com.foo.handlers", MyHandler.class.getName()); + storePropertyToFile("config1", props); + + // we didn't call updateConfiguration, so just changing the + // content of the file should have had no no effect yet. + assertEquals("FINER", manager.getProperty("com.foo.level"), + "com.foo.level set to FINER by updateConfiguration"); + assertEquals(Level.FINER, logger.getLevel(), + "Logger.getLogger(\"com.foo\").getLevel()"); + assertEquals(null, + manager.getProperty("com.foo.handlers"), + "manager.getProperty(\"com.foo.handlers\")"); + assertDeepEquals(new Handler[] {h}, logger.getHandlers(), + "Logger.getLogger(\"com.foo\").getHandlers()"); + + // Calls updateConfiguration with a lambda whose effect is a noop. + // This should not change the current configuration, so + // check that the new configuration still has + // com.foo.level=FINER + // and nothing for com.foo.handlers + // check that the logger still has FINER level and still + // has its handlers and that they haven't been reset. + manager.updateConfiguration((k) -> ((o, n) -> o)); + assertEquals("FINER", manager.getProperty("com.foo.level"), + "com.foo.level set to FINER by updateConfiguration"); + assertEquals(Level.FINER, logger.getLevel(), + "Logger.getLogger(\"com.foo\").getLevel()"); + assertEquals(null, + manager.getProperty("com.foo.handlers"), + "manager.getProperty(\"com.foo.handlers\")"); + assertDeepEquals(new Handler[] {h}, logger.getHandlers(), + "Logger.getLogger(\"com.foo\").getHandlers()"); + + // Calls updateConfiguration with a lambda whose effect is to + // take all values from the new configuration. + // This should update the configuration to what is in configFile, so + // check that the new configuration has + // com.foo.level=FINEST + // com.foo.handlers=SimpleUpdateConfigurationTest$MyHandler + // check that the logger now has FINEST level + // and a new handler instance, since the old config + // had no handlers for com.foo and the new config has one. + manager.updateConfiguration((k) -> ((o, n) -> n)); + assertEquals("FINEST", manager.getProperty("com.foo.level"), + "com.foo.level updated by updateConfiguration"); + assertEquals(Level.FINEST, logger.getLevel(), + "Logger.getLogger(\"com.foo\").getLevel()"); + assertEquals(MyHandler.class.getName(), + manager.getProperty("com.foo.handlers"), + "manager.getProperty(\"com.foo.handlers\")"); + Handler[] loggerHandlers = logger.getHandlers().clone(); + assertEquals(1, loggerHandlers.length, + "Logger.getLogger(\"com.foo\").getHandlers().length"); + assertEquals(MyHandler.class, loggerHandlers[0].getClass(), + "Logger.getLogger(\"com.foo\").getHandlers()[0].getClass()"); + assertEquals(h.count + 1, ((MyHandler)logger.getHandlers()[0]).count, + "Logger.getLogger(\"com.foo\").getHandlers()[0].count"); + + // Calls updateConfiguration with a lambda whose effect is a noop. + // This should not change the current configuration, so + // check that the new configuration still has + // com.foo.level=FINEST + // com.foo.handlers=SimpleUpdateConfigurationTest$MyHandler + // check that the logger still has FINEST level and still + // has its handlers and that they haven't been reset. + manager.updateConfiguration((k) -> ((o, n) -> o)); + assertDeepEquals(loggerHandlers, logger.getHandlers(), + "Logger.getLogger(\"com.foo\").getHandlers()"); + assertEquals("FINEST", manager.getProperty("com.foo.level"), + "com.foo.level updated by updateConfiguration"); + assertEquals(Level.FINEST, logger.getLevel(), + "Logger.getLogger(\"com.foo\").getLevel()"); + assertEquals(MyHandler.class.getName(), + manager.getProperty("com.foo.handlers"), + "manager.getProperty(\"com.foo.handlers\")"); + + // Calls updateConfiguration with a lambda whose effect is to + // take all values from the new configuration. + // Because the content of the configFile hasn't changed, then + // it should also be a noop. + // check that the new configuration still has + // com.foo.level=FINEST + // com.foo.handlers=SimpleUpdateConfigurationTest$MyHandler + // check that the logger still has FINEST level and still + // has its handlers and that they haven't been reset. + manager.updateConfiguration((k) -> ((o, n) -> n)); + assertDeepEquals(loggerHandlers, logger.getHandlers(), + "Logger.getLogger(\"com.foo\").getHandlers()"); + assertEquals("FINEST", manager.getProperty("com.foo.level"), + "com.foo.level updated by updateConfiguration"); + assertEquals(Level.FINEST, logger.getLevel(), + "Logger.getLogger(\"com.foo\").getLevel()"); + assertEquals(MyHandler.class.getName(), + manager.getProperty("com.foo.handlers"), + "manager.getProperty(\"com.foo.handlers\")"); + + // Calls updateConfiguration with a null lambda, whose effect is to + // take all values from the new configuration. + // Because the content of the configFile hasn't changed, then + // it should also be a noop. + // check that the new configuration still has + // com.foo.level=FINEST + // com.foo.handlers=SimpleUpdateConfigurationTest$MyHandler + // check that the logger still has FINEST level and still + // has its handlers and that they haven't been reset. + manager.updateConfiguration((k) -> ((o, n) -> n)); + assertDeepEquals(loggerHandlers, logger.getHandlers(), + "Logger.getLogger(\"com.foo\").getHandlers()"); + assertEquals("FINEST", manager.getProperty("com.foo.level"), + "com.foo.level updated by updateConfiguration"); + assertEquals(Level.FINEST, logger.getLevel(), + "Logger.getLogger(\"com.foo\").getLevel()"); + assertEquals(MyHandler.class.getName(), + manager.getProperty("com.foo.handlers"), + "manager.getProperty(\"com.foo.handlers\")"); + + // no remove com.foo.handlers=SimpleUpdateConfigurationTest$MyHandler + // from the configuration file. + props.remove("com.foo.handlers"); + storePropertyToFile("config1", props); + + // Calls updateConfiguration with a lambda whose effect is a noop. + // This should not change the current configuration, so + // check that the new configuration still has + // com.foo.level=FINEST + // com.foo.handlers=SimpleUpdateConfigurationTest$MyHandler + // check that the logger still has FINEST level and still + // has its handlers and that they haven't been reset. + manager.updateConfiguration((k) -> ((o, n) -> o)); + assertDeepEquals(loggerHandlers, logger.getHandlers(), + "Logger.getLogger(\"com.foo\").getHandlers()"); + assertEquals("FINEST", manager.getProperty("com.foo.level"), + "com.foo.level updated by updateConfiguration"); + assertEquals(Level.FINEST, logger.getLevel(), + "Logger.getLogger(\"com.foo\").getLevel()"); + assertEquals(MyHandler.class.getName(), + manager.getProperty("com.foo.handlers"), + "manager.getProperty(\"com.foo.handlers\")"); + + // Calls updateConfiguration with a lambda whose effect is to + // take all values from the new configuration. + // This should update the configuration to what is in configFile, so + // check that the new configuration has + // com.foo.level=FINEST + // and nothing for com.foo.handlers + // check that the logger still has FINEST level + // and no handlers, since the old config + // had an handler for com.foo and the new config doesn't. + manager.updateConfiguration((k) -> ((o, n) -> n)); + assertDeepEquals(new Handler[0], logger.getHandlers(), + "Logger.getLogger(\"com.foo\").getHandlers()"); + assertEquals("FINEST", manager.getProperty("com.foo.level"), + "com.foo.level updated by updateConfiguration"); + assertEquals(Level.FINEST, logger.getLevel(), + "Logger.getLogger(\"com.foo\").getLevel()"); + assertEquals(null, + manager.getProperty("com.foo.handlers"), + "manager.getProperty(\"com.foo.handlers\")"); + + + } catch (RuntimeException | Error r) { + throw r; + } catch (Exception x) { + throw new RuntimeException(x); + } finally { + if (configFile != null) { + // cleanup + final String file = configFile; + Configure.doPrivileged(() -> { + try { + Files.delete(Paths.get(file)); + } catch (RuntimeException | Error r) { + throw r; + } catch (Exception x) { + throw new RuntimeException(x); + } + }, SimplePolicy.allowAll); + } + } + } + + public static void main(String[] args) throws Exception { + if (args == null || args.length == 0) { + args = new String[] { "UNSECURE", "SECURE" }; + } + for (String test : args) { + TestCase.valueOf(test).execute(SimpleUpdateConfigurationTest::testUpdateConfiguration); + } + } + + static class Configure { + static Policy policy = null; + static void setUp(TestCase test) { + switch (test) { + case SECURE: + if (policy == null && System.getSecurityManager() != null) { + throw new IllegalStateException("SecurityManager already set"); + } else if (policy == null) { + policy = new SimplePolicy(TestCase.SECURE); + Policy.setPolicy(policy); + System.setSecurityManager(new SecurityManager()); + } + if (System.getSecurityManager() == null) { + throw new IllegalStateException("No SecurityManager."); + } + if (policy == null) { + throw new IllegalStateException("policy not configured"); + } + break; + case UNSECURE: + if (System.getSecurityManager() != null) { + throw new IllegalStateException("SecurityManager already set"); + } + break; + default: + throw new InternalError("No such testcase: " + test); + } + } + + static void updateConfigurationWith(Properties propertyFile, + Function> remapper) { + try { + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + propertyFile.store(bytes, propertyFile.getProperty("test.name")); + ByteArrayInputStream bais = new ByteArrayInputStream(bytes.toByteArray()); + LogManager.getLogManager().updateConfiguration(bais, remapper); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + static void doPrivileged(Runnable run, ThreadLocal granter) { + final boolean old = granter.get().getAndSet(true); + try { + run.run(); + } finally { + granter.get().set(old); + } + } + static T callPrivileged(Callable call, + ThreadLocal granter) throws Exception { + final boolean old = granter.get().getAndSet(true); + try { + return call.call(); + } finally { + granter.get().set(old); + } + } + } + + static final class TestAssertException extends RuntimeException { + TestAssertException(String msg) { + super(msg); + } + } + + private static void assertEquals(long expected, long received, String msg) { + if (expected != received) { + throw new TestAssertException("Unexpected result for " + msg + + ".\n\texpected: " + expected + + "\n\tactual: " + received); + } else { + System.out.println("Got expected " + msg + ": " + received); + } + } + + private static void assertEquals(String expected, String received, String msg) { + if (!Objects.equals(expected, received)) { + throw new TestAssertException("Unexpected result for " + msg + + ".\n\texpected: " + expected + + "\n\tactual: " + received); + } else { + System.out.println("Got expected " + msg + ": " + received); + } + } + + private static void assertEquals(Object expected, Object received, String msg) { + if (!Objects.equals(expected, received)) { + throw new TestAssertException("Unexpected result for " + msg + + ".\n\texpected: " + expected + + "\n\tactual: " + received); + } else { + System.out.println("Got expected " + msg + ": " + received); + } + } + + public static String deepToString(Object o) { + if (o == null) { + return "null"; + } else if (o.getClass().isArray()) { + String s; + if (o instanceof Object[]) + s = Arrays.deepToString((Object[]) o); + else if (o instanceof byte[]) + s = Arrays.toString((byte[]) o); + else if (o instanceof short[]) + s = Arrays.toString((short[]) o); + else if (o instanceof int[]) + s = Arrays.toString((int[]) o); + else if (o instanceof long[]) + s = Arrays.toString((long[]) o); + else if (o instanceof char[]) + s = Arrays.toString((char[]) o); + else if (o instanceof float[]) + s = Arrays.toString((float[]) o); + else if (o instanceof double[]) + s = Arrays.toString((double[]) o); + else if (o instanceof boolean[]) + s = Arrays.toString((boolean[]) o); + else + s = o.toString(); + return s; + } else { + return o.toString(); + } + } + + private static void assertDeepEquals(Object expected, Object received, String msg) { + if (!Objects.deepEquals(expected, received)) { + throw new TestAssertException("Unexpected result for " + msg + + ".\n\texpected: " + deepToString(expected) + + "\n\tactual: " + deepToString(received)); + } else { + System.out.println("Got expected " + msg + ": " + deepToString(received)); + } + } + + final static class PermissionsBuilder { + final Permissions perms; + public PermissionsBuilder() { + this(new Permissions()); + } + public PermissionsBuilder(Permissions perms) { + this.perms = perms; + } + public PermissionsBuilder add(Permission p) { + perms.add(p); + return this; + } + public PermissionsBuilder addAll(PermissionCollection col) { + if (col != null) { + for (Enumeration e = col.elements(); e.hasMoreElements(); ) { + perms.add(e.nextElement()); + } + } + return this; + } + public Permissions toPermissions() { + final PermissionsBuilder builder = new PermissionsBuilder(); + builder.addAll(perms); + return builder.perms; + } + } + + public static class SimplePolicy extends Policy { + + final Permissions basic; + final Permissions control; + final Permissions all; + public final static ThreadLocal allowAll = + new ThreadLocal() { + @Override + protected AtomicBoolean initialValue() { + return new AtomicBoolean(); + } + }; + public final static ThreadLocal allowControl = + new ThreadLocal() { + @Override + protected AtomicBoolean initialValue() { + return new AtomicBoolean(); + } + }; + public SimplePolicy(TestCase test) { + basic = new Permissions(); + control = new Permissions(); + control.add(new LoggingPermission("control", null)); + + // These permissions are required to call updateConfiguration(Function) + control.add(new PropertyPermission("java.util.logging.config.file", "read")); + control.add(new PropertyPermission("java.home", "read")); + control.add(new FilePermission( + Paths.get(System.getProperty("user.dir", "."),"-").toString(), "read")); + control.add(new FilePermission( + Paths.get(System.getProperty("java.home"),"conf","-").toString(), "read")); + + // these are used for configuring the test itself... + all = new Permissions(); + all.add(new java.security.AllPermission()); + + } + + @Override + public boolean implies(ProtectionDomain domain, Permission permission) { + return getPermissions(domain).implies(permission); + } + + public PermissionCollection permissions() { + PermissionsBuilder builder = new PermissionsBuilder(); + if (allowAll.get().get()) { + builder.addAll(all); + } else { + builder.addAll(basic); + if (allowControl.get().get()) { + builder.addAll(control); + } + } + return builder.toPermissions(); + } + + @Override + public PermissionCollection getPermissions(CodeSource codesource) { + return permissions(); + } + + @Override + public PermissionCollection getPermissions(ProtectionDomain domain) { + return permissions(); + } + } + +} diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/logging/LogManager/Configuration/updateConfiguration/UpdateConfigurationTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/util/logging/LogManager/Configuration/updateConfiguration/UpdateConfigurationTest.java Wed Jul 05 20:54:48 2017 +0200 @@ -0,0 +1,608 @@ +/* + * Copyright (c) 2015, 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. + */ +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FilePermission; +import java.io.IOException; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.lang.reflect.Field; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.CodeSource; +import java.security.Permission; +import java.security.PermissionCollection; +import java.security.Permissions; +import java.security.Policy; +import java.security.ProtectionDomain; +import java.util.Arrays; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Properties; +import java.util.Set; +import java.util.TreeSet; +import java.util.UUID; +import java.util.concurrent.Callable; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.logging.FileHandler; +import java.util.logging.LogManager; +import java.util.logging.Logger; +import java.util.logging.LoggingPermission; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * @test + * @bug 8033661 + * @summary tests LogManager.updateConfiguration(bin) + * @run main/othervm UpdateConfigurationTest UNSECURE + * @run main/othervm UpdateConfigurationTest SECURE + * @author danielfuchs + */ +public class UpdateConfigurationTest { + + /** + * We will test the handling of abstract logger nodes with file handlers in + * two configurations: + * UNSECURE: No security manager. + * SECURE: With the security manager present - and the required + * permissions granted. + */ + public static enum TestCase { + UNSECURE, SECURE; + public void run(Properties propertyFile, boolean last) throws Exception { + System.out.println("Running test case: " + name()); + Configure.setUp(this); + test(this.name() + " " + propertyFile.getProperty("test.name"), + propertyFile, last); + } + } + + + private static final String PREFIX = + "FileHandler-" + UUID.randomUUID() + ".log"; + private static final String userDir = System.getProperty("user.dir", "."); + private static final boolean userDirWritable = Files.isWritable(Paths.get(userDir)); + + static enum ConfigMode { APPEND, REPLACE, DEFAULT; + boolean append() { return this == APPEND; } + Function> remapper() { + switch(this) { + case APPEND: + return (k) -> ((o,n) -> (n == null ? o : n)); + case REPLACE: + return (k) -> ((o,n) -> n); + } + return null; + } + } + + private static final List properties; + static { + // The test will be run with each of the configurations below. + // The 'child' logger is forgotten after each test + + Properties props1 = new Properties(); + props1.setProperty("test.name", "props1"); + props1.setProperty("test.config.mode", ConfigMode.REPLACE.name()); + props1.setProperty(FileHandler.class.getName() + ".pattern", PREFIX); + props1.setProperty(FileHandler.class.getName() + ".limit", String.valueOf(Integer.MAX_VALUE)); + props1.setProperty(FileHandler.class.getName() + ".level", "ALL"); + props1.setProperty(FileHandler.class.getName() + ".formatter", "java.util.logging.SimpleFormatter"); + props1.setProperty("com.foo.handlers", FileHandler.class.getName()); + props1.setProperty("com.bar.level", "FINEST"); + + Properties props2 = new Properties(); + props2.setProperty("test.name", "props2"); + props2.setProperty("test.config.mode", ConfigMode.DEFAULT.name()); + props2.setProperty(FileHandler.class.getName() + ".pattern", PREFIX); + props2.setProperty(FileHandler.class.getName() + ".limit", String.valueOf(Integer.MAX_VALUE)); + props2.setProperty(FileHandler.class.getName() + ".level", "ALL"); + props2.setProperty(FileHandler.class.getName() + ".formatter", "java.util.logging.SimpleFormatter"); + props2.setProperty("com.foo.handlers", FileHandler.class.getName()); + props2.setProperty("com.foo.handlers.ensureCloseOnReset", "true"); + props2.setProperty("com.level", "FINE"); + + Properties props3 = new Properties(); + props3.setProperty("test.name", "props3"); + props3.setProperty("test.config.mode", ConfigMode.APPEND.name()); + props3.setProperty(FileHandler.class.getName() + ".pattern", PREFIX); + props3.setProperty(FileHandler.class.getName() + ".limit", String.valueOf(Integer.MAX_VALUE)); + props3.setProperty(FileHandler.class.getName() + ".level", "ALL"); + props3.setProperty(FileHandler.class.getName() + ".formatter", "java.util.logging.SimpleFormatter"); + props3.setProperty("com.foo.handlers", ""); // specify "" to override the value in the previous conf + props3.setProperty("com.foo.handlers.ensureCloseOnReset", "false"); + props3.setProperty("com.bar.level", "FINER"); + + + properties = Collections.unmodifiableList(Arrays.asList( + props1, props2, props3, props1)); + } + + static Properties previous; + static Properties current; + static final Field propsField; + static { + LogManager manager = LogManager.getLogManager(); + try { + propsField = LogManager.class.getDeclaredField("props"); + propsField.setAccessible(true); + previous = current = (Properties) propsField.get(manager); + } catch (NoSuchFieldException | IllegalAccessException ex) { + throw new ExceptionInInitializerError(ex); + } + } + + static Properties getProperties() { + try { + return (Properties) propsField.get(LogManager.getLogManager()); + } catch (IllegalAccessException x) { + throw new RuntimeException(x); + } + } + + static String trim(String value) { + return value == null ? null : value.trim(); + } + + + /** + * Tests one of the configuration defined above. + *

          + * This is the main test method (the rest is infrastructure). + *

          + * Creates a child of the com.foo logger (com.foo.child), resets + * the configuration, and verifies that com.foo has no handler. + * Then reapplies the configuration and verifies that the handler + * for com.foo has been reestablished, depending on whether + * java.util.logging.LogManager.reconfigureHandlers is present and + * true. + *

          + * Finally releases the logger com.foo.child, so that com.foo can + * be garbage collected, and the next configuration can be + * tested. + */ + static void test(ConfigMode mode, String name, Properties props, boolean last) + throws Exception { + + // Then create a child of the com.foo logger. + Logger fooChild = Logger.getLogger("com.foo.child"); + fooChild.info("hello world"); + Logger barChild = Logger.getLogger("com.bar.child"); + barChild.info("hello world"); + + ReferenceQueue queue = new ReferenceQueue(); + WeakReference fooRef = new WeakReference<>(Logger.getLogger("com.foo"), queue); + if (fooRef.get() != fooChild.getParent()) { + throw new RuntimeException("Unexpected parent logger: " + + fooChild.getParent() +"\n\texpected: " + fooRef.get()); + } + WeakReference barRef = new WeakReference<>(Logger.getLogger("com.bar"), queue); + if (barRef.get() != barChild.getParent()) { + throw new RuntimeException("Unexpected parent logger: " + + barChild.getParent() +"\n\texpected: " + barRef.get()); + } + Reference ref2; + int max = 3; + barChild = null; + while ((ref2 = queue.poll()) == null) { + System.gc(); + Thread.sleep(100); + if (--max == 0) break; + } + + Throwable failed = null; + try { + if (ref2 != null) { + String refName = ref2 == fooRef ? "fooRef" : ref2 == barRef ? "barRef" : "unknown"; + if (ref2 != barRef) { + throw new RuntimeException("Unexpected logger reference cleared: " + refName); + } else { + System.out.println("Reference " + refName + " cleared as expected"); + } + } else if (ref2 == null) { + throw new RuntimeException("Expected 'barRef' to be cleared"); + } + // Now lets try to check that ref2 has expected handlers, and + // attempt to configure again. + String p = current.getProperty("com.foo.handlers", "").trim(); + assertEquals(p.isEmpty() ? 0 : 1, fooChild.getParent().getHandlers().length, + "["+name+"] fooChild.getParent().getHandlers().length"); + Configure.doPrivileged(() -> Configure.updateConfigurationWith(props, mode.remapper())); + String p2 = previous.getProperty("com.foo.handlers", "").trim(); + assertEquals(p, p2, "["+name+"] com.foo.handlers"); + String n = trim(props.getProperty("com.foo.handlers", null)); + boolean hasHandlers = mode.append() + ? (n == null ? !p.isEmpty() : !n.isEmpty()) + : n != null && !n.isEmpty(); + assertEquals( hasHandlers ? 1 : 0, + fooChild.getParent().getHandlers().length, + "["+name+"] fooChild.getParent().getHandlers().length" + + "[p=\""+p+"\", n=" + (n==null?null:"\""+n+"\"") + "]"); + + checkProperties(mode, previous, current, props); + + } catch (Throwable t) { + failed = t; + } finally { + if (last || failed != null) { + final Throwable suppressed = failed; + Configure.doPrivileged(LogManager.getLogManager()::reset); + Configure.doPrivileged(() -> { + try { + StringBuilder builder = new StringBuilder(); + Files.list(Paths.get(userDir)) + .filter((f) -> f.toString().contains(PREFIX)) + .filter((f) -> f.toString().endsWith(".lck")) + .forEach((f) -> { + builder.append(f.toString()).append('\n'); + }); + if (!builder.toString().isEmpty()) { + throw new RuntimeException("Lock files not cleaned:\n" + + builder.toString()); + } + } catch(RuntimeException | Error x) { + if (suppressed != null) x.addSuppressed(suppressed); + throw x; + } catch(Exception x) { + if (suppressed != null) x.addSuppressed(suppressed); + throw new RuntimeException(x); + } + }); + + // Now we need to forget the child, so that loggers are released, + // and so that we can run the test with the next configuration... + + fooChild = null; + System.out.println("Setting fooChild to: " + fooChild); + while ((ref2 = queue.poll()) == null) { + System.gc(); + Thread.sleep(1000); + } + if (ref2 != fooRef) { + throw new RuntimeException("Unexpected reference: " + + ref2 +"\n\texpected: " + fooRef); + } + if (ref2.get() != null) { + throw new RuntimeException("Referent not cleared: " + ref2.get()); + } + System.out.println("Got fooRef after reset(), fooChild is " + fooChild); + + } + } + if (failed != null) { + // should rarely happen... + throw new RuntimeException(failed); + } + + } + + private static void checkProperties(ConfigMode mode, + Properties previous, Properties current, Properties props) { + Set set = new HashSet<>(); + + // Check that all property names from 'props' are in current. + set.addAll(props.stringPropertyNames()); + set.removeAll(current.keySet()); + if (!set.isEmpty()) { + throw new RuntimeException("Missing properties in current: " + set); + } + set.clear(); + set.addAll(current.stringPropertyNames()); + set.removeAll(previous.keySet()); + set.removeAll(props.keySet()); + if (!set.isEmpty()) { + throw new RuntimeException("Superfluous properties in current: " + set); + } + set.clear(); + Stream allnames = + Stream.concat( + Stream.concat(previous.stringPropertyNames().stream(), + props.stringPropertyNames().stream()), + current.stringPropertyNames().stream()) + .collect(Collectors.toCollection(TreeSet::new)) + .stream(); + if (mode.append()) { + // Check that all previous property names are in current. + set.addAll(previous.stringPropertyNames()); + set.removeAll(current.keySet()); + if (!set.isEmpty()) { + throw new RuntimeException("Missing properties in current: " + set + + "\n\tprevious: " + previous + + "\n\tcurrent: " + current + + "\n\tprops: " + props); + + } + allnames.forEach((k) -> { + String p = previous.getProperty(k, "").trim(); + String n = current.getProperty(k, "").trim(); + if (props.containsKey(k)) { + assertEquals(props.getProperty(k), n, k); + } else { + assertEquals(p, n, k); + } + }); + } else { + // Check that only properties from 'props' are in current. + set.addAll(current.stringPropertyNames()); + set.removeAll(props.keySet()); + if (!set.isEmpty()) { + throw new RuntimeException("Superfluous properties in current: " + set); + } + allnames.forEach((k) -> { + String p = previous.getProperty(k, ""); + String n = current.getProperty(k, ""); + if (props.containsKey(k)) { + assertEquals(props.getProperty(k), n, k); + } else { + assertEquals("", n, k); + } + }); + } + + } + + public static void main(String... args) throws Exception { + + + if (args == null || args.length == 0) { + args = new String[] { + TestCase.UNSECURE.name(), + TestCase.SECURE.name(), + }; + } + + try { + for (String testName : args) { + TestCase test = TestCase.valueOf(testName); + for (int i=0; i { + // cleanup - delete files that have been created + try { + Files.list(Paths.get(userDir)) + .filter((f) -> f.toString().contains(PREFIX)) + .forEach((f) -> { + try { + System.out.println("deleting " + f); + Files.delete(f); + } catch(Throwable t) { + System.err.println("Failed to delete " + f + ": " + t); + } + }); + } catch(Throwable t) { + System.err.println("Cleanup failed to list files: " + t); + t.printStackTrace(); + } + }); + } + } + } + + static class Configure { + static Policy policy = null; + static final ThreadLocal allowAll = new ThreadLocal() { + @Override + protected AtomicBoolean initialValue() { + return new AtomicBoolean(false); + } + }; + static void setUp(TestCase test) { + switch (test) { + case SECURE: + if (policy == null && System.getSecurityManager() != null) { + throw new IllegalStateException("SecurityManager already set"); + } else if (policy == null) { + policy = new SimplePolicy(TestCase.SECURE, allowAll); + Policy.setPolicy(policy); + System.setSecurityManager(new SecurityManager()); + } + if (System.getSecurityManager() == null) { + throw new IllegalStateException("No SecurityManager."); + } + if (policy == null) { + throw new IllegalStateException("policy not configured"); + } + break; + case UNSECURE: + if (System.getSecurityManager() != null) { + throw new IllegalStateException("SecurityManager already set"); + } + break; + default: + new InternalError("No such testcase: " + test); + } + } + + static void updateConfigurationWith(Properties propertyFile, + Function> remapper) { + try { + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + propertyFile.store(bytes, propertyFile.getProperty("test.name")); + ByteArrayInputStream bais = new ByteArrayInputStream(bytes.toByteArray()); + LogManager.getLogManager().updateConfiguration(bais, remapper); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + static void doPrivileged(Runnable run) { + final boolean old = allowAll.get().getAndSet(true); + try { + Properties before = getProperties(); + try { + run.run(); + } finally { + Properties after = getProperties(); + if (before != after) { + previous = before; + current = after; + } + } + } finally { + allowAll.get().set(old); + } + } + static T callPrivileged(Callable call) throws Exception { + final boolean old = allowAll.get().getAndSet(true); + try { + Properties before = getProperties(); + try { + return call.call(); + } finally { + Properties after = getProperties(); + if (before != after) { + previous = before; + current = after; + } + } + } finally { + allowAll.get().set(old); + } + } + } + + @FunctionalInterface + public static interface FileHandlerSupplier { + public FileHandler test() throws Exception; + } + + static final class TestAssertException extends RuntimeException { + TestAssertException(String msg) { + super(msg); + } + } + + private static void assertEquals(long expected, long received, String msg) { + if (expected != received) { + throw new TestAssertException("Unexpected result for " + msg + + ".\n\texpected: " + expected + + "\n\tactual: " + received); + } else { + System.out.println("Got expected " + msg + ": " + received); + } + } + + private static void assertEquals(String expected, String received, String msg) { + if (!Objects.equals(expected, received)) { + throw new TestAssertException("Unexpected result for " + msg + + ".\n\texpected: " + expected + + "\n\tactual: " + received); + } else { + System.out.println("Got expected " + msg + ": " + received); + } + } + + + public static void test(String name, Properties props, boolean last) throws Exception { + ConfigMode configMode = ConfigMode.valueOf(props.getProperty("test.config.mode")); + System.out.println("\nTesting: " + name + " mode=" + configMode); + if (!userDirWritable) { + throw new RuntimeException("Not writable: "+userDir); + } + switch(configMode) { + case REPLACE: + case APPEND: + case DEFAULT: + test(configMode, name, props, last); break; + default: + throw new RuntimeException("Unknwown mode: " + configMode); + } + } + + final static class PermissionsBuilder { + final Permissions perms; + public PermissionsBuilder() { + this(new Permissions()); + } + public PermissionsBuilder(Permissions perms) { + this.perms = perms; + } + public PermissionsBuilder add(Permission p) { + perms.add(p); + return this; + } + public PermissionsBuilder addAll(PermissionCollection col) { + if (col != null) { + for (Enumeration e = col.elements(); e.hasMoreElements(); ) { + perms.add(e.nextElement()); + } + } + return this; + } + public Permissions toPermissions() { + final PermissionsBuilder builder = new PermissionsBuilder(); + builder.addAll(perms); + return builder.perms; + } + } + + public static class SimplePolicy extends Policy { + + final Permissions permissions; + final Permissions allPermissions; + final ThreadLocal allowAll; // actually: this should be in a thread locale + public SimplePolicy(TestCase test, ThreadLocal allowAll) { + this.allowAll = allowAll; + permissions = new Permissions(); + permissions.add(new LoggingPermission("control", null)); + permissions.add(new FilePermission(PREFIX+".lck", "read,write,delete")); + permissions.add(new FilePermission(PREFIX, "read,write")); + + // these are used for configuring the test itself... + allPermissions = new Permissions(); + allPermissions.add(new java.security.AllPermission()); + + } + + @Override + public boolean implies(ProtectionDomain domain, Permission permission) { + if (allowAll.get().get()) return allPermissions.implies(permission); + return permissions.implies(permission); + } + + @Override + public PermissionCollection getPermissions(CodeSource codesource) { + return new PermissionsBuilder().addAll(allowAll.get().get() + ? allPermissions : permissions).toPermissions(); + } + + @Override + public PermissionCollection getPermissions(ProtectionDomain domain) { + return new PermissionsBuilder().addAll(allowAll.get().get() + ? allPermissions : permissions).toPermissions(); + } + } + +} diff -r 9d153359b8ff -r 470068717ae2 jdk/test/java/util/zip/ZipFile/ZipEntryFreeTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/util/zip/ZipFile/ZipEntryFreeTest.java Wed Jul 05 20:54:48 2017 +0200 @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2015, 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 6907252 + * @summary ZipFileInputStream Not Thread-Safe + * @library /lib/testlibrary + * @build jdk.testlibrary.* + * @run main ZipEntryFreeTest + */ + +import java.io.*; +import java.nio.file.Paths; +import java.util.Random; +import java.util.Timer; +import java.util.TimerTask; +import java.util.zip.*; +import jdk.testlibrary.FileUtils; + +public class ZipEntryFreeTest extends Thread { + + private static final int NUM_THREADS = 5; + private static final int TEST_ITERATIONS = 5; + private static final String ZIPFILE_NAME = "large.zip"; + private static final String ZIPENTRY_NAME = "random.txt"; + private static InputStream is = null; + final Timer timer = new Timer(); + + public static void main(String args[]) throws Exception { + createZipFile(); + try { + for (int i = 0; i < TEST_ITERATIONS; i++) { + runTest(); + } + } finally { + FileUtils.deleteFileIfExistsWithRetry(Paths.get(ZIPFILE_NAME)); + } + } + + private static void runTest() throws Exception { + try (ZipFile zf = new ZipFile(new File(ZIPFILE_NAME))) { + is = zf.getInputStream(zf.getEntry(ZIPENTRY_NAME + "_0")); + Thread[] threadArray = new Thread[NUM_THREADS]; + for (int i = 0; i < threadArray.length; i++) { + threadArray[i] = new ZipEntryFreeTest(); + } + for (int i = 0; i < threadArray.length; i++) { + threadArray[i].start(); + } + for (int i = 0; i < threadArray.length; i++) { + threadArray[i].join(); + } + } + } + + private static void createZipFile() throws Exception { + Random rnd = new Random(1000L); + byte[] contents = new byte[2_000_000]; + ZipEntry ze = null; + + try (ZipOutputStream zos = + new ZipOutputStream(new FileOutputStream(ZIPFILE_NAME))) { + // uncompressed mode seemed to tickle the crash + zos.setMethod(ZipOutputStream.STORED); + for (int ze_count = 0; ze_count < 10; ze_count++) { + rnd.nextBytes(contents); + ze = createZipEntry(contents, ze_count); + zos.putNextEntry(ze); + zos.write(contents, 0, contents.length); + } + zos.flush(); + } + } + + private static ZipEntry createZipEntry(byte[] b, int i) { + ZipEntry ze = new ZipEntry(ZIPENTRY_NAME + "_" + i); + ze.setCompressedSize(b.length); + ze.setSize(b.length); + CRC32 crc = new CRC32(); + crc.update(b); + ze.setCrc(crc.getValue()); + return ze; + } + + @Override + public void run() { + try { + int iteration = 0; + TimerTask tt = (new TimerTask() { + @Override + public void run() { + try { + is.close(); + } catch (Exception ex) { + ex.printStackTrace(System.out); + } + } + }); + timer.schedule(tt, 50); + while (is.read() != -1 && iteration++ < 1_000) { } + } catch (ZipException ze) { + // ZipException now expected instead of ZIP_Read crash + System.out.println(ze); + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + timer.cancel(); + } + } +} diff -r 9d153359b8ff -r 470068717ae2 jdk/test/javax/xml/bind/jxc/8073519/InputWithError.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/xml/bind/jxc/8073519/InputWithError.java Wed Jul 05 20:54:48 2017 +0200 @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2015, 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. + */ +import javax.xml.bind.annotation.XmlType; + +@XmlType +public class InputWithError { + public int a; + int compile-error; +} + diff -r 9d153359b8ff -r 470068717ae2 jdk/test/javax/xml/bind/jxc/8073519/SchemagenErrorReporting.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/xml/bind/jxc/8073519/SchemagenErrorReporting.java Wed Jul 05 20:54:48 2017 +0200 @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2015, 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 8073519 + * @summary test that schemagen tool reports errors during + * xsd generation process + * @library /lib/testlibrary + * @run testng/othervm SchemagenErrorReporting + */ +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.Arrays; +import java.util.stream.Collectors; +import jdk.testlibrary.JDKToolLauncher; +import org.testng.Assert; +import org.testng.annotations.Test; + +public class SchemagenErrorReporting { + + @Test + public void schemagenErrorReporting() throws Exception { + //schemagen tool output file name + final String SCHEMA_FILE = "schema1.xsd"; + //Schemagen input java file with not compilable source + final String CLASS_FILE = "InputWithError.java"; + //Test working, src directories and test output file + Path testWorkDir, testSrcDir, testOutput; + + //Prepare test environment + //Create test directory inside scratch + testWorkDir = Paths.get(System.getProperty("user.dir", ".")) + .resolve("SchemagenErrorReporting"); + //Get test source directory + testSrcDir = Paths.get(System.getProperty("test.src", ".")); + //Set test output file path + testOutput = testWorkDir.resolve("stdErrContent"); + //Create test directory inside scratch directory + Files.createDirectory(testWorkDir); + //Copy java source from test.src to the test directory + Files.copy(testSrcDir.resolve(CLASS_FILE), testWorkDir.resolve(CLASS_FILE), + StandardCopyOption.REPLACE_EXISTING); + + //Prepare process builder to run schemagen tool and save its output + JDKToolLauncher sgl = JDKToolLauncher.createUsingTestJDK("schemagen"); + sgl.addToolArg(CLASS_FILE); + System.out.println("Executing: " + Arrays.asList(sgl.getCommand())); + ProcessBuilder pb = new ProcessBuilder(sgl.getCommand()); + //Set schemagen work directory with the input java file + pb.directory(testWorkDir.toFile()); + //Redirect schemagen output to file + pb.redirectError(testOutput.toFile()); + Process p = pb.start(); + int result = p.waitFor(); + p.destroy(); + + //Read schemagen output from the file + String stdErrContent = Files.lines(testOutput) + .collect(Collectors.joining(System.lineSeparator(), System.lineSeparator(), "")); + System.out.println("Schemagen return value:" + result); + System.out.println("Error output:" + stdErrContent); + //Check test results: + //Schemagen finished with non-0 return value + Assert.assertNotEquals(result, 0); + //Schemagen output contains compile error message + Assert.assertTrue(stdErrContent.contains("InputWithError.java:28: error")); + } +} diff -r 9d153359b8ff -r 470068717ae2 jdk/test/sun/util/resources/TimeZone/Bug8139107.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/sun/util/resources/TimeZone/Bug8139107.java Wed Jul 05 20:54:48 2017 +0200 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2015, 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 8139107 + * @summary Test that date parsing with DateTimeFormatter pattern + * that contains timezone field doesn't trigger NPE. All supported + * locales are tested. + * @run testng/othervm -Djava.locale.providers=JRE,SPI Bug8139107 + */ +import java.time.format.DateTimeFormatter; +import java.util.Locale; +import org.testng.annotations.Test; + +public class Bug8139107 { + + @Test + public void testSupportedLocales() { + for (Locale loc:Locale.getAvailableLocales()) { + testLocale(loc); + } + } + + //Test one locale + void testLocale(Locale tl) { + System.out.println("Locale:" + tl); + DateTimeFormatter inputDateTimeFormat = DateTimeFormatter + .ofPattern(pattern) + .withLocale(tl); + System.out.println("Parse result: " + inputDateTimeFormat.parse(inputDate)); + } + + // Input date time string with short time zone name + static final String inputDate = "06-10-2015 18:58:04 MSK"; + // Pattern with time zone field + static final String pattern = "dd-MM-yyyy HH:mm:ss z"; +} + diff -r 9d153359b8ff -r 470068717ae2 nashorn/.hgtags --- a/nashorn/.hgtags Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/.hgtags Wed Jul 05 20:54:48 2017 +0200 @@ -320,3 +320,4 @@ 214b97ba911f4d768c0214098739e32ab54c8503 jdk9-b84 285628dac94332d95979de365630c93ab8fa9962 jdk9-b85 e4283eeb182cbdf5004740728a404aac6afa1104 jdk9-b86 +0bf2fe0c7b3277cd247ed4f6faaf2b56d83139cf jdk9-b87 diff -r 9d153359b8ff -r 470068717ae2 nashorn/samples/jsadapter-fallthrough.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/samples/jsadapter-fallthrough.js Wed Jul 05 20:54:48 2017 +0200 @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Oracle nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// Demonstrate how to provide a default fall-through solution for adapted Java +// objects, forwarding all calls that are not adapted. + +var Date = Java.type('java.time.LocalDate') + +function wrapDate(d) { + return new JSAdapter() { + __call__: function() { + // adapted (extra) methods + if (arguments[0] === 'yesterday') { + return d.minusDays(1) + } + // fall-through: forward all other, non-adapted method calls + var args = [d].concat(Array.prototype.slice.call(arguments, 1)) + return Function.call.apply(d[arguments[0]], args) + } + } +} + +var now = wrapDate(Date.now()) + +print(now) +print(now.yesterday()) // adapted +print(now.lengthOfMonth()) // fall through to original method +print(now.atTime(23, 42)) // arguments are passed through + diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/CallSiteDescriptor.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/CallSiteDescriptor.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/CallSiteDescriptor.java Wed Jul 05 20:54:48 2017 +0200 @@ -96,8 +96,6 @@ * guarding linkers so they aren't tempted to directly manipulate the call sites. The constructors of built-in * {@link RelinkableCallSite} implementations all need a call site descriptor. Even if you create your own call site * descriptors consider using {@link CallSiteDescriptorFactory#tokenizeName(String)} in your implementation. - * - * @author Attila Szegedi */ public interface CallSiteDescriptor { /** @@ -157,7 +155,9 @@ public MethodType getMethodType(); /** - * Returns the lookup passed to the bootstrap method. + * Returns the lookup passed to the bootstrap method. If the lookup isn't the public lookup, the + * implementation must check the {@code RuntimePermission("dynalink.getLookup")} permission if a security + * manager is present. * @return the lookup passed to the bootstrap method. */ public Lookup getLookup(); diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/ChainedCallSite.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/ChainedCallSite.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/ChainedCallSite.java Wed Jul 05 20:54:48 2017 +0200 @@ -85,9 +85,9 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; +import java.util.Arrays; import java.util.Iterator; import java.util.LinkedList; -import java.util.concurrent.atomic.AtomicReference; import jdk.internal.dynalink.linker.GuardedInvocation; import jdk.internal.dynalink.support.AbstractRelinkableCallSite; import jdk.internal.dynalink.support.Lookup; @@ -112,7 +112,14 @@ PRUNE_SWITCHPOINTS = MethodHandles.insertArguments(PRUNE, 2, false); } - private final AtomicReference> invocations = new AtomicReference<>(); + /** + * Contains the invocations currently linked into this call site's target. They are used when we are + * relinking to rebuild the guardWithTest chain. Valid values for this field are: {@code null} if there's + * no linked invocations, or an instance of {@link GuardedInvocation} if there is exactly one previous + * invocation, or an instance of {@code GuardedInvocation[]} if there is more than one previous + * invocation. + */ + private Object invocations; /** * Creates a new chained call site. @@ -142,10 +149,18 @@ } private MethodHandle relinkInternal(final GuardedInvocation invocation, final MethodHandle relink, final boolean reset, final boolean removeCatches) { - final LinkedList currentInvocations = invocations.get(); - @SuppressWarnings({ "unchecked", "rawtypes" }) - final LinkedList newInvocations = - currentInvocations == null || reset ? new LinkedList<>() : (LinkedList)currentInvocations.clone(); + final Object currentInvocations = invocations; + final LinkedList newInvocations; + if (currentInvocations == null || reset) { + newInvocations = new LinkedList<>(); + } else if (currentInvocations instanceof GuardedInvocation) { + newInvocations = new LinkedList<>(); + newInvocations.add((GuardedInvocation)currentInvocations); + } else if (currentInvocations instanceof GuardedInvocation[]) { + newInvocations = new LinkedList<>(Arrays.asList(((GuardedInvocation[])currentInvocations))); + } else { + throw new AssertionError(); + } // First, prune the chain of invalidated switchpoints, we always do this // We also remove any catches if the remove catches flag is set @@ -177,12 +192,17 @@ target = inv.compose(target, pruneAndInvokeSwitchPoints, pruneAndInvokeCatches); } - // If nobody else updated the call site while we were rebuilding the chain, set the target to our chain. In case - // we lost the race for multithreaded update, just do nothing. Either the other thread installed the same thing - // we wanted to install, or otherwise, we'll be asked to relink again. - if(invocations.compareAndSet(currentInvocations, newInvocations)) { - setTarget(target); + switch (newInvocations.size()) { + case 0: + invocations = null; + break; + case 1: + invocations = newInvocations.getFirst(); + break; + default: + invocations = newInvocations.toArray(new GuardedInvocation[newInvocations.size()]); } + setTarget(target); return target; } diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/DefaultBootstrapper.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/DefaultBootstrapper.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/DefaultBootstrapper.java Wed Jul 05 20:54:48 2017 +0200 @@ -97,8 +97,6 @@ * and one that just uses the passed caller as the lookup scope. Using the public lookup one is advised if your language * runtime has no concept of interacting with Java visibility scopes, as it results in a more lightweight runtime * information. - * - * @author Attila Szegedi */ public class DefaultBootstrapper { private static final DynamicLinker dynamicLinker = new DynamicLinkerFactory().createLinker(); diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/DynamicLinker.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/DynamicLinker.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/DynamicLinker.java Wed Jul 05 20:54:48 2017 +0200 @@ -147,15 +147,14 @@ * additional parameters to the bootstrap method) to them. * * - * - * @author Attila Szegedi */ -public class DynamicLinker { +public final class DynamicLinker { private static final String CLASS_NAME = DynamicLinker.class.getName(); private static final String RELINK_METHOD_NAME = "relink"; private static final String INITIAL_LINK_CLASS_NAME = "java.lang.invoke.MethodHandleNatives"; private static final String INITIAL_LINK_METHOD_NAME = "linkCallSite"; + private static final String INVOKE_PACKAGE_PREFIX = "java.lang.invoke."; private final LinkerServices linkerServices; private final GuardedInvocationFilter prelinkFilter; @@ -305,26 +304,21 @@ final StackTraceElement[] trace = new Throwable().getStackTrace(); for(int i = 0; i < trace.length - 1; ++i) { final StackTraceElement frame = trace[i]; + // If we found any of our linking entry points on the stack... if(isRelinkFrame(frame) || isInitialLinkFrame(frame)) { - return trace[i + 1]; + // ... then look for the first thing calling it that isn't j.l.invoke + for (int j = i + 1; j < trace.length; ++j) { + final StackTraceElement frame2 = trace[j]; + if (!frame2.getClassName().startsWith(INVOKE_PACKAGE_PREFIX)) { + return frame2; + } + } } } return null; } /** - * Deprecated because of imprecise name. - * - * @deprecated Use {@link #getLinkedCallSiteLocation()} instead. - * - * @return see non-deprecated method - */ - @Deprecated - public static StackTraceElement getRelinkedCallSiteLocation() { - return getLinkedCallSiteLocation(); - } - - /** * Returns {@code true} if the frame represents {@code MethodHandleNatives.linkCallSite()}, * the frame immediately on top of the call site frame when the call site is * being linked for the first time. diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/DynamicLinkerFactory.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/DynamicLinkerFactory.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/DynamicLinkerFactory.java Wed Jul 05 20:54:48 2017 +0200 @@ -111,14 +111,13 @@ import jdk.internal.dynalink.support.TypeUtilities; /** - * A factory class for creating {@link DynamicLinker}s. The most usual dynamic linker is a linker that is a composition - * of all {@link GuardingDynamicLinker}s known and pre-created by the caller as well as any - * {@link AutoDiscovery automatically discovered} guarding linkers and the standard fallback {@link BeansLinker} and a - * {@link DefaultPrelinkFilter}. See {@link DynamicLinker} documentation for tips on how to use this class. - * - * @author Attila Szegedi + * A factory class for creating {@link DynamicLinker}s. The usual dynamic linker is a linker composed of all + * {@link GuardingDynamicLinker}s known and pre-created by the caller as well as any + * {@link AutoDiscovery automatically discovered} guarding linkers and the standard fallback + * {@link BeansLinker} and a {@link DefaultPrelinkFilter}. See {@link DynamicLinker} documentation for tips on + * how to use this class. */ -public class DynamicLinkerFactory { +public final class DynamicLinkerFactory { /** * Default value for {@link #setUnstableRelinkThreshold(int) unstable relink threshold}. */ diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/MonomorphicCallSite.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/MonomorphicCallSite.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/MonomorphicCallSite.java Wed Jul 05 20:54:48 2017 +0200 @@ -91,8 +91,6 @@ * A relinkable call site that implements monomorphic inline caching strategy. After it linked a method, it will keep it * until either its guard evaluates to false, or its switchpoint is invalidated, at which time it will throw away the * previous linkage, and trigger relinking with its associated {@link DynamicLinker}. - * - * @author Attila Szegedi */ public class MonomorphicCallSite extends AbstractRelinkableCallSite { /** diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/NoSuchDynamicMethodException.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/NoSuchDynamicMethodException.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/NoSuchDynamicMethodException.java Wed Jul 05 20:54:48 2017 +0200 @@ -87,8 +87,6 @@ /** * Thrown at the invocation if the call site can not be linked by any available {@link GuardingDynamicLinker}. - * - * @author Attila Szegedi */ public class NoSuchDynamicMethodException extends RuntimeException { private static final long serialVersionUID = 1L; diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/RelinkableCallSite.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/RelinkableCallSite.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/RelinkableCallSite.java Wed Jul 05 20:54:48 2017 +0200 @@ -96,13 +96,11 @@ * {@link ChainedCallSite} that retains a chain of already linked method handles. The reason this is defined as an * interface instead of a concrete, albeit abstract class is that it allows independent implementations to choose * between {@link MutableCallSite} and {@link VolatileCallSite} as they see fit. - * - * @author Attila Szegedi */ public interface RelinkableCallSite { /** - * Initializes the relinkable call site by setting a relink-and-invoke method handle. The call site implementation - * is supposed to set this method handle as its target. + * Initializes the relinkable call site by setting a relink-and-invoke method handle. The call site + * implementation is supposed to set this method handle as its target. * @param relinkAndInvoke a relink-and-invoke method handle supplied by the {@link DynamicLinker}. */ public void initialize(MethodHandle relinkAndInvoke); diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/AbstractJavaLinker.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/AbstractJavaLinker.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/AbstractJavaLinker.java Wed Jul 05 20:54:48 2017 +0200 @@ -111,8 +111,6 @@ /** * A base class for both {@link StaticClassLinker} and {@link BeanLinker}. Deals with common aspects of property * exposure and method calls for both static and instance facets of a class. - * - * @author Attila Szegedi */ abstract class AbstractJavaLinker implements GuardingDynamicLinker { diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/AccessibleMembersLookup.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/AccessibleMembersLookup.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/AccessibleMembersLookup.java Wed Jul 05 20:54:48 2017 +0200 @@ -98,8 +98,6 @@ * public, or belongs to a restricted-access package. In that case, it is required to lookup a member in a publicly * accessible superclass or implemented interface of the class, and use it instead of the member discovered on the * class. - * - * @author Attila Szegedi */ class AccessibleMembersLookup { private final Map methods; @@ -140,8 +138,6 @@ /** * A helper class that represents a method signature - name and argument types. - * - * @author Attila Szegedi */ static final class MethodSignature { private final String name; diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/ApplicableOverloadedMethods.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/ApplicableOverloadedMethods.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/ApplicableOverloadedMethods.java Wed Jul 05 20:54:48 2017 +0200 @@ -90,8 +90,6 @@ /** * Represents overloaded methods applicable to a specific call site signature. - * - * @author Attila Szegedi */ class ApplicableOverloadedMethods { private final List methods; diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/BeanLinker.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/BeanLinker.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/BeanLinker.java Wed Jul 05 20:54:48 2017 +0200 @@ -102,8 +102,6 @@ /** * A class that provides linking capabilities for a single POJO class. Normally not used directly, but managed by * {@link BeansLinker}. - * - * @author Attila Szegedi */ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicLinker { BeanLinker(final Class clazz) { @@ -316,8 +314,6 @@ /** * Contains methods to adapt an item getter/setter method handle to the requested type, optionally binding it to a * fixed key first. - * @author Attila Szegedi - * @version $Id: $ */ private static class Binder { private final LinkerServices linkerServices; diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/BeansLinker.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/BeansLinker.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/BeansLinker.java Wed Jul 05 20:54:48 2017 +0200 @@ -125,8 +125,6 @@ *

          Variable argument invocation is handled for both methods and constructors.

          *

          Currently, only public fields and methods are supported. Any Lookup objects passed in the * {@link LinkRequest}s are ignored and {@link MethodHandles#publicLookup()} is used instead.

          - * - * @author Attila Szegedi */ public class BeansLinker implements GuardingDynamicLinker { private static final ClassValue linkers = new ClassValue() { diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/CallerSensitiveDynamicMethod.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/CallerSensitiveDynamicMethod.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/CallerSensitiveDynamicMethod.java Wed Jul 05 20:54:48 2017 +0200 @@ -98,8 +98,6 @@ * caller sensitive, it doesn't cache a method handle but rather uses the passed lookup object in * {@link #getTarget(java.lang.invoke.MethodHandles.Lookup)} to unreflect a method handle from the reflective member on * every request. - * - * @author Attila Szegedi */ class CallerSensitiveDynamicMethod extends SingleDynamicMethod { // Typed as "AccessibleObject" as it can be either a method or a constructor. diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/ClassLinker.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/ClassLinker.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/ClassLinker.java Wed Jul 05 20:54:48 2017 +0200 @@ -93,7 +93,6 @@ * A linker for java.lang.Class objects. Provides a synthetic property "static" that allows access to static fields and * methods on the class (respecting property getter/setter conventions). Note that Class objects are not recognized by * the Dynalink as constructors for the instances of the class, {@link StaticClass} is used for this purpose. - * @author Attila Szegedi */ class ClassLinker extends BeanLinker { diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/ClassString.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/ClassString.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/ClassString.java Wed Jul 05 20:54:48 2017 +0200 @@ -92,8 +92,9 @@ import jdk.internal.dynalink.support.TypeUtilities; /** - * - * @author Attila Szegedi + * Represents a sequence of {@link Class} objects, useful for representing method signatures. Provides value + * semantics for using them as map keys, as well as specificity calculations and applicability checks as per + * JLS. */ final class ClassString { /** diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/DynamicMethod.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/DynamicMethod.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/DynamicMethod.java Wed Jul 05 20:54:48 2017 +0200 @@ -93,8 +93,6 @@ * overloaded methods will perform overload resolution (actually, it will perform partial overloaded resolution at link * time, but if that fails to identify exactly one target method, it will generate a method handle that will perform the * rest of the overload resolution at invocation time for actual argument types). - * - * @author Attila Szegedi */ abstract class DynamicMethod { private final String name; diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/FacetIntrospector.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/FacetIntrospector.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/FacetIntrospector.java Wed Jul 05 20:54:48 2017 +0200 @@ -97,7 +97,6 @@ /** * Base for classes that expose class field and method information to an {@link AbstractJavaLinker}. There are * subclasses for instance (bean) and static facet of a class. - * @author Attila Szegedi */ abstract class FacetIntrospector { private final Class clazz; diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/GuardedInvocationComponent.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/GuardedInvocationComponent.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/GuardedInvocationComponent.java Wed Jul 05 20:54:48 2017 +0200 @@ -91,8 +91,6 @@ * {@link AbstractJavaLinker}. In addition to holding a guarded invocation, it holds semantic information about its * guard. All guards produced in the AbstractJavaLinker are either "Class.isInstance()" or "getClass() == clazz" * expressions. This allows choosing the most restrictive guard as the guard for the composition of two components. - * @author Attila Szegedi - * @version $Id: $ */ class GuardedInvocationComponent { enum ValidationType { diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/MaximallySpecific.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/MaximallySpecific.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/MaximallySpecific.java Wed Jul 05 20:54:48 2017 +0200 @@ -94,8 +94,6 @@ /** * Utility class that encapsulates the algorithm for choosing the maximally specific methods. - * - * @author Attila Szegedi */ class MaximallySpecific { /** diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/OverloadedDynamicMethod.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/OverloadedDynamicMethod.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/OverloadedDynamicMethod.java Wed Jul 05 20:54:48 2017 +0200 @@ -101,8 +101,6 @@ * Represents a group of {@link SingleDynamicMethod} objects that represents all overloads of a particular name (or all * constructors) for a particular class. Correctly handles overload resolution, variable arity methods, and caller * sensitive methods within the overloads. - * - * @author Attila Szegedi */ class OverloadedDynamicMethod extends DynamicMethod { /** diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/OverloadedMethod.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/OverloadedMethod.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/OverloadedMethod.java Wed Jul 05 20:54:48 2017 +0200 @@ -100,8 +100,6 @@ * a vararg subset depending on the subclass. The method is for a fixed number of arguments though (as it is generated * for a concrete call site). As such, all methods in the subset can be invoked with the specified number of arguments * (exactly matching for fixargs, or having less than or equal fixed arguments, for varargs). - * - * @author Attila Szegedi */ class OverloadedMethod { private final Map argTypesToMethods = new ConcurrentHashMap<>(); @@ -122,7 +120,7 @@ fixArgMethods = new ArrayList<>(methodHandles.size()); varArgMethods = new ArrayList<>(methodHandles.size()); final int argNum = callSiteType.parameterCount(); - for(MethodHandle mh: methodHandles) { + for(final MethodHandle mh: methodHandles) { if(mh.isVarargsCollector()) { final MethodHandle asFixed = mh.asFixedArity(); if(argNum == asFixed.type().parameterCount()) { diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/SimpleDynamicMethod.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/SimpleDynamicMethod.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/SimpleDynamicMethod.java Wed Jul 05 20:54:48 2017 +0200 @@ -93,8 +93,6 @@ * {@link #getTarget(Lookup)}. Can be used in general to represents dynamic methods bound to a single method handle, * even if that handle is not mapped to a Java method, i.e. as a wrapper around field getters/setters, array element * getters/setters, etc. - * - * @author Attila Szegedi */ class SimpleDynamicMethod extends SingleDynamicMethod { private final MethodHandle target; diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/SingleDynamicMethod.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/SingleDynamicMethod.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/SingleDynamicMethod.java Wed Jul 05 20:54:48 2017 +0200 @@ -97,7 +97,6 @@ * Base class for dynamic methods that dispatch to a single target Java method or constructor. Handles adaptation of the * target method to a call site type (including mapping variable arity methods to a call site signature with different * arity). - * @author Attila Szegedi */ abstract class SingleDynamicMethod extends DynamicMethod { diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/StaticClassLinker.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/StaticClassLinker.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/StaticClassLinker.java Wed Jul 05 20:54:48 2017 +0200 @@ -99,7 +99,6 @@ /** * Provides a linker for the {@link StaticClass} objects. - * @author Attila Szegedi */ class StaticClassLinker implements TypeBasedGuardingDynamicLinker { private static final ClassValue linkers = new ClassValue() { diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/linker/ConversionComparator.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/linker/ConversionComparator.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/linker/ConversionComparator.java Wed Jul 05 20:54:48 2017 +0200 @@ -90,15 +90,17 @@ * of additional conversions. The static way of selecting the "most specific" method will fail more often, because there * will be multiple maximally specific method with unrelated signatures. In these cases, language runtimes can be asked * to resolve the ambiguity by expressing preferences for one conversion over the other. - * @author Attila Szegedi */ public interface ConversionComparator { /** * Enumeration of possible outcomes of comparing one conversion to another. */ enum Comparison { + /** The conversions cannot be compared. **/ INDETERMINATE, + /** The first conversion is better than the second one. **/ TYPE_1_BETTER, + /** The second conversion is better than the first one. **/ TYPE_2_BETTER, } diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/linker/GuardedInvocation.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/linker/GuardedInvocation.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/linker/GuardedInvocation.java Wed Jul 05 20:54:48 2017 +0200 @@ -101,8 +101,6 @@ * external invalidation of the invocation handle. The invocation handle is suitable for invocation if the guard * handle returns true for its arguments, and as long as the switch point is not invalidated. Both the guard and the * switch point are optional; neither, one, or both can be present. - * - * @author Attila Szegedi */ public class GuardedInvocation { private final MethodHandle invocation; diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/linker/GuardingDynamicLinker.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/linker/GuardingDynamicLinker.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/linker/GuardingDynamicLinker.java Wed Jul 05 20:54:48 2017 +0200 @@ -89,8 +89,6 @@ * very least, it depends on the receiver belonging to the language runtime of the linker). Language runtime * implementors will normally implement one for their own language, and declare it in the * META-INF/services/jdk.internal.dynalink.linker.GuardingDynamicLinker file within their JAR file. - * - * @author Attila Szegedi */ public interface GuardingDynamicLinker { /** diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/linker/GuardingTypeConverterFactory.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/linker/GuardingTypeConverterFactory.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/linker/GuardingTypeConverterFactory.java Wed Jul 05 20:54:48 2017 +0200 @@ -91,8 +91,6 @@ * very likely want to implement {@link ConversionComparator} interface too, as your additional language-specific * conversions, in absence of a strategy for prioritizing these conversions, will cause more ambiguity in selecting the * correct overload when trying to link to an overloaded POJO method. - * - * @author Attila Szegedi */ public interface GuardingTypeConverterFactory { /** diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/linker/LinkRequest.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/linker/LinkRequest.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/linker/LinkRequest.java Wed Jul 05 20:54:48 2017 +0200 @@ -89,8 +89,6 @@ /** * Represents a request to link a particular invocation at a particular call site. Instances of these requests are being * passed to {@link GuardingDynamicLinker}. - * - * @author Attila Szegedi */ public interface LinkRequest { /** diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/linker/LinkerServices.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/linker/LinkerServices.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/linker/LinkerServices.java Wed Jul 05 20:54:48 2017 +0200 @@ -95,8 +95,6 @@ * Interface for services provided to {@link GuardingDynamicLinker} instances by the {@link DynamicLinker} that owns * them. You can think of it as the interface of the {@link DynamicLinker} that faces the {@link GuardingDynamicLinker} * s. - * - * @author Attila Szegedi */ public interface LinkerServices { /** @@ -130,7 +128,11 @@ * {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)} with * {@link GuardingTypeConverterFactory}-produced type converters as filters. */ - public MethodHandle asTypeLosslessReturn(MethodHandle handle, MethodType fromType); + public default MethodHandle asTypeLosslessReturn(final MethodHandle handle, final MethodType fromType) { + final Class handleReturnType = handle.type().returnType(); + return asType(handle, TypeUtilities.isConvertibleWithoutLoss(handleReturnType, fromType.returnType()) ? + fromType : fromType.changeReturnType(handleReturnType)); + } /** * Given a source and target type, returns a method handle that converts between them. Never returns null; in worst @@ -188,23 +190,4 @@ * @return a method handle with parameters and/or return type potentially filtered for wrapping and unwrapping. */ public MethodHandle filterInternalObjects(final MethodHandle target); - - /** - * If we could just use Java 8 constructs, then {@code asTypeSafeReturn} would be a method with default - * implementation. Since we can't do that, we extract common default implementations into this static class. - */ - public static class Implementation { - /** - * Default implementation for {@link LinkerServices#asTypeLosslessReturn(MethodHandle, MethodType)}. - * @param linkerServices the linker services that delegates to this implementation - * @param handle the passed handle - * @param fromType the passed type - * @return the converted method handle, as per the {@code asTypeSafeReturn} semantics. - */ - public static MethodHandle asTypeLosslessReturn(final LinkerServices linkerServices, final MethodHandle handle, final MethodType fromType) { - final Class handleReturnType = handle.type().returnType(); - return linkerServices.asType(handle, TypeUtilities.isConvertibleWithoutLoss(handleReturnType, fromType.returnType()) ? - fromType : fromType.changeReturnType(handleReturnType)); - } - } } diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/linker/TypeBasedGuardingDynamicLinker.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/linker/TypeBasedGuardingDynamicLinker.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/linker/TypeBasedGuardingDynamicLinker.java Wed Jul 05 20:54:48 2017 +0200 @@ -88,8 +88,6 @@ * argument at linking invocation time. (The first argument is usually the receiver class). Most language-specific * linkers will fall into this category, as they recognize their native objects as Java objects of classes implementing * a specific language-native interface or superclass. The linker mechanism can optimize the dispatch for these linkers. - * - * @author Attila Szegedi */ public interface TypeBasedGuardingDynamicLinker extends GuardingDynamicLinker { /** diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/AbstractCallSiteDescriptor.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/AbstractCallSiteDescriptor.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/AbstractCallSiteDescriptor.java Wed Jul 05 20:54:48 2017 +0200 @@ -91,7 +91,6 @@ /** * A base class for call site descriptor implementations. Provides reconstruction of the name from the tokens, as well * as a generally useful {@code equals} and {@code hashCode} methods. - * @author Attila Szegedi */ public abstract class AbstractCallSiteDescriptor implements CallSiteDescriptor { diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/AbstractRelinkableCallSite.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/AbstractRelinkableCallSite.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/AbstractRelinkableCallSite.java Wed Jul 05 20:54:48 2017 +0200 @@ -90,8 +90,6 @@ /** * A basic implementation of the {@link RelinkableCallSite} as a {@link MutableCallSite} subclass. - * - * @author Attila Szegedi */ public abstract class AbstractRelinkableCallSite extends MutableCallSite implements RelinkableCallSite { private final CallSiteDescriptor descriptor; diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/BottomGuardingDynamicLinker.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/BottomGuardingDynamicLinker.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/BottomGuardingDynamicLinker.java Wed Jul 05 20:54:48 2017 +0200 @@ -91,8 +91,6 @@ /** * A linker that can't link any call site. Only used internally by {@link CompositeTypeBasedGuardingDynamicLinker}. Can * be used by other language runtimes if they need it though. - * - * @author Attila Szegedi */ public class BottomGuardingDynamicLinker implements TypeBasedGuardingDynamicLinker { diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/CallSiteDescriptorFactory.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/CallSiteDescriptorFactory.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/CallSiteDescriptorFactory.java Wed Jul 05 20:54:48 2017 +0200 @@ -97,12 +97,12 @@ import jdk.internal.dynalink.CallSiteDescriptor; /** - * Usable as a default factory for call site descriptor implementations. It is weakly canonicalizing, meaning it will - * return the same immutable call site descriptor for identical inputs, i.e. repeated requests for a descriptor - * signifying public lookup for "dyn:getProp:color" of type "Object(Object)" will return the same object as long as - * a previously created, at least softly reachable one exists. It also uses several different implementations of the - * {@link CallSiteDescriptor} internally, and chooses the most space-efficient one based on the input. - * @author Attila Szegedi + * Usable as a default factory for call site descriptor implementations. It is weakly canonicalizing, meaning + * it will return the same immutable call site descriptor for identical inputs, i.e. repeated requests for a + * descriptor signifying public lookup for {@code "dyn:getProp:color"} of type {@code Object(Object)} will + * return the same object as long as a previously created, at least softly reachable one exists. It also uses + * several different implementations of the {@link CallSiteDescriptor} internally, and chooses the most + * space-efficient one based on the input. */ public class CallSiteDescriptorFactory { private static final WeakHashMap> publicDescs = diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/ClassMap.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/ClassMap.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/ClassMap.java Wed Jul 05 20:54:48 2017 +0200 @@ -96,7 +96,6 @@ * A dual map that can either strongly or weakly reference a given class depending on whether the class is visible from * a class loader or not. * - * @author Attila Szegedi * @param the type of the values in the map */ public abstract class ClassMap { diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/CompositeGuardingDynamicLinker.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/CompositeGuardingDynamicLinker.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/CompositeGuardingDynamicLinker.java Wed Jul 05 20:54:48 2017 +0200 @@ -95,8 +95,6 @@ * A {@link GuardingDynamicLinker} that delegates sequentially to a list of other guarding dynamic linkers. The first * value returned from a component linker other than null is returned. If no component linker returns an invocation, * null is returned. - * - * @author Attila Szegedi */ public class CompositeGuardingDynamicLinker implements GuardingDynamicLinker, Serializable { diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/CompositeTypeBasedGuardingDynamicLinker.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/CompositeTypeBasedGuardingDynamicLinker.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/CompositeTypeBasedGuardingDynamicLinker.java Wed Jul 05 20:54:48 2017 +0200 @@ -98,8 +98,6 @@ * are queried sequentially on their {@link TypeBasedGuardingDynamicLinker#canLinkType(Class)} method. The linkers * returning true are then bound to the class, and next time a receiver of same type is encountered, the linking is * delegated to those linkers only, speeding up dispatch. - * - * @author Attila Szegedi */ public class CompositeTypeBasedGuardingDynamicLinker implements TypeBasedGuardingDynamicLinker, Serializable { private static final long serialVersionUID = 1L; diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/DefaultCallSiteDescriptor.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/DefaultCallSiteDescriptor.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/DefaultCallSiteDescriptor.java Wed Jul 05 20:54:48 2017 +0200 @@ -91,7 +91,6 @@ * A default, fairly light implementation of a call site descriptor used for describing non-standard operations. It does * not store {@link Lookup} objects but always returns the public lookup from its {@link #getLookup()} method. If you * need to support non-public lookup, you can use {@link LookupCallSiteDescriptor}. - * @author Attila Szegedi */ class DefaultCallSiteDescriptor extends AbstractCallSiteDescriptor { diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/Guards.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/Guards.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/Guards.java Wed Jul 05 20:54:48 2017 +0200 @@ -94,7 +94,6 @@ /** * Utility methods for creating typical guards. TODO: introduce reasonable caching of created guards. * - * @author Attila Szegedi */ public class Guards { private static final Logger LOG = Logger diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/LinkRequestImpl.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/LinkRequestImpl.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/LinkRequestImpl.java Wed Jul 05 20:54:48 2017 +0200 @@ -89,8 +89,6 @@ /** * Default implementation of the {@link LinkRequest}, representing a link request to a call site that passes no language * runtime specific native context arguments on the stack. - * - * @author Attila Szegedi */ public class LinkRequestImpl implements LinkRequest { diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/LinkerServicesImpl.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/LinkerServicesImpl.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/LinkerServicesImpl.java Wed Jul 05 20:54:48 2017 +0200 @@ -94,8 +94,6 @@ /** * Default implementation of the {@link LinkerServices} interface. - * - * @author Attila Szegedi */ public class LinkerServicesImpl implements LinkerServices { @@ -132,11 +130,6 @@ } @Override - public MethodHandle asTypeLosslessReturn(final MethodHandle handle, final MethodType fromType) { - return Implementation.asTypeLosslessReturn(this, handle, fromType); - } - - @Override public MethodHandle getTypeConverter(final Class sourceType, final Class targetType) { return typeConverterFactory.getTypeConverter(sourceType, targetType); } diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/Lookup.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/Lookup.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/Lookup.java Wed Jul 05 20:54:48 2017 +0200 @@ -93,8 +93,6 @@ /** * A wrapper around MethodHandles.Lookup that masks checked exceptions in those cases when you're looking up methods * within your own codebase (therefore it is an error if they are not present). - * - * @author Attila Szegedi */ public class Lookup { private final MethodHandles.Lookup lookup; diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/LookupCallSiteDescriptor.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/LookupCallSiteDescriptor.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/LookupCallSiteDescriptor.java Wed Jul 05 20:54:48 2017 +0200 @@ -89,7 +89,6 @@ /** * A call site descriptor that stores a specific {@link Lookup}. It does not, however, store static bootstrap arguments. - * @author Attila Szegedi */ class LookupCallSiteDescriptor extends DefaultCallSiteDescriptor { private final Lookup lookup; diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/NameCodec.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/NameCodec.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/NameCodec.java Wed Jul 05 20:54:48 2017 +0200 @@ -99,8 +99,6 @@ * have your own way of creating call site descriptors, but you still delegate to this method of the default factory * (it is recommended that you do), then you have demangling handled for you already, and only need to ensure that you * mangle the names when you're emitting them in the bytecode. - * - * @author Attila Szegedi */ public class NameCodec { private static final char ESCAPE_CHAR = '\\'; diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/RuntimeContextLinkRequestImpl.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/RuntimeContextLinkRequestImpl.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/RuntimeContextLinkRequestImpl.java Wed Jul 05 20:54:48 2017 +0200 @@ -89,8 +89,6 @@ /** * A link request implementation for call sites that pass language runtime specific context arguments on the stack. The * context specific arguments should be the first "n" arguments. - * - * @author Attila Szegedi */ public class RuntimeContextLinkRequestImpl extends LinkRequestImpl { diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/TypeConverterFactory.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/TypeConverterFactory.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/TypeConverterFactory.java Wed Jul 05 20:54:48 2017 +0200 @@ -103,8 +103,6 @@ * A factory for type converters. This class is the main implementation behind the * {@link LinkerServices#asType(MethodHandle, MethodType)}. It manages the known {@link GuardingTypeConverterFactory} * instances and creates appropriate converters for method handles. - * - * @author Attila Szegedi */ public class TypeConverterFactory { diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/TypeUtilities.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/TypeUtilities.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/TypeUtilities.java Wed Jul 05 20:54:48 2017 +0200 @@ -96,8 +96,6 @@ /** * Various static utility methods for testing type relationships. - * - * @author Attila Szegedi */ public class TypeUtilities { static final Class OBJECT_CLASS = Object.class; diff -r 9d153359b8ff -r 470068717ae2 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java Wed Oct 21 15:15:26 2015 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java Wed Jul 05 20:54:48 2017 +0200 @@ -211,11 +211,6 @@ } @Override - public MethodHandle asTypeLosslessReturn(final MethodHandle handle, final MethodType fromType) { - return Implementation.asTypeLosslessReturn(this, handle, fromType); - } - - @Override public MethodHandle getTypeConverter(final Class sourceType, final Class targetType) { return linkerServices.getTypeConverter(sourceType, targetType); }