--- a/jdk/src/share/classes/java/lang/Class.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/java/lang/Class.java Wed Jul 03 14:35:10 2013 -0700
@@ -360,36 +360,24 @@
* any exception thrown by the constructor in a (checked) {@link
* java.lang.reflect.InvocationTargetException}.
*
- * @return a newly allocated instance of the class represented by this
- * object.
- * @exception IllegalAccessException if the class or its nullary
- * constructor is not accessible.
- * @exception InstantiationException
- * if this {@code Class} represents an abstract class,
- * an interface, an array class, a primitive type, or void;
- * or if the class has no nullary constructor;
- * or if the instantiation fails for some other reason.
- * @exception ExceptionInInitializerError if the initialization
- * provoked by this method fails.
- * @exception SecurityException
- * If a security manager, <i>s</i>, is present and any of the
- * following conditions is met:
- *
- * <ul>
- *
- * <li> invocation of
- * {@link SecurityManager#checkMemberAccess
- * s.checkMemberAccess(this, Member.PUBLIC)} denies
- * creation of new instances of this class
- *
- * <li> the caller's class loader is not the same as or an
- * ancestor of the class loader for the current class and
- * invocation of {@link SecurityManager#checkPackageAccess
- * s.checkPackageAccess()} denies access to the package
- * of this class
- *
- * </ul>
- *
+ * @return a newly allocated instance of the class represented by this
+ * object.
+ * @throws IllegalAccessException if the class or its nullary
+ * constructor is not accessible.
+ * @throws InstantiationException
+ * if this {@code Class} represents an abstract class,
+ * an interface, an array class, a primitive type, or void;
+ * or if the class has no nullary constructor;
+ * or if the instantiation fails for some other reason.
+ * @throws ExceptionInInitializerError if the initialization
+ * provoked by this method fails.
+ * @throws SecurityException
+ * If a security manager, <i>s</i>, is present and
+ * the caller's class loader is not the same as or an
+ * ancestor of the class loader for the current class and
+ * invocation of {@link SecurityManager#checkPackageAccess
+ * s.checkPackageAccess()} denies access to the package
+ * of this class.
*/
@CallerSensitive
public T newInstance()
@@ -981,24 +969,27 @@
*
* @return the immediately enclosing method of the underlying class, if
* that class is a local or anonymous class; otherwise {@code null}.
- * @exception SecurityException
- * If a security manager, <i>s</i>, is present and any of the
- * following conditions is met:
*
- * <ul>
+ * @throws SecurityException
+ * If a security manager, <i>s</i>, is present and any of the
+ * following conditions is met:
+ *
+ * <ul>
*
- * <li> invocation of
- * {@link SecurityManager#checkMemberAccess
- * s.checkMemberAccess(enclosingClass, Member.DECLARED)} denies
- * access to the methods within the enclosing class
+ * <li> the caller's class loader is not the same as the
+ * class loader of the enclosing class and invocation of
+ * {@link SecurityManager#checkPermission
+ * s.checkPermission} method with
+ * {@code RuntimePermission("accessDeclaredMembers")}
+ * denies access to the methods within the enclosing class
*
- * <li> the caller's class loader is not the same as or an
- * ancestor of the class loader for the enclosing class and
- * invocation of {@link SecurityManager#checkPackageAccess
- * s.checkPackageAccess()} denies access to the package
- * of the enclosing class
+ * <li> the caller's class loader is not the same as or an
+ * ancestor of the class loader for the enclosing class and
+ * invocation of {@link SecurityManager#checkPackageAccess
+ * s.checkPackageAccess()} denies access to the package
+ * of the enclosing class
*
- * </ul>
+ * </ul>
* @since 1.5
*/
@CallerSensitive
@@ -1025,11 +1016,6 @@
// Perform access check
Class<?> enclosingCandidate = enclosingInfo.getEnclosingClass();
- // be very careful not to change the stack depth of this
- // checkMemberAccess call for security reasons
- // see java.lang.SecurityManager.checkMemberAccess
- //
- // Note that we need to do this on the enclosing class
enclosingCandidate.checkMemberAccess(Member.DECLARED,
Reflection.getCallerClass(), true);
/*
@@ -1137,24 +1123,26 @@
*
* @return the immediately enclosing constructor of the underlying class, if
* that class is a local or anonymous class; otherwise {@code null}.
- * @exception SecurityException
- * If a security manager, <i>s</i>, is present and any of the
- * following conditions is met:
+ * @throws SecurityException
+ * If a security manager, <i>s</i>, is present and any of the
+ * following conditions is met:
*
- * <ul>
+ * <ul>
*
- * <li> invocation of
- * {@link SecurityManager#checkMemberAccess
- * s.checkMemberAccess(enclosingClass, Member.DECLARED)} denies
- * access to the constructors within the enclosing class
+ * <li> the caller's class loader is not the same as the
+ * class loader of the enclosing class and invocation of
+ * {@link SecurityManager#checkPermission
+ * s.checkPermission} method with
+ * {@code RuntimePermission("accessDeclaredMembers")}
+ * denies access to the constructors within the enclosing class
*
- * <li> the caller's class loader is not the same as or an
- * ancestor of the class loader for the enclosing class and
- * invocation of {@link SecurityManager#checkPackageAccess
- * s.checkPackageAccess()} denies access to the package
- * of the enclosing class
+ * <li> the caller's class loader is not the same as or an
+ * ancestor of the class loader for the enclosing class and
+ * invocation of {@link SecurityManager#checkPackageAccess
+ * s.checkPackageAccess()} denies access to the package
+ * of the enclosing class
*
- * </ul>
+ * </ul>
* @since 1.5
*/
@CallerSensitive
@@ -1180,11 +1168,6 @@
// Perform access check
Class<?> enclosingCandidate = enclosingInfo.getEnclosingClass();
- // be very careful not to change the stack depth of this
- // checkMemberAccess call for security reasons
- // see java.lang.SecurityManager.checkMemberAccess
- //
- // Note that we need to do this on the enclosing class
enclosingCandidate.checkMemberAccess(Member.DECLARED,
Reflection.getCallerClass(), true);
/*
@@ -1457,25 +1440,14 @@
* class, or void.
*
* @return the array of {@code Class} objects representing the public
- * members of this class
- * @exception SecurityException
- * If a security manager, <i>s</i>, is present and any of the
- * following conditions is met:
- *
- * <ul>
- *
- * <li> invocation of
- * {@link SecurityManager#checkMemberAccess
- * s.checkMemberAccess(this, Member.PUBLIC)} method
- * denies access to the classes within this class
- *
- * <li> the caller's class loader is not the same as or an
- * ancestor of the class loader for the current class and
- * invocation of {@link SecurityManager#checkPackageAccess
- * s.checkPackageAccess()} denies access to the package
- * of this class
- *
- * </ul>
+ * members of this class
+ * @throws SecurityException
+ * If a security manager, <i>s</i>, is present and
+ * the caller's class loader is not the same as or an
+ * ancestor of the class loader for the current class and
+ * invocation of {@link SecurityManager#checkPackageAccess
+ * s.checkPackageAccess()} denies access to the package
+ * of this class.
*
* @since JDK1.1
*/
@@ -1530,25 +1502,14 @@
* <p> See <em>The Java Language Specification</em>, sections 8.2 and 8.3.
*
* @return the array of {@code Field} objects representing the
- * public fields
- * @exception SecurityException
- * If a security manager, <i>s</i>, is present and any of the
- * following conditions is met:
- *
- * <ul>
- *
- * <li> invocation of
- * {@link SecurityManager#checkMemberAccess
- * s.checkMemberAccess(this, Member.PUBLIC)} denies
- * access to the fields within this class
- *
- * <li> the caller's class loader is not the same as or an
- * ancestor of the class loader for the current class and
- * invocation of {@link SecurityManager#checkPackageAccess
- * s.checkPackageAccess()} denies access to the package
- * of this class
- *
- * </ul>
+ * public fields
+ * @throws SecurityException
+ * If a security manager, <i>s</i>, is present and
+ * the caller's class loader is not the same as or an
+ * ancestor of the class loader for the current class and
+ * invocation of {@link SecurityManager#checkPackageAccess
+ * s.checkPackageAccess()} denies access to the package
+ * of this class.
*
* @since JDK1.1
*/
@@ -1579,25 +1540,14 @@
* <p> See <em>The Java Language Specification</em>, sections 8.2 and 8.4.
*
* @return the array of {@code Method} objects representing the
- * public methods of this class
- * @exception SecurityException
- * If a security manager, <i>s</i>, is present and any of the
- * following conditions is met:
- *
- * <ul>
- *
- * <li> invocation of
- * {@link SecurityManager#checkMemberAccess
- * s.checkMemberAccess(this, Member.PUBLIC)} denies
- * access to the methods within this class
- *
- * <li> the caller's class loader is not the same as or an
- * ancestor of the class loader for the current class and
- * invocation of {@link SecurityManager#checkPackageAccess
- * s.checkPackageAccess()} denies access to the package
- * of this class
- *
- * </ul>
+ * public methods of this class
+ * @throws SecurityException
+ * If a security manager, <i>s</i>, is present and
+ * the caller's class loader is not the same as or an
+ * ancestor of the class loader for the current class and
+ * invocation of {@link SecurityManager#checkPackageAccess
+ * s.checkPackageAccess()} denies access to the package
+ * of this class.
*
* @since JDK1.1
*/
@@ -1626,25 +1576,14 @@
* {@code Constructor<T>[]}.
*
* @return the array of {@code Constructor} objects representing the
- * public constructors of this class
- * @exception SecurityException
- * If a security manager, <i>s</i>, is present and any of the
- * following conditions is met:
- *
- * <ul>
- *
- * <li> invocation of
- * {@link SecurityManager#checkMemberAccess
- * s.checkMemberAccess(this, Member.PUBLIC)} denies
- * access to the constructors within this class
- *
- * <li> the caller's class loader is not the same as or an
- * ancestor of the class loader for the current class and
- * invocation of {@link SecurityManager#checkPackageAccess
- * s.checkPackageAccess()} denies access to the package
- * of this class
- *
- * </ul>
+ * public constructors of this class
+ * @throws SecurityException
+ * If a security manager, <i>s</i>, is present and
+ * the caller's class loader is not the same as or an
+ * ancestor of the class loader for the current class and
+ * invocation of {@link SecurityManager#checkPackageAccess
+ * s.checkPackageAccess()} denies access to the package
+ * of this class.
*
* @since JDK1.1
*/
@@ -1678,29 +1617,18 @@
* <p> See <em>The Java Language Specification</em>, sections 8.2 and 8.3.
*
* @param name the field name
- * @return the {@code Field} object of this class specified by
- * {@code name}
- * @exception NoSuchFieldException if a field with the specified name is
- * not found.
- * @exception NullPointerException if {@code name} is {@code null}
- * @exception SecurityException
- * If a security manager, <i>s</i>, is present and any of the
- * following conditions is met:
- *
- * <ul>
- *
- * <li> invocation of
- * {@link SecurityManager#checkMemberAccess
- * s.checkMemberAccess(this, Member.PUBLIC)} denies
- * access to the field
- *
- * <li> the caller's class loader is not the same as or an
- * ancestor of the class loader for the current class and
- * invocation of {@link SecurityManager#checkPackageAccess
- * s.checkPackageAccess()} denies access to the package
- * of this class
- *
- * </ul>
+ * @return the {@code Field} object of this class specified by
+ * {@code name}
+ * @throws NoSuchFieldException if a field with the specified name is
+ * not found.
+ * @throws NullPointerException if {@code name} is {@code null}
+ * @throws SecurityException
+ * If a security manager, <i>s</i>, is present and
+ * the caller's class loader is not the same as or an
+ * ancestor of the class loader for the current class and
+ * invocation of {@link SecurityManager#checkPackageAccess
+ * s.checkPackageAccess()} denies access to the package
+ * of this class.
*
* @since JDK1.1
*/
@@ -1762,28 +1690,17 @@
* @param name the name of the method
* @param parameterTypes the list of parameters
* @return the {@code Method} object that matches the specified
- * {@code name} and {@code parameterTypes}
- * @exception NoSuchMethodException if a matching method is not found
- * or if the name is "<init>"or "<clinit>".
- * @exception NullPointerException if {@code name} is {@code null}
- * @exception SecurityException
- * If a security manager, <i>s</i>, is present and any of the
- * following conditions is met:
- *
- * <ul>
- *
- * <li> invocation of
- * {@link SecurityManager#checkMemberAccess
- * s.checkMemberAccess(this, Member.PUBLIC)} denies
- * access to the method
- *
- * <li> the caller's class loader is not the same as or an
- * ancestor of the class loader for the current class and
- * invocation of {@link SecurityManager#checkPackageAccess
- * s.checkPackageAccess()} denies access to the package
- * of this class
- *
- * </ul>
+ * {@code name} and {@code parameterTypes}
+ * @throws NoSuchMethodException if a matching method is not found
+ * or if the name is "<init>"or "<clinit>".
+ * @throws NullPointerException if {@code name} is {@code null}
+ * @throws SecurityException
+ * If a security manager, <i>s</i>, is present and
+ * the caller's class loader is not the same as or an
+ * ancestor of the class loader for the current class and
+ * invocation of {@link SecurityManager#checkPackageAccess
+ * s.checkPackageAccess()} denies access to the package
+ * of this class.
*
* @since JDK1.1
*/
@@ -1816,26 +1733,15 @@
*
* @param parameterTypes the parameter array
* @return the {@code Constructor} object of the public constructor that
- * matches the specified {@code parameterTypes}
- * @exception NoSuchMethodException if a matching method is not found.
- * @exception SecurityException
- * If a security manager, <i>s</i>, is present and any of the
- * following conditions is met:
- *
- * <ul>
- *
- * <li> invocation of
- * {@link SecurityManager#checkMemberAccess
- * s.checkMemberAccess(this, Member.PUBLIC)} denies
- * access to the constructor
- *
- * <li> the caller's class loader is not the same as or an
- * ancestor of the class loader for the current class and
- * invocation of {@link SecurityManager#checkPackageAccess
- * s.checkPackageAccess()} denies access to the package
- * of this class
- *
- * </ul>
+ * matches the specified {@code parameterTypes}
+ * @throws NoSuchMethodException if a matching method is not found.
+ * @throws SecurityException
+ * If a security manager, <i>s</i>, is present and
+ * the caller's class loader is not the same as or an
+ * ancestor of the class loader for the current class and
+ * invocation of {@link SecurityManager#checkPackageAccess
+ * s.checkPackageAccess()} denies access to the package
+ * of this class.
*
* @since JDK1.1
*/
@@ -1858,25 +1764,27 @@
* primitive type, an array class, or void.
*
* @return the array of {@code Class} objects representing all the
- * declared members of this class
- * @exception SecurityException
- * If a security manager, <i>s</i>, is present and any of the
- * following conditions is met:
+ * declared members of this class
+ * @throws SecurityException
+ * If a security manager, <i>s</i>, is present and any of the
+ * following conditions is met:
*
- * <ul>
+ * <ul>
*
- * <li> invocation of
- * {@link SecurityManager#checkMemberAccess
- * s.checkMemberAccess(this, Member.DECLARED)} denies
- * access to the declared classes within this class
+ * <li> the caller's class loader is not the same as the
+ * class loader of this class and invocation of
+ * {@link SecurityManager#checkPermission
+ * s.checkPermission} method with
+ * {@code RuntimePermission("accessDeclaredMembers")}
+ * denies access to the declared classes within this class
*
- * <li> the caller's class loader is not the same as or an
- * ancestor of the class loader for the current class and
- * invocation of {@link SecurityManager#checkPackageAccess
- * s.checkPackageAccess()} denies access to the package
- * of this class
+ * <li> the caller's class loader is not the same as or an
+ * ancestor of the class loader for the current class and
+ * invocation of {@link SecurityManager#checkPackageAccess
+ * s.checkPackageAccess()} denies access to the package
+ * of this class
*
- * </ul>
+ * </ul>
*
* @since JDK1.1
*/
@@ -1899,26 +1807,28 @@
*
* <p> See <em>The Java Language Specification</em>, sections 8.2 and 8.3.
*
- * @return the array of {@code Field} objects representing all the
- * declared fields of this class
- * @exception SecurityException
- * If a security manager, <i>s</i>, is present and any of the
- * following conditions is met:
+ * @return the array of {@code Field} objects representing all the
+ * declared fields of this class
+ * @throws SecurityException
+ * If a security manager, <i>s</i>, is present and any of the
+ * following conditions is met:
*
- * <ul>
+ * <ul>
*
- * <li> invocation of
- * {@link SecurityManager#checkMemberAccess
- * s.checkMemberAccess(this, Member.DECLARED)} denies
- * access to the declared fields within this class
+ * <li> the caller's class loader is not the same as the
+ * class loader of this class and invocation of
+ * {@link SecurityManager#checkPermission
+ * s.checkPermission} method with
+ * {@code RuntimePermission("accessDeclaredMembers")}
+ * denies access to the declared fields within this class
*
- * <li> the caller's class loader is not the same as or an
- * ancestor of the class loader for the current class and
- * invocation of {@link SecurityManager#checkPackageAccess
- * s.checkPackageAccess()} denies access to the package
- * of this class
+ * <li> the caller's class loader is not the same as or an
+ * ancestor of the class loader for the current class and
+ * invocation of {@link SecurityManager#checkPackageAccess
+ * s.checkPackageAccess()} denies access to the package
+ * of this class
*
- * </ul>
+ * </ul>
*
* @since JDK1.1
*/
@@ -1945,26 +1855,28 @@
*
* <p> See <em>The Java Language Specification</em>, section 8.2.
*
- * @return the array of {@code Method} objects representing all the
- * declared methods of this class
- * @exception SecurityException
- * If a security manager, <i>s</i>, is present and any of the
- * following conditions is met:
+ * @return the array of {@code Method} objects representing all the
+ * declared methods of this class
+ * @throws SecurityException
+ * If a security manager, <i>s</i>, is present and any of the
+ * following conditions is met:
*
- * <ul>
+ * <ul>
*
- * <li> invocation of
- * {@link SecurityManager#checkMemberAccess
- * s.checkMemberAccess(this, Member.DECLARED)} denies
- * access to the declared methods within this class
+ * <li> the caller's class loader is not the same as the
+ * class loader of this class and invocation of
+ * {@link SecurityManager#checkPermission
+ * s.checkPermission} method with
+ * {@code RuntimePermission("accessDeclaredMembers")}
+ * denies access to the declared methods within this class
*
- * <li> the caller's class loader is not the same as or an
- * ancestor of the class loader for the current class and
- * invocation of {@link SecurityManager#checkPackageAccess
- * s.checkPackageAccess()} denies access to the package
- * of this class
+ * <li> the caller's class loader is not the same as or an
+ * ancestor of the class loader for the current class and
+ * invocation of {@link SecurityManager#checkPackageAccess
+ * s.checkPackageAccess()} denies access to the package
+ * of this class
*
- * </ul>
+ * </ul>
*
* @since JDK1.1
*/
@@ -1988,26 +1900,28 @@
*
* <p> See <em>The Java Language Specification</em>, section 8.2.
*
- * @return the array of {@code Constructor} objects representing all the
- * declared constructors of this class
- * @exception SecurityException
- * If a security manager, <i>s</i>, is present and any of the
- * following conditions is met:
+ * @return the array of {@code Constructor} objects representing all the
+ * declared constructors of this class
+ * @throws SecurityException
+ * If a security manager, <i>s</i>, is present and any of the
+ * following conditions is met:
*
- * <ul>
+ * <ul>
*
- * <li> invocation of
- * {@link SecurityManager#checkMemberAccess
- * s.checkMemberAccess(this, Member.DECLARED)} denies
- * access to the declared constructors within this class
+ * <li> the caller's class loader is not the same as the
+ * class loader of this class and invocation of
+ * {@link SecurityManager#checkPermission
+ * s.checkPermission} method with
+ * {@code RuntimePermission("accessDeclaredMembers")}
+ * denies access to the declared constructors within this class
*
- * <li> the caller's class loader is not the same as or an
- * ancestor of the class loader for the current class and
- * invocation of {@link SecurityManager#checkPackageAccess
- * s.checkPackageAccess()} denies access to the package
- * of this class
+ * <li> the caller's class loader is not the same as or an
+ * ancestor of the class loader for the current class and
+ * invocation of {@link SecurityManager#checkPackageAccess
+ * s.checkPackageAccess()} denies access to the package
+ * of this class
*
- * </ul>
+ * </ul>
*
* @since JDK1.1
*/
@@ -2026,29 +1940,31 @@
* will not reflect the {@code length} field of an array class.
*
* @param name the name of the field
- * @return the {@code Field} object for the specified field in this
- * class
- * @exception NoSuchFieldException if a field with the specified name is
- * not found.
- * @exception NullPointerException if {@code name} is {@code null}
- * @exception SecurityException
- * If a security manager, <i>s</i>, is present and any of the
- * following conditions is met:
+ * @return the {@code Field} object for the specified field in this
+ * class
+ * @throws NoSuchFieldException if a field with the specified name is
+ * not found.
+ * @throws NullPointerException if {@code name} is {@code null}
+ * @throws SecurityException
+ * If a security manager, <i>s</i>, is present and any of the
+ * following conditions is met:
*
- * <ul>
+ * <ul>
*
- * <li> invocation of
- * {@link SecurityManager#checkMemberAccess
- * s.checkMemberAccess(this, Member.DECLARED)} denies
- * access to the declared field
+ * <li> the caller's class loader is not the same as the
+ * class loader of this class and invocation of
+ * {@link SecurityManager#checkPermission
+ * s.checkPermission} method with
+ * {@code RuntimePermission("accessDeclaredMembers")}
+ * denies access to the declared field
*
- * <li> the caller's class loader is not the same as or an
- * ancestor of the class loader for the current class and
- * invocation of {@link SecurityManager#checkPackageAccess
- * s.checkPackageAccess()} denies access to the package
- * of this class
+ * <li> the caller's class loader is not the same as or an
+ * ancestor of the class loader for the current class and
+ * invocation of {@link SecurityManager#checkPackageAccess
+ * s.checkPackageAccess()} denies access to the package
+ * of this class
*
- * </ul>
+ * </ul>
*
* @since JDK1.1
*/
@@ -2080,28 +1996,30 @@
*
* @param name the name of the method
* @param parameterTypes the parameter array
- * @return the {@code Method} object for the method of this class
- * matching the specified name and parameters
- * @exception NoSuchMethodException if a matching method is not found.
- * @exception NullPointerException if {@code name} is {@code null}
- * @exception SecurityException
- * If a security manager, <i>s</i>, is present and any of the
- * following conditions is met:
+ * @return the {@code Method} object for the method of this class
+ * matching the specified name and parameters
+ * @throws NoSuchMethodException if a matching method is not found.
+ * @throws NullPointerException if {@code name} is {@code null}
+ * @throws SecurityException
+ * If a security manager, <i>s</i>, is present and any of the
+ * following conditions is met:
*
- * <ul>
+ * <ul>
*
- * <li> invocation of
- * {@link SecurityManager#checkMemberAccess
- * s.checkMemberAccess(this, Member.DECLARED)} denies
- * access to the declared method
+ * <li> the caller's class loader is not the same as the
+ * class loader of this class and invocation of
+ * {@link SecurityManager#checkPermission
+ * s.checkPermission} method with
+ * {@code RuntimePermission("accessDeclaredMembers")}
+ * denies access to the declared method
*
- * <li> the caller's class loader is not the same as or an
- * ancestor of the class loader for the current class and
- * invocation of {@link SecurityManager#checkPackageAccess
- * s.checkPackageAccess()} denies access to the package
- * of this class
+ * <li> the caller's class loader is not the same as or an
+ * ancestor of the class loader for the current class and
+ * invocation of {@link SecurityManager#checkPackageAccess
+ * s.checkPackageAccess()} denies access to the package
+ * of this class
*
- * </ul>
+ * </ul>
*
* @since JDK1.1
*/
@@ -2129,27 +2047,29 @@
* include the explicit enclosing instance as the first parameter.
*
* @param parameterTypes the parameter array
- * @return The {@code Constructor} object for the constructor with the
- * specified parameter list
- * @exception NoSuchMethodException if a matching method is not found.
- * @exception SecurityException
- * If a security manager, <i>s</i>, is present and any of the
- * following conditions is met:
+ * @return The {@code Constructor} object for the constructor with the
+ * specified parameter list
+ * @throws NoSuchMethodException if a matching method is not found.
+ * @throws SecurityException
+ * If a security manager, <i>s</i>, is present and any of the
+ * following conditions is met:
*
- * <ul>
+ * <ul>
*
- * <li> invocation of
- * {@link SecurityManager#checkMemberAccess
- * s.checkMemberAccess(this, Member.DECLARED)} denies
- * access to the declared constructor
+ * <li> the caller's class loader is not the same as the
+ * class loader of this class and invocation of
+ * {@link SecurityManager#checkPermission
+ * s.checkPermission} method with
+ * {@code RuntimePermission("accessDeclaredMembers")}
+ * denies access to the declared constructor
*
- * <li> the caller's class loader is not the same as or an
- * ancestor of the class loader for the current class and
- * invocation of {@link SecurityManager#checkPackageAccess
- * s.checkPackageAccess()} denies access to the package
- * of this class
+ * <li> the caller's class loader is not the same as or an
+ * ancestor of the class loader for the current class and
+ * invocation of {@link SecurityManager#checkPackageAccess
+ * s.checkPackageAccess()} denies access to the package
+ * of this class
*
- * </ul>
+ * </ul>
*
* @since JDK1.1
*/
@@ -2306,14 +2226,6 @@
*/
static native Class<?> getPrimitiveClass(String name);
- private static boolean isCheckMemberAccessOverridden(SecurityManager smgr) {
- if (smgr.getClass() == SecurityManager.class) return false;
-
- Class<?>[] paramTypes = new Class<?>[] {Class.class, int.class};
- return smgr.getClass().getMethod0("checkMemberAccess", paramTypes).
- getDeclaringClass() != SecurityManager.class;
- }
-
/*
* Check if client is allowed to access members. If access is denied,
* throw a SecurityException.
@@ -2326,19 +2238,17 @@
private void checkMemberAccess(int which, Class<?> caller, boolean checkProxyInterfaces) {
final SecurityManager s = System.getSecurityManager();
if (s != null) {
+ /* Default policy allows access to all {@link Member#PUBLIC} members,
+ * as well as access to classes that have the same class loader as the caller.
+ * In all other cases, it requires RuntimePermission("accessDeclaredMembers")
+ * permission.
+ */
final ClassLoader ccl = ClassLoader.getClassLoader(caller);
final ClassLoader cl = getClassLoader0();
- if (!isCheckMemberAccessOverridden(s)) {
- // Inlined SecurityManager.checkMemberAccess
- if (which != Member.PUBLIC) {
- if (ccl != cl) {
- s.checkPermission(SecurityConstants.CHECK_MEMBER_ACCESS_PERMISSION);
- }
+ if (which != Member.PUBLIC) {
+ if (ccl != cl) {
+ s.checkPermission(SecurityConstants.CHECK_MEMBER_ACCESS_PERMISSION);
}
- } else {
- // Don't refactor; otherwise break the stack depth for
- // checkMemberAccess of subclasses of SecurityManager as specified.
- s.checkMemberAccess(this, which);
}
this.checkPackageAccess(ccl, checkProxyInterfaces);
}
--- a/jdk/src/share/classes/java/lang/SecurityManager.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/java/lang/SecurityManager.java Wed Jul 03 14:35:10 2013 -0700
@@ -1675,10 +1675,18 @@
* permission to access members.
* @exception NullPointerException if the <code>clazz</code> argument is
* <code>null</code>.
+ *
+ * @deprecated This method relies on the caller being at a stack depth
+ * of 4 which is error-prone and cannot be enforced by the runtime.
+ * Users of this method should instead invoke {@link #checkPermission}
+ * directly. This method will be changed in a future release
+ * to check the permission {@code java.security.AllPermission}.
+ *
* @see java.lang.reflect.Member
* @since JDK1.1
* @see #checkPermission(java.security.Permission) checkPermission
*/
+ @Deprecated
@CallerSensitive
public void checkMemberAccess(Class<?> clazz, int which) {
if (clazz == null) {
--- a/jdk/src/share/classes/java/lang/instrument/Instrumentation.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/java/lang/instrument/Instrumentation.java Wed Jul 03 14:35:10 2013 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 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
@@ -363,6 +363,8 @@
* Primitive classes (for example, <code>java.lang.Integer.TYPE</code>)
* and array classes are never modifiable.
*
+ * @param theClass the class to check for being modifiable
+ * @return whether or not the argument class is modifiable
* @throws java.lang.NullPointerException if the specified class is <code>null</code>.
*
* @see #retransformClasses
@@ -549,14 +551,14 @@
* {@link java.lang.instrument.ClassFileTransformer ClassFileTransformer},
* it enables native methods to be
* instrumented.
- * <p/>
+ * <p>
* Since native methods cannot be directly instrumented
* (they have no bytecodes), they must be wrapped with
* a non-native method which can be instrumented.
* For example, if we had:
* <pre>
* native boolean foo(int x);</pre>
- * <p/>
+ * <p>
* We could transform the class file (with the
* ClassFileTransformer during the initial definition
* of the class) so that this becomes:
@@ -567,14 +569,14 @@
* }
*
* native boolean wrapped_foo(int x);</pre>
- * <p/>
+ * <p>
* Where <code>foo</code> becomes a wrapper for the actual native
* method with the appended prefix "wrapped_". Note that
* "wrapped_" would be a poor choice of prefix since it
* might conceivably form the name of an existing method
* thus something like "$$$MyAgentWrapped$$$_" would be
* better but would make these examples less readable.
- * <p/>
+ * <p>
* The wrapper will allow data to be collected on the native
* method call, but now the problem becomes linking up the
* wrapped method with the native implementation.
@@ -583,7 +585,7 @@
* which might be:
* <pre>
* Java_somePackage_someClass_foo(JNIEnv* env, jint x)</pre>
- * <p/>
+ * <p>
* This function allows the prefix to be specified and the
* proper resolution to occur.
* Specifically, when the standard resolution fails, the
@@ -596,29 +598,29 @@
* <pre>{@code
* method(foo) -> nativeImplementation(foo)
* }</pre>
- * <p/>
+ * <p>
* When this fails, the resolution will be retried with
* the specified prefix prepended to the method name,
* yielding the correct resolution:
* <pre>{@code
* method(wrapped_foo) -> nativeImplementation(foo)
* }</pre>
- * <p/>
+ * <p>
* For automatic resolution, the JVM will attempt:
* <pre>{@code
* method(wrapped_foo) -> nativeImplementation(wrapped_foo)
* }</pre>
- * <p/>
+ * <p>
* When this fails, the resolution will be retried with
* the specified prefix deleted from the implementation name,
* yielding the correct resolution:
* <pre>{@code
* method(wrapped_foo) -> nativeImplementation(foo)
* }</pre>
- * <p/>
+ * <p>
* Note that since the prefix is only used when standard
* resolution fails, native methods can be wrapped selectively.
- * <p/>
+ * <p>
* Since each <code>ClassFileTransformer</code>
* can do its own transformation of the bytecodes, more
* than one layer of wrappers may be applied. Thus each
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandleImpl.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandleImpl.java Wed Jul 03 14:35:10 2013 -0700
@@ -747,7 +747,8 @@
GuardWithCatch gguard = new GuardWithCatch(gtarget, exType, gcatcher);
if (gtarget == null || gcatcher == null) throw new InternalError();
MethodHandle ginvoker = GuardWithCatch.VARARGS_INVOKE.bindReceiver(gguard);
- return makeCollectArguments(ginvoker, ValueConversions.varargsArray(nargs), 0, false);
+ MethodHandle gcollect = makeCollectArguments(ginvoker, ValueConversions.varargsArray(nargs), 0, false);
+ return makePairwiseConvert(gcollect, type, 2);
}
}
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandles.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandles.java Wed Jul 03 14:35:10 2013 -0700
@@ -41,6 +41,7 @@
import sun.security.util.SecurityConstants;
import static java.lang.invoke.MethodHandleStatics.*;
import static java.lang.invoke.MethodHandleNatives.Constants.*;
+import sun.security.util.SecurityConstants;
/**
* This class consists exclusively of static methods that operate on or return
@@ -305,36 +306,30 @@
* <a name="secmgr"></a>
* If a security manager is present, member lookups are subject to
* additional checks.
- * From one to four calls are made to the security manager.
+ * From one to three calls are made to the security manager.
* Any of these calls can refuse access by throwing a
* {@link java.lang.SecurityException SecurityException}.
* Define {@code smgr} as the security manager,
+ * {@code lookc} as the lookup class of the current lookup object,
* {@code refc} as the containing class in which the member
* is being sought, and {@code defc} as the class in which the
* member is actually defined.
+ * The value {@code lookc} is defined as <em>not present</em>
+ * if the current lookup object does not have
+ * {@linkplain java.lang.invoke.MethodHandles.Lookup#PRIVATE private access}.
* The calls are made according to the following rules:
* <ul>
- * <li>In all cases, {@link SecurityManager#checkMemberAccess
- * smgr.checkMemberAccess(refc, Member.PUBLIC)} is called.
- * <li>If the class loader of the lookup class is not
+ * <li>If {@code lookc} is not present, or if its class loader is not
* the same as or an ancestor of the class loader of {@code refc},
* then {@link SecurityManager#checkPackageAccess
* smgr.checkPackageAccess(refcPkg)} is called,
* where {@code refcPkg} is the package of {@code refc}.
+ * <li>If the retrieved member is not public and
+ * {@code lookc} is not present, then
+ * {@link SecurityManager#checkPermission smgr.checkPermission}
+ * with {@code RuntimePermission("accessDeclaredMembers")} is called.
* <li>If the retrieved member is not public,
- * {@link SecurityManager#checkMemberAccess
- * smgr.checkMemberAccess(defc, Member.DECLARED)} is called.
- * (Note that {@code defc} might be the same as {@code refc}.)
- * The default implementation of this security manager method
- * inspects the stack to determine the original caller of
- * the reflective request (such as {@code findStatic}),
- * and performs additional permission checks if the
- * class loader of {@code defc} differs from the class
- * loader of the class from which the reflective request came.
- * <li>If the retrieved member is not public,
- * and if {@code defc} and {@code refc} are in different class loaders,
- * and if the class loader of the lookup class is not
- * the same as or an ancestor of the class loader of {@code defc},
+ * and if {@code defc} and {@code refc} are different,
* then {@link SecurityManager#checkPackageAccess
* smgr.checkPackageAccess(defcPkg)} is called,
* where {@code defcPkg} is the package of {@code defc}.
@@ -1054,22 +1049,6 @@
}
/**
- * Determine whether a security manager has an overridden
- * SecurityManager.checkMemberAccess method.
- */
- private boolean isCheckMemberAccessOverridden(SecurityManager sm) {
- final Class<? extends SecurityManager> cls = sm.getClass();
- if (cls == SecurityManager.class) return false;
-
- try {
- return cls.getMethod("checkMemberAccess", Class.class, int.class).
- getDeclaringClass() != SecurityManager.class;
- } catch (NoSuchMethodException e) {
- throw new InternalError("should not reach here");
- }
- }
-
- /**
* Perform necessary <a href="MethodHandles.Lookup.html#secmgr">access checks</a>.
* Determines a trustable caller class to compare with refc, the symbolic reference class.
* If this lookup object has private access, then the caller class is the lookupClass.
@@ -1079,45 +1058,22 @@
if (smgr == null) return;
if (allowedModes == TRUSTED) return;
- final boolean overridden = isCheckMemberAccessOverridden(smgr);
// Step 1:
- {
- // Default policy is to allow Member.PUBLIC; no need to check
- // permission if SecurityManager is the default implementation
- final int which = Member.PUBLIC;
- final Class<?> clazz = refc;
- if (overridden) {
- // Don't refactor; otherwise break the stack depth for
- // checkMemberAccess of subclasses of SecurityManager as specified.
- smgr.checkMemberAccess(clazz, which);
- }
- }
-
- // Step 2:
if (!isFullPowerLookup() ||
!VerifyAccess.classLoaderIsAncestor(lookupClass, refc)) {
ReflectUtil.checkPackageAccess(refc);
}
- // Step 3:
+ // Step 2:
if (m.isPublic()) return;
Class<?> defc = m.getDeclaringClass();
{
- // Inline SecurityManager.checkMemberAccess
- final int which = Member.DECLARED;
- final Class<?> clazz = defc;
- if (!overridden) {
- if (!isFullPowerLookup()) {
- smgr.checkPermission(SecurityConstants.CHECK_MEMBER_ACCESS_PERMISSION);
- }
- } else {
- // Don't refactor; otherwise break the stack depth for
- // checkMemberAccess of subclasses of SecurityManager as specified.
- smgr.checkMemberAccess(clazz, which);
+ if (!isFullPowerLookup()) {
+ smgr.checkPermission(SecurityConstants.CHECK_MEMBER_ACCESS_PERMISSION);
}
}
- // Step 4:
+ // Step 3:
if (defc != refc) {
ReflectUtil.checkPackageAccess(defc);
}
--- a/jdk/src/share/classes/java/lang/reflect/Member.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/java/lang/reflect/Member.java Wed Jul 03 14:35:10 2013 -0700
@@ -42,14 +42,12 @@
/**
* Identifies the set of all public members of a class or interface,
* including inherited members.
- * @see java.lang.SecurityManager#checkMemberAccess
*/
public static final int PUBLIC = 0;
/**
* Identifies the set of declared members of a class or interface.
* Inherited members are not included.
- * @see java.lang.SecurityManager#checkMemberAccess
*/
public static final int DECLARED = 1;
--- a/jdk/src/share/classes/java/math/BigInteger.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/java/math/BigInteger.java Wed Jul 03 14:35:10 2013 -0700
@@ -1042,7 +1042,7 @@
* recalculate powers of radix^(2^n) more than once. This speeds
* Schoenhage recursive base conversion significantly.
*/
- private static ArrayList<BigInteger>[] powerCache;
+ private static volatile BigInteger[][] powerCache;
/** The cache of logarithms of radices for base conversion. */
private static final double[] logCache;
@@ -1063,14 +1063,12 @@
* with just the very first value. Additional values will be created
* on demand.
*/
- powerCache = (ArrayList<BigInteger>[])
- new ArrayList[Character.MAX_RADIX+1];
+ powerCache = new BigInteger[Character.MAX_RADIX+1][];
logCache = new double[Character.MAX_RADIX+1];
for (int i=Character.MIN_RADIX; i<=Character.MAX_RADIX; i++)
{
- powerCache[i] = new ArrayList<BigInteger>(1);
- powerCache[i].add(BigInteger.valueOf(i));
+ powerCache[i] = new BigInteger[] { BigInteger.valueOf(i) };
logCache[i] = Math.log(i);
}
}
@@ -3454,22 +3452,25 @@
* This could be changed to a more complicated caching method using
* <code>Future</code>.
*/
- private static synchronized BigInteger getRadixConversionCache(int radix,
- int exponent) {
- BigInteger retVal = null;
- ArrayList<BigInteger> cacheLine = powerCache[radix];
- int oldSize = cacheLine.size();
- if (exponent >= oldSize) {
- cacheLine.ensureCapacity(exponent+1);
- for (int i=oldSize; i<=exponent; i++) {
- retVal = cacheLine.get(i-1).square();
- cacheLine.add(i, retVal);
- }
+ private static BigInteger getRadixConversionCache(int radix, int exponent) {
+ BigInteger[] cacheLine = powerCache[radix]; // volatile read
+ if (exponent < cacheLine.length) {
+ return cacheLine[exponent];
}
- else
- retVal = cacheLine.get(exponent);
-
- return retVal;
+
+ int oldLength = cacheLine.length;
+ cacheLine = Arrays.copyOf(cacheLine, exponent + 1);
+ for (int i = oldLength; i <= exponent; i++) {
+ cacheLine[i] = cacheLine[i - 1].pow(2);
+ }
+
+ BigInteger[][] pc = powerCache; // volatile read again
+ if (exponent >= pc[radix].length) {
+ pc = pc.clone();
+ pc[radix] = cacheLine;
+ powerCache = pc; // volatile write, publish
+ }
+ return cacheLine[exponent];
}
/* zero[i] is a string of i consecutive zeros. */
--- a/jdk/src/share/classes/java/security/KeyStore.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/java/security/KeyStore.java Wed Jul 03 14:35:10 2013 -0700
@@ -227,15 +227,13 @@
* {@link #store(KeyStore.LoadStoreParameter) store} operations.
* <p>
* The following syntax is supported for configuration data:
- * <pre>
- *
+ * <pre>{@code
* domain <domainName> [<property> ...] {
* keystore <keystoreName> [<property> ...] ;
* ...
* };
* ...
- *
- * </pre>
+ * }</pre>
* where {@code domainName} and {@code keystoreName} are identifiers
* and {@code property} is a key/value pairing. The key and value are
* separated by an 'equals' symbol and the value is enclosed in double
--- a/jdk/src/share/classes/java/security/Provider.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/java/security/Provider.java Wed Jul 03 14:35:10 2013 -0700
@@ -67,6 +67,7 @@
* or modified by applications.
* The following attributes are automatically placed in each Provider object:
* <table cellspacing=4>
+ * <caption><b>Attributes Automatically Placed in a Provider Object</b></caption>
* <tr><th>Name</th><th>Value</th>
* <tr><td>{@code Provider.id name}</td>
* <td>{@code String.valueOf(provider.getName())}</td>
--- a/jdk/src/share/classes/java/security/Security.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/java/security/Security.java Wed Jul 03 14:35:10 2013 -0700
@@ -480,8 +480,8 @@
* Returns an array containing all installed providers that satisfy the
* specified selection criterion, or null if no such providers have been
* installed. The returned providers are ordered
- * according to their <a href=
- * "#insertProviderAt(java.security.Provider, int)">preference order</a>.
+ * according to their
+ * {@linkplain #insertProviderAt(java.security.Provider, int) preference order}.
*
* <p> A cryptographic service is always associated with a particular
* algorithm or type. For example, a digital signature service is
@@ -492,8 +492,8 @@
* <p>The selection criterion must be specified in one of the following two
* formats:
* <ul>
- * <li> <i><crypto_service>.<algorithm_or_type></i> <p> The
- * cryptographic service name must not contain any dots.
+ * <li> <i>{@literal <crypto_service>.<algorithm_or_type>}</i>
+ * <p> The cryptographic service name must not contain any dots.
* <p> A
* provider satisfies the specified selection criterion iff the provider
* implements the
@@ -501,11 +501,12 @@
* <p> For example, "CertificateFactory.X.509"
* would be satisfied by any provider that supplied
* a CertificateFactory implementation for X.509 certificates.
- * <li> <i><crypto_service>.<algorithm_or_type>
- * <attribute_name>:< attribute_value></i>
+ * <li> <i>{@literal <crypto_service>.<algorithm_or_type>
+ * <attribute_name>:<attribute_value>}</i>
* <p> The cryptographic service name must not contain any dots. There
* must be one or more space charaters between the
- * <i><algorithm_or_type></i> and the <i><attribute_name></i>.
+ * <i>{@literal <algorithm_or_type>}</i> and the
+ * <i>{@literal <attribute_name>}</i>.
* <p> A provider satisfies this selection criterion iff the
* provider implements the specified algorithm or type for the specified
* cryptographic service and its implementation meets the
@@ -558,8 +559,9 @@
* Returns an array containing all installed providers that satisfy the
* specified* selection criteria, or null if no such providers have been
* installed. The returned providers are ordered
- * according to their <a href=
- * "#insertProviderAt(java.security.Provider, int)">preference order</a>.
+ * according to their
+ * {@linkplain #insertProviderAt(java.security.Provider, int)
+ * preference order}.
*
* <p>The selection criteria are represented by a map.
* Each map entry represents a selection criterion.
@@ -567,16 +569,18 @@
* criteria. The key for any entry in such a map must be in one of the
* following two formats:
* <ul>
- * <li> <i><crypto_service>.<algorithm_or_type></i>
+ * <li> <i>{@literal <crypto_service>.<algorithm_or_type>}</i>
* <p> The cryptographic service name must not contain any dots.
* <p> The value associated with the key must be an empty string.
* <p> A provider
* satisfies this selection criterion iff the provider implements the
* specified algorithm or type for the specified cryptographic service.
- * <li> <i><crypto_service>.<algorithm_or_type> <attribute_name></i>
+ * <li> <i>{@literal <crypto_service>}.
+ * {@literal <algorithm_or_type> <attribute_name>}</i>
* <p> The cryptographic service name must not contain any dots. There
- * must be one or more space charaters between the <i><algorithm_or_type></i>
- * and the <i><attribute_name></i>.
+ * must be one or more space charaters between the
+ * <i>{@literal <algorithm_or_type>}</i>
+ * and the <i>{@literal <attribute_name>}</i>.
* <p> The value associated with the key must be a non-empty string.
* A provider satisfies this selection criterion iff the
* provider implements the specified algorithm or type for the specified
--- a/jdk/src/share/classes/java/security/cert/X509CRL.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/java/security/cert/X509CRL.java Wed Jul 03 14:35:10 2013 -0700
@@ -249,7 +249,8 @@
* The ASN.1 definition for this is:
* <pre>
* version Version OPTIONAL,
- * -- if present, must be v2<p>
+ * -- if present, must be v2
+ *
* Version ::= INTEGER { v1(0), v2(1), v3(2) }
* -- v3 does not apply to CRLs but appears for consistency
* -- with definition of Version for certs
@@ -413,7 +414,8 @@
* signature algorithm. An example is the string "SHA256withRSA".
* The ASN.1 definition for this is:
* <pre>
- * signatureAlgorithm AlgorithmIdentifier<p>
+ * signatureAlgorithm AlgorithmIdentifier
+ *
* AlgorithmIdentifier ::= SEQUENCE {
* algorithm OBJECT IDENTIFIER,
* parameters ANY DEFINED BY algorithm OPTIONAL }
--- a/jdk/src/share/classes/java/security/cert/X509CRLEntry.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/java/security/cert/X509CRLEntry.java Wed Jul 03 14:35:10 2013 -0700
@@ -43,11 +43,11 @@
* crlEntryExtensions Extensions OPTIONAL
* -- if present, must be v2
* } OPTIONAL
- *<p>
+ *
* CertificateSerialNumber ::= INTEGER
- *<p>
+ *
* Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
- *<p>
+ *
* Extension ::= SEQUENCE {
* extnId OBJECT IDENTIFIER,
* critical BOOLEAN DEFAULT FALSE,
--- a/jdk/src/share/classes/java/security/cert/X509Certificate.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/java/security/cert/X509Certificate.java Wed Jul 03 14:35:10 2013 -0700
@@ -126,10 +126,12 @@
* is valid. It is defined in
* ASN.1 as:
* <pre>
- * validity Validity<p>
+ * validity Validity
+ *
* Validity ::= SEQUENCE {
* notBefore CertificateValidityDate,
- * notAfter CertificateValidityDate }<p>
+ * notAfter CertificateValidityDate }
+ *
* CertificateValidityDate ::= CHOICE {
* utcTime UTCTime,
* generalTime GeneralizedTime }
@@ -165,7 +167,8 @@
* certificate.
* The ASN.1 definition for this is:
* <pre>
- * version [0] EXPLICIT Version DEFAULT v1<p>
+ * version [0] EXPLICIT Version DEFAULT v1
+ *
* Version ::= INTEGER { v1(0), v2(1), v3(2) }
* </pre>
* @return the version number, i.e. 1, 2 or 3.
@@ -180,7 +183,7 @@
* serial number identify a unique certificate).
* The ASN.1 definition for this is:
* <pre>
- * serialNumber CertificateSerialNumber<p>
+ * serialNumber CertificateSerialNumber
*
* CertificateSerialNumber ::= INTEGER
* </pre>
@@ -204,7 +207,7 @@
* X.500 distinguished name (DN).
* The ASN.1 definition for this is:
* <pre>
- * issuer Name<p>
+ * issuer Name
*
* Name ::= CHOICE { RDNSequence }
* RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
@@ -295,11 +298,12 @@
* the certificate.
* The relevant ASN.1 definitions are:
* <pre>
- * validity Validity<p>
+ * validity Validity
*
* Validity ::= SEQUENCE {
* notBefore CertificateValidityDate,
- * notAfter CertificateValidityDate }<p>
+ * notAfter CertificateValidityDate }
+ *
* CertificateValidityDate ::= CHOICE {
* utcTime UTCTime,
* generalTime GeneralizedTime }
@@ -348,7 +352,8 @@
* signature algorithm. An example is the string "SHA256withRSA".
* The ASN.1 definition for this is:
* <pre>
- * signatureAlgorithm AlgorithmIdentifier<p>
+ * signatureAlgorithm AlgorithmIdentifier
+ *
* AlgorithmIdentifier ::= SEQUENCE {
* algorithm OBJECT IDENTIFIER,
* parameters ANY DEFINED BY algorithm OPTIONAL }
@@ -410,7 +415,8 @@
*
* <p>The ASN.1 definition for this is:
* <pre>
- * issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL<p>
+ * issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL
+ *
* UniqueIdentifier ::= BIT STRING
* </pre>
*
@@ -424,7 +430,8 @@
*
* <p>The ASN.1 definition for this is:
* <pre>
- * subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL<p>
+ * subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL
+ *
* UniqueIdentifier ::= BIT STRING
* </pre>
*
@@ -474,9 +481,9 @@
* indicated in the key usage extension field. The ASN.1
* definition for this is:
* <pre>
- * ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId<p>
+ * ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId
*
- * KeyPurposeId ::= OBJECT IDENTIFIER<p>
+ * KeyPurposeId ::= OBJECT IDENTIFIER
* </pre>
*
* Key purposes may be defined by any organization with a
--- a/jdk/src/share/classes/java/time/format/DateTimeFormatter.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/java/time/format/DateTimeFormatter.java Wed Jul 03 14:35:10 2013 -0700
@@ -1304,6 +1304,7 @@
* LocalTime time = parsed.query(LocalTime::from);
* Period extraDays = parsed.query(DateTimeFormatter.parsedExcessDays());
* </pre>
+ * @return a query that provides access to the excess days that were parsed
*/
public static final TemporalQuery<Period> parsedExcessDays() {
return PARSED_EXCESS_DAYS;
@@ -1344,6 +1345,7 @@
* // validate leap-second is correct and apply correct smoothing
* }
* </pre>
+ * @return a query that provides access to whether a leap-second was parsed
*/
public static final TemporalQuery<Boolean> parsedLeapSecond() {
return PARSED_LEAP_SECOND;
--- a/jdk/src/share/classes/java/util/concurrent/ArrayBlockingQueue.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/java/util/concurrent/ArrayBlockingQueue.java Wed Jul 03 14:35:10 2013 -0700
@@ -34,8 +34,15 @@
*/
package java.util.concurrent;
-import java.util.concurrent.locks.*;
-import java.util.*;
+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.lang.ref.WeakReference;
+import java.util.Spliterators;
+import java.util.Spliterator;
/**
* A bounded {@linkplain BlockingQueue blocking queue} backed by an
@@ -102,19 +109,21 @@
/** Main lock guarding all access */
final ReentrantLock lock;
+
/** Condition for waiting takes */
private final Condition notEmpty;
+
/** Condition for waiting puts */
private final Condition notFull;
- // Internal helper methods
+ /**
+ * Shared state for currently active iterators, or null if there
+ * are known not to be any. Allows queue operations to update
+ * iterator state.
+ */
+ transient Itrs itrs = null;
- /**
- * Circularly increment i.
- */
- final int inc(int i) {
- return (++i == items.length) ? 0 : i;
- }
+ // Internal helper methods
/**
* Circularly decrement i.
@@ -123,11 +132,6 @@
return ((i == 0) ? items.length : i) - 1;
}
- @SuppressWarnings("unchecked")
- static <E> E cast(Object item) {
- return (E) item;
- }
-
/**
* Returns item at index i.
*/
@@ -150,10 +154,14 @@
* Inserts element at current put position, advances, and signals.
* Call only when holding lock.
*/
- private void insert(E x) {
+ private void enqueue(E x) {
+ // assert lock.getHoldCount() == 1;
+ // assert items[putIndex] == null;
+ final Object[] items = this.items;
items[putIndex] = x;
- putIndex = inc(putIndex);
- ++count;
+ if (++putIndex == items.length)
+ putIndex = 0;
+ count++;
notEmpty.signal();
}
@@ -161,43 +169,62 @@
* Extracts element at current take position, advances, and signals.
* Call only when holding lock.
*/
- private E extract() {
+ private E dequeue() {
+ // assert lock.getHoldCount() == 1;
+ // assert items[takeIndex] != null;
final Object[] items = this.items;
@SuppressWarnings("unchecked")
E x = (E) items[takeIndex];
items[takeIndex] = null;
- takeIndex = inc(takeIndex);
- --count;
+ if (++takeIndex == items.length)
+ takeIndex = 0;
+ count--;
+ if (itrs != null)
+ itrs.elementDequeued();
notFull.signal();
return x;
}
/**
- * Deletes item at position i.
- * Utility for remove and iterator.remove.
+ * Deletes item at array index removeIndex.
+ * Utility for remove(Object) and iterator.remove.
* Call only when holding lock.
*/
- void removeAt(int i) {
+ void removeAt(final int removeIndex) {
+ // assert lock.getHoldCount() == 1;
+ // assert items[removeIndex] != null;
+ // assert removeIndex >= 0 && removeIndex < items.length;
final Object[] items = this.items;
- // if removing front item, just advance
- if (i == takeIndex) {
+ if (removeIndex == takeIndex) {
+ // removing front item; just advance
items[takeIndex] = null;
- takeIndex = inc(takeIndex);
+ if (++takeIndex == items.length)
+ takeIndex = 0;
+ count--;
+ if (itrs != null)
+ itrs.elementDequeued();
} else {
+ // an "interior" remove
+
// slide over all others up through putIndex.
- for (;;) {
- int nexti = inc(i);
- if (nexti != putIndex) {
- items[i] = items[nexti];
- i = nexti;
+ 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;
- putIndex = i;
+ this.putIndex = i;
break;
}
}
+ count--;
+ if (itrs != null)
+ itrs.removedAt(removeIndex);
}
- --count;
notFull.signal();
}
@@ -302,7 +329,7 @@
if (count == items.length)
return false;
else {
- insert(e);
+ enqueue(e);
return true;
}
} finally {
@@ -324,7 +351,7 @@
try {
while (count == items.length)
notFull.await();
- insert(e);
+ enqueue(e);
} finally {
lock.unlock();
}
@@ -351,7 +378,7 @@
return false;
nanos = notFull.awaitNanos(nanos);
}
- insert(e);
+ enqueue(e);
return true;
} finally {
lock.unlock();
@@ -362,7 +389,7 @@
final ReentrantLock lock = this.lock;
lock.lock();
try {
- return (count == 0) ? null : extract();
+ return (count == 0) ? null : dequeue();
} finally {
lock.unlock();
}
@@ -374,7 +401,7 @@
try {
while (count == 0)
notEmpty.await();
- return extract();
+ return dequeue();
} finally {
lock.unlock();
}
@@ -390,7 +417,7 @@
return null;
nanos = notEmpty.awaitNanos(nanos);
}
- return extract();
+ return dequeue();
} finally {
lock.unlock();
}
@@ -400,7 +427,7 @@
final ReentrantLock lock = this.lock;
lock.lock();
try {
- return (count == 0) ? null : itemAt(takeIndex);
+ return itemAt(takeIndex); // null when queue is empty
} finally {
lock.unlock();
}
@@ -469,11 +496,17 @@
final ReentrantLock lock = this.lock;
lock.lock();
try {
- for (int i = takeIndex, k = count; k > 0; i = inc(i), k--) {
- if (o.equals(items[i])) {
- removeAt(i);
- return true;
- }
+ if (count > 0) {
+ final int putIndex = this.putIndex;
+ int i = takeIndex;
+ do {
+ if (o.equals(items[i])) {
+ removeAt(i);
+ return true;
+ }
+ if (++i == items.length)
+ i = 0;
+ } while (i != putIndex);
}
return false;
} finally {
@@ -495,9 +528,16 @@
final ReentrantLock lock = this.lock;
lock.lock();
try {
- for (int i = takeIndex, k = count; k > 0; i = inc(i), k--)
- if (o.equals(items[i]))
- return true;
+ if (count > 0) {
+ final int putIndex = this.putIndex;
+ int i = takeIndex;
+ do {
+ if (o.equals(items[i]))
+ return true;
+ if (++i == items.length)
+ i = 0;
+ } while (i != putIndex);
+ }
return false;
} finally {
lock.unlock();
@@ -518,18 +558,23 @@
* @return an array containing all of the elements in this queue
*/
public Object[] toArray() {
- final Object[] items = this.items;
+ Object[] a;
final ReentrantLock lock = this.lock;
lock.lock();
try {
final int count = this.count;
- Object[] a = new Object[count];
- for (int i = takeIndex, k = 0; k < count; i = inc(i), k++)
- a[k] = items[i];
- return a;
+ 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);
+ }
} finally {
lock.unlock();
}
+ return a;
}
/**
@@ -553,8 +598,7 @@
* The following code can be used to dump the queue into a newly
* allocated array of {@code String}:
*
- * <pre>
- * String[] y = x.toArray(new String[0]);</pre>
+ * <pre> {@code String[] y = x.toArray(new String[0]);}</pre>
*
* Note that {@code toArray(new Object[0])} is identical in function to
* {@code toArray()}.
@@ -579,14 +623,19 @@
if (len < count)
a = (T[])java.lang.reflect.Array.newInstance(
a.getClass().getComponentType(), count);
- for (int i = takeIndex, k = 0; k < count; i = inc(i), k++)
- a[k] = (T) items[i];
+ 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);
+ }
if (len > count)
a[count] = null;
- return a;
} finally {
lock.unlock();
}
+ return a;
}
public String toString() {
@@ -597,14 +646,17 @@
if (k == 0)
return "[]";
+ final Object[] items = this.items;
StringBuilder sb = new StringBuilder();
sb.append('[');
- for (int i = takeIndex; ; i = inc(i)) {
+ 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();
@@ -620,12 +672,22 @@
final ReentrantLock lock = this.lock;
lock.lock();
try {
- for (int i = takeIndex, k = count; k > 0; i = inc(i), k--)
- items[i] = null;
- count = 0;
- putIndex = 0;
- takeIndex = 0;
- notFull.signalAll();
+ int k = count;
+ if (k > 0) {
+ final int putIndex = this.putIndex;
+ int i = takeIndex;
+ do {
+ items[i] = null;
+ if (++i == items.length)
+ i = 0;
+ } while (i != putIndex);
+ takeIndex = putIndex;
+ count = 0;
+ if (itrs != null)
+ itrs.queueIsEmpty();
+ for (; k > 0 && lock.hasWaiters(notFull); k--)
+ notFull.signal();
+ }
} finally {
lock.unlock();
}
@@ -638,34 +700,7 @@
* @throws IllegalArgumentException {@inheritDoc}
*/
public int drainTo(Collection<? super E> c) {
- checkNotNull(c);
- if (c == this)
- throw new IllegalArgumentException();
- final Object[] items = this.items;
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- int i = takeIndex;
- int n = 0;
- int max = count;
- while (n < max) {
- @SuppressWarnings("unchecked")
- E x = (E) items[i];
- c.add(x);
- items[i] = null;
- i = inc(i);
- ++n;
- }
- if (n > 0) {
- count = 0;
- putIndex = 0;
- takeIndex = 0;
- notFull.signalAll();
- }
- return n;
- } finally {
- lock.unlock();
- }
+ return drainTo(c, Integer.MAX_VALUE);
}
/**
@@ -684,23 +719,35 @@
final ReentrantLock lock = this.lock;
lock.lock();
try {
- int i = takeIndex;
- int n = 0;
- int max = (maxElements < count) ? maxElements : count;
- while (n < max) {
- @SuppressWarnings("unchecked")
- E x = (E) items[i];
- c.add(x);
- items[i] = null;
- i = inc(i);
- ++n;
+ int n = Math.min(maxElements, count);
+ int take = takeIndex;
+ int i = 0;
+ try {
+ while (i < n) {
+ @SuppressWarnings("unchecked")
+ E x = (E) items[take];
+ c.add(x);
+ items[take] = null;
+ if (++take == items.length)
+ take = 0;
+ i++;
+ }
+ return n;
+ } finally {
+ // Restore invariants even if c.add() threw
+ if (i > 0) {
+ count -= i;
+ takeIndex = take;
+ if (itrs != null) {
+ if (count == 0)
+ itrs.queueIsEmpty();
+ else if (i > take)
+ itrs.takeIndexWrapped();
+ }
+ for (; i > 0 && lock.hasWaiters(notFull); i--)
+ notFull.signal();
+ }
}
- if (n > 0) {
- count -= n;
- takeIndex = i;
- notFull.signalAll();
- }
- return n;
} finally {
lock.unlock();
}
@@ -710,12 +757,12 @@
* Returns an iterator over the elements in this queue in proper sequence.
* The elements will be returned in order from first (head) to last (tail).
*
- * <p>The returned {@code Iterator} is a "weakly consistent" iterator that
+ * <p>The returned iterator is a "weakly consistent" iterator that
* will never throw {@link java.util.ConcurrentModificationException
- * ConcurrentModificationException},
- * and guarantees to traverse elements as they existed upon
- * construction of the iterator, and may (but is not guaranteed to)
- * reflect any modifications subsequent to construction.
+ * ConcurrentModificationException}, and guarantees to traverse
+ * elements as they existed upon construction of the iterator, and
+ * may (but is not guaranteed to) reflect any modifications
+ * subsequent to construction.
*
* @return an iterator over the elements in this queue in proper sequence
*/
@@ -724,88 +771,634 @@
}
/**
- * Iterator for ArrayBlockingQueue. To maintain weak consistency
- * with respect to puts and takes, we (1) read ahead one slot, so
- * as to not report hasNext true but then not have an element to
- * return -- however we later recheck this slot to use the most
- * current value; (2) ensure that each array slot is traversed at
- * most once (by tracking "remaining" elements); (3) skip over
- * null slots, which can occur if takes race ahead of iterators.
- * However, for circular array-based queues, we cannot rely on any
- * well established definition of what it means to be weakly
- * consistent with respect to interior removes since these may
- * require slot overwrites in the process of sliding elements to
- * cover gaps. So we settle for resiliency, operating on
- * established apparent nexts, which may miss some elements that
- * have moved between calls to next.
+ * Shared data between iterators and their queue, allowing queue
+ * modifications to update iterators when elements are removed.
+ *
+ * This adds a lot of complexity for the sake of correctly
+ * handling some uncommon operations, but the combination of
+ * circular-arrays and supporting interior removes (i.e., those
+ * not at head) would cause iterators to sometimes lose their
+ * places and/or (re)report elements they shouldn't. To avoid
+ * this, when a queue has one or more iterators, it keeps iterator
+ * state consistent by:
+ *
+ * (1) keeping track of the number of "cycles", that is, the
+ * number of times takeIndex has wrapped around to 0.
+ * (2) notifying all iterators via the callback removedAt whenever
+ * an interior element is removed (and thus other elements may
+ * be shifted).
+ *
+ * These suffice to eliminate iterator inconsistencies, but
+ * unfortunately add the secondary responsibility of maintaining
+ * the list of iterators. We track all active iterators in a
+ * simple linked list (accessed only when the queue's lock is
+ * held) of weak references to Itr. The list is cleaned up using
+ * 3 different mechanisms:
+ *
+ * (1) Whenever a new iterator is created, do some O(1) checking for
+ * stale list elements.
+ *
+ * (2) Whenever takeIndex wraps around to 0, check for iterators
+ * that have been unused for more than one wrap-around cycle.
+ *
+ * (3) Whenever the queue becomes empty, all iterators are notified
+ * and this entire data structure is discarded.
+ *
+ * So in addition to the removedAt callback that is necessary for
+ * correctness, iterators have the shutdown and takeIndexWrapped
+ * callbacks that help remove stale iterators from the list.
+ *
+ * Whenever a list element is examined, it is expunged if either
+ * the GC has determined that the iterator is discarded, or if the
+ * iterator reports that it is "detached" (does not need any
+ * further state updates). Overhead is maximal when takeIndex
+ * never advances, iterators are discarded before they are
+ * exhausted, and all removals are interior removes, in which case
+ * all stale iterators are discovered by the GC. But even in this
+ * case we don't increase the amortized complexity.
+ *
+ * Care must be taken to keep list sweeping methods from
+ * reentrantly invoking another such method, causing subtle
+ * corruption bugs.
+ */
+ class Itrs {
+
+ /**
+ * Node in a linked list of weak iterator references.
+ */
+ private class Node extends WeakReference<Itr> {
+ Node next;
+
+ Node(Itr iterator, Node next) {
+ super(iterator);
+ this.next = next;
+ }
+ }
+
+ /** Incremented whenever takeIndex wraps around to 0 */
+ int cycles = 0;
+
+ /** Linked list of weak iterator references */
+ private Node head;
+
+ /** Used to expunge stale iterators */
+ private Node sweeper = null;
+
+ private static final int SHORT_SWEEP_PROBES = 4;
+ private static final int LONG_SWEEP_PROBES = 16;
+
+ Itrs(Itr initial) {
+ register(initial);
+ }
+
+ /**
+ * Sweeps itrs, looking for and expunging stale iterators.
+ * If at least one was found, tries harder to find more.
+ * Called only from iterating thread.
+ *
+ * @param tryHarder whether to start in try-harder mode, because
+ * there is known to be at least one iterator to collect
+ */
+ void doSomeSweeping(boolean tryHarder) {
+ // assert lock.getHoldCount() == 1;
+ // assert head != null;
+ int probes = tryHarder ? LONG_SWEEP_PROBES : SHORT_SWEEP_PROBES;
+ Node o, p;
+ final Node sweeper = this.sweeper;
+ boolean passedGo; // to limit search to one full sweep
+
+ if (sweeper == null) {
+ o = null;
+ p = head;
+ passedGo = true;
+ } else {
+ o = sweeper;
+ p = o.next;
+ passedGo = false;
+ }
+
+ for (; probes > 0; probes--) {
+ if (p == null) {
+ if (passedGo)
+ break;
+ o = null;
+ p = head;
+ passedGo = true;
+ }
+ final Itr it = p.get();
+ final Node next = p.next;
+ if (it == null || it.isDetached()) {
+ // found a discarded/exhausted iterator
+ probes = LONG_SWEEP_PROBES; // "try harder"
+ // unlink p
+ p.clear();
+ p.next = null;
+ if (o == null) {
+ head = next;
+ if (next == null) {
+ // We've run out of iterators to track; retire
+ itrs = null;
+ return;
+ }
+ }
+ else
+ o.next = next;
+ } else {
+ o = p;
+ }
+ p = next;
+ }
+
+ this.sweeper = (p == null) ? null : o;
+ }
+
+ /**
+ * Adds a new iterator to the linked list of tracked iterators.
+ */
+ void register(Itr itr) {
+ // assert lock.getHoldCount() == 1;
+ head = new Node(itr, head);
+ }
+
+ /**
+ * Called whenever takeIndex wraps around to 0.
+ *
+ * Notifies all iterators, and expunges any that are now stale.
+ */
+ void takeIndexWrapped() {
+ // assert lock.getHoldCount() == 1;
+ cycles++;
+ for (Node o = null, p = head; p != null;) {
+ final Itr it = p.get();
+ final Node next = p.next;
+ if (it == null || it.takeIndexWrapped()) {
+ // unlink p
+ // assert it == null || it.isDetached();
+ p.clear();
+ p.next = null;
+ if (o == null)
+ head = next;
+ else
+ o.next = next;
+ } else {
+ o = p;
+ }
+ p = next;
+ }
+ if (head == null) // no more iterators to track
+ itrs = null;
+ }
+
+ /**
+ * Called whenever an interior remove (not at takeIndex) occured.
+ *
+ * Notifies all iterators, and expunges any that are now stale.
+ */
+ void removedAt(int removedIndex) {
+ for (Node o = null, p = head; p != null;) {
+ final Itr it = p.get();
+ final Node next = p.next;
+ if (it == null || it.removedAt(removedIndex)) {
+ // unlink p
+ // assert it == null || it.isDetached();
+ p.clear();
+ p.next = null;
+ if (o == null)
+ head = next;
+ else
+ o.next = next;
+ } else {
+ o = p;
+ }
+ p = next;
+ }
+ if (head == null) // no more iterators to track
+ itrs = null;
+ }
+
+ /**
+ * Called whenever the queue becomes empty.
+ *
+ * Notifies all active iterators that the queue is empty,
+ * clears all weak refs, and unlinks the itrs datastructure.
+ */
+ void queueIsEmpty() {
+ // assert lock.getHoldCount() == 1;
+ for (Node p = head; p != null; p = p.next) {
+ Itr it = p.get();
+ if (it != null) {
+ p.clear();
+ it.shutdown();
+ }
+ }
+ head = null;
+ itrs = null;
+ }
+
+ /**
+ * Called whenever an element has been dequeued (at takeIndex).
+ */
+ void elementDequeued() {
+ // assert lock.getHoldCount() == 1;
+ if (count == 0)
+ queueIsEmpty();
+ else if (takeIndex == 0)
+ takeIndexWrapped();
+ }
+ }
+
+ /**
+ * Iterator for ArrayBlockingQueue.
+ *
+ * To maintain weak consistency with respect to puts and takes, we
+ * read ahead one slot, so as to not report hasNext true but then
+ * not have an element to return.
+ *
+ * We switch into "detached" mode (allowing prompt unlinking from
+ * itrs without help from the GC) when all indices are negative, or
+ * when hasNext returns false for the first time. This allows the
+ * iterator to track concurrent updates completely accurately,
+ * except for the corner case of the user calling Iterator.remove()
+ * after hasNext() returned false. Even in this case, we ensure
+ * that we don't remove the wrong element by keeping track of the
+ * expected element to remove, in lastItem. Yes, we may fail to
+ * remove lastItem from the queue if it moved due to an interleaved
+ * interior remove while in detached mode.
*/
private class Itr implements Iterator<E> {
- private int remaining; // Number of elements yet to be returned
- private int nextIndex; // Index of element to be returned by next
- private E nextItem; // Element to be returned by next call to next
- private E lastItem; // Element returned by last call to next
- private int lastRet; // Index of last element returned, or -1 if none
+ /** Index to look for new nextItem; NONE at end */
+ private int cursor;
+
+ /** Element to be returned by next call to next(); null if none */
+ private E nextItem;
+
+ /** Index of nextItem; NONE if none, REMOVED if removed elsewhere */
+ private int nextIndex;
+
+ /** Last element returned; null if none or not detached. */
+ private E lastItem;
+
+ /** Index of lastItem, NONE if none, REMOVED if removed elsewhere */
+ private int lastRet;
+
+ /** Previous value of takeIndex, or DETACHED when detached */
+ private int prevTakeIndex;
+
+ /** Previous value of iters.cycles */
+ private int prevCycles;
+
+ /** Special index value indicating "not available" or "undefined" */
+ private static final int NONE = -1;
+
+ /**
+ * Special index value indicating "removed elsewhere", that is,
+ * removed by some operation other than a call to this.remove().
+ */
+ private static final int REMOVED = -2;
+
+ /** Special value for prevTakeIndex indicating "detached mode" */
+ private static final int DETACHED = -3;
Itr() {
+ // assert lock.getHoldCount() == 0;
+ lastRet = NONE;
final ReentrantLock lock = ArrayBlockingQueue.this.lock;
lock.lock();
try {
- lastRet = -1;
- if ((remaining = count) > 0)
+ if (count == 0) {
+ // assert itrs == null;
+ cursor = NONE;
+ nextIndex = NONE;
+ prevTakeIndex = DETACHED;
+ } else {
+ final int takeIndex = ArrayBlockingQueue.this.takeIndex;
+ prevTakeIndex = takeIndex;
nextItem = itemAt(nextIndex = takeIndex);
+ cursor = incCursor(takeIndex);
+ if (itrs == null) {
+ itrs = new Itrs(this);
+ } else {
+ itrs.register(this); // in this order
+ itrs.doSomeSweeping(false);
+ }
+ prevCycles = itrs.cycles;
+ // assert takeIndex >= 0;
+ // assert prevTakeIndex == takeIndex;
+ // assert nextIndex >= 0;
+ // assert nextItem != null;
+ }
} finally {
lock.unlock();
}
}
- public boolean hasNext() {
- return remaining > 0;
+ boolean isDetached() {
+ // assert lock.getHoldCount() == 1;
+ return prevTakeIndex < 0;
+ }
+
+ private int incCursor(int index) {
+ // assert lock.getHoldCount() == 1;
+ if (++index == items.length)
+ index = 0;
+ if (index == putIndex)
+ index = NONE;
+ return index;
+ }
+
+ /**
+ * Returns true if index is invalidated by the given number of
+ * dequeues, starting from prevTakeIndex.
+ */
+ private boolean invalidated(int index, int prevTakeIndex,
+ long dequeues, int length) {
+ if (index < 0)
+ return false;
+ int distance = index - prevTakeIndex;
+ if (distance < 0)
+ distance += length;
+ return dequeues > distance;
}
- public E next() {
+ /**
+ * Adjusts indices to incorporate all dequeues since the last
+ * operation on this iterator. Call only from iterating thread.
+ */
+ private void incorporateDequeues() {
+ // assert lock.getHoldCount() == 1;
+ // assert itrs != null;
+ // assert !isDetached();
+ // assert count > 0;
+
+ final int cycles = itrs.cycles;
+ final int takeIndex = ArrayBlockingQueue.this.takeIndex;
+ final int prevCycles = this.prevCycles;
+ final int prevTakeIndex = this.prevTakeIndex;
+
+ if (cycles != prevCycles || takeIndex != prevTakeIndex) {
+ final int len = items.length;
+ // how far takeIndex has advanced since the previous
+ // operation of this iterator
+ long dequeues = (cycles - prevCycles) * len
+ + (takeIndex - prevTakeIndex);
+
+ // Check indices for invalidation
+ if (invalidated(lastRet, prevTakeIndex, dequeues, len))
+ lastRet = REMOVED;
+ if (invalidated(nextIndex, prevTakeIndex, dequeues, len))
+ nextIndex = REMOVED;
+ if (invalidated(cursor, prevTakeIndex, dequeues, len))
+ cursor = takeIndex;
+
+ if (cursor < 0 && nextIndex < 0 && lastRet < 0)
+ detach();
+ else {
+ this.prevCycles = cycles;
+ this.prevTakeIndex = takeIndex;
+ }
+ }
+ }
+
+ /**
+ * Called when itrs should stop tracking this iterator, either
+ * because there are no more indices to update (cursor < 0 &&
+ * nextIndex < 0 && lastRet < 0) or as a special exception, when
+ * lastRet >= 0, because hasNext() is about to return false for the
+ * first time. Call only from iterating thread.
+ */
+ private void detach() {
+ // Switch to detached mode
+ // assert lock.getHoldCount() == 1;
+ // assert cursor == NONE;
+ // assert nextIndex < 0;
+ // assert lastRet < 0 || nextItem == null;
+ // assert lastRet < 0 ^ lastItem != null;
+ if (prevTakeIndex >= 0) {
+ // assert itrs != null;
+ prevTakeIndex = DETACHED;
+ // try to unlink from itrs (but not too hard)
+ itrs.doSomeSweeping(true);
+ }
+ }
+
+ /**
+ * For performance reasons, we would like not to acquire a lock in
+ * hasNext in the common case. To allow for this, we only access
+ * fields (i.e. nextItem) that are not modified by update operations
+ * triggered by queue modifications.
+ */
+ public boolean hasNext() {
+ // assert lock.getHoldCount() == 0;
+ if (nextItem != null)
+ return true;
+ noNext();
+ return false;
+ }
+
+ private void noNext() {
final ReentrantLock lock = ArrayBlockingQueue.this.lock;
lock.lock();
try {
- if (remaining <= 0)
- throw new NoSuchElementException();
- lastRet = nextIndex;
- E x = itemAt(nextIndex); // check for fresher value
- if (x == null) {
- x = nextItem; // we are forced to report old value
- lastItem = null; // but ensure remove fails
+ // assert cursor == NONE;
+ // assert nextIndex == NONE;
+ if (!isDetached()) {
+ // assert lastRet >= 0;
+ incorporateDequeues(); // might update lastRet
+ if (lastRet >= 0) {
+ lastItem = itemAt(lastRet);
+ // assert lastItem != null;
+ detach();
+ }
}
- else
- lastItem = x;
- while (--remaining > 0 && // skip over nulls
- (nextItem = itemAt(nextIndex = inc(nextIndex))) == null)
- ;
- return x;
+ // assert isDetached();
+ // assert lastRet < 0 ^ lastItem != null;
} finally {
lock.unlock();
}
}
- public void remove() {
+ public E next() {
+ // assert lock.getHoldCount() == 0;
+ final E x = nextItem;
+ if (x == null)
+ throw new NoSuchElementException();
final ReentrantLock lock = ArrayBlockingQueue.this.lock;
lock.lock();
try {
- int i = lastRet;
- if (i == -1)
- throw new IllegalStateException();
- lastRet = -1;
- E x = lastItem;
- lastItem = null;
- // only remove if item still at index
- if (x != null && x == items[i]) {
- boolean removingHead = (i == takeIndex);
- removeAt(i);
- if (!removingHead)
- nextIndex = dec(nextIndex);
+ if (!isDetached())
+ incorporateDequeues();
+ // assert nextIndex != NONE;
+ // assert lastItem == null;
+ lastRet = nextIndex;
+ final int cursor = this.cursor;
+ if (cursor >= 0) {
+ nextItem = itemAt(nextIndex = cursor);
+ // assert nextItem != null;
+ this.cursor = incCursor(cursor);
+ } else {
+ nextIndex = NONE;
+ nextItem = null;
}
} finally {
lock.unlock();
}
+ return x;
}
+
+ public void remove() {
+ // assert lock.getHoldCount() == 0;
+ final ReentrantLock lock = ArrayBlockingQueue.this.lock;
+ lock.lock();
+ try {
+ if (!isDetached())
+ incorporateDequeues(); // might update lastRet or detach
+ final int lastRet = this.lastRet;
+ this.lastRet = NONE;
+ if (lastRet >= 0) {
+ if (!isDetached())
+ removeAt(lastRet);
+ else {
+ final E lastItem = this.lastItem;
+ // assert lastItem != null;
+ this.lastItem = null;
+ if (itemAt(lastRet) == lastItem)
+ removeAt(lastRet);
+ }
+ } else if (lastRet == NONE)
+ throw new IllegalStateException();
+ // else lastRet == REMOVED and the last returned element was
+ // previously asynchronously removed via an operation other
+ // than this.remove(), so nothing to do.
+
+ if (cursor < 0 && nextIndex < 0)
+ detach();
+ } finally {
+ lock.unlock();
+ // assert lastRet == NONE;
+ // assert lastItem == null;
+ }
+ }
+
+ /**
+ * Called to notify the iterator that the queue is empty, or that it
+ * has fallen hopelessly behind, so that it should abandon any
+ * further iteration, except possibly to return one more element
+ * from next(), as promised by returning true from hasNext().
+ */
+ void shutdown() {
+ // assert lock.getHoldCount() == 1;
+ cursor = NONE;
+ if (nextIndex >= 0)
+ nextIndex = REMOVED;
+ if (lastRet >= 0) {
+ lastRet = REMOVED;
+ lastItem = null;
+ }
+ prevTakeIndex = DETACHED;
+ // Don't set nextItem to null because we must continue to be
+ // able to return it on next().
+ //
+ // Caller will unlink from itrs when convenient.
+ }
+
+ private int distance(int index, int prevTakeIndex, int length) {
+ int distance = index - prevTakeIndex;
+ if (distance < 0)
+ distance += length;
+ return distance;
+ }
+
+ /**
+ * Called whenever an interior remove (not at takeIndex) occured.
+ *
+ * @return true if this iterator should be unlinked from itrs
+ */
+ boolean removedAt(int removedIndex) {
+ // assert lock.getHoldCount() == 1;
+ 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++;
+ final int removedDistance =
+ (cycleDiff * len) + (removedIndex - prevTakeIndex);
+ // assert removedDistance >= 0;
+ int cursor = this.cursor;
+ if (cursor >= 0) {
+ int x = distance(cursor, prevTakeIndex, len);
+ if (x == removedDistance) {
+ if (cursor == putIndex)
+ this.cursor = cursor = NONE;
+ }
+ else if (x > removedDistance) {
+ // assert cursor != prevTakeIndex;
+ this.cursor = cursor = dec(cursor);
+ }
+ }
+ int lastRet = this.lastRet;
+ if (lastRet >= 0) {
+ int x = distance(lastRet, prevTakeIndex, len);
+ if (x == removedDistance)
+ this.lastRet = lastRet = REMOVED;
+ else if (x > removedDistance)
+ this.lastRet = lastRet = dec(lastRet);
+ }
+ int nextIndex = this.nextIndex;
+ if (nextIndex >= 0) {
+ int x = distance(nextIndex, prevTakeIndex, len);
+ if (x == removedDistance)
+ this.nextIndex = nextIndex = REMOVED;
+ else if (x > removedDistance)
+ this.nextIndex = nextIndex = dec(nextIndex);
+ }
+ else if (cursor < 0 && nextIndex < 0 && lastRet < 0) {
+ this.prevTakeIndex = DETACHED;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Called whenever takeIndex wraps around to zero.
+ *
+ * @return true if this iterator should be unlinked from itrs
+ */
+ boolean takeIndexWrapped() {
+ // assert lock.getHoldCount() == 1;
+ if (isDetached())
+ return true;
+ if (itrs.cycles - prevCycles > 1) {
+ // All the elements that existed at the time of the last
+ // operation are gone, so abandon further iteration.
+ shutdown();
+ return true;
+ }
+ return false;
+ }
+
+// /** Uncomment for debugging. */
+// public String toString() {
+// return ("cursor=" + cursor + " " +
+// "nextIndex=" + nextIndex + " " +
+// "lastRet=" + lastRet + " " +
+// "nextItem=" + nextItem + " " +
+// "lastItem=" + lastItem + " " +
+// "prevCycles=" + prevCycles + " " +
+// "prevTakeIndex=" + prevTakeIndex + " " +
+// "size()=" + size() + " " +
+// "remainingCapacity()=" + remainingCapacity());
+// }
}
+ public Spliterator<E> spliterator() {
+ return Spliterators.spliterator
+ (this, Spliterator.ORDERED | Spliterator.NONNULL |
+ Spliterator.CONCURRENT);
+ }
}
--- a/jdk/src/share/classes/java/util/concurrent/BlockingDeque.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/java/util/concurrent/BlockingDeque.java Wed Jul 03 14:35:10 2013 -0700
@@ -41,17 +41,18 @@
* for the deque to become non-empty when retrieving an element, and wait for
* space to become available in the deque when storing an element.
*
- * <p><tt>BlockingDeque</tt> methods come in four forms, with different ways
+ * <p>{@code BlockingDeque} methods come in four forms, with different ways
* of handling operations that cannot be satisfied immediately, but may be
* satisfied at some point in the future:
* one throws an exception, the second returns a special value (either
- * <tt>null</tt> or <tt>false</tt>, depending on the operation), the third
+ * {@code null} or {@code false}, depending on the operation), the third
* blocks the current thread indefinitely until the operation can succeed,
* and the fourth blocks for only a given maximum time limit before giving
* up. These methods are summarized in the following table:
*
* <p>
* <table BORDER CELLPADDING=3 CELLSPACING=1>
+ * <caption>Summary of BlockingDeque methods</caption>
* <tr>
* <td ALIGN=CENTER COLSPAN = 5> <b>First Element (Head)</b></td>
* </tr>
@@ -116,20 +117,21 @@
* </tr>
* </table>
*
- * <p>Like any {@link BlockingQueue}, a <tt>BlockingDeque</tt> is thread safe,
+ * <p>Like any {@link BlockingQueue}, a {@code BlockingDeque} is thread safe,
* does not permit null elements, and may (or may not) be
* capacity-constrained.
*
- * <p>A <tt>BlockingDeque</tt> implementation may be used directly as a FIFO
- * <tt>BlockingQueue</tt>. The methods inherited from the
- * <tt>BlockingQueue</tt> interface are precisely equivalent to
- * <tt>BlockingDeque</tt> methods as indicated in the following table:
+ * <p>A {@code BlockingDeque} implementation may be used directly as a FIFO
+ * {@code BlockingQueue}. The methods inherited from the
+ * {@code BlockingQueue} interface are precisely equivalent to
+ * {@code BlockingDeque} methods as indicated in the following table:
*
* <p>
* <table BORDER CELLPADDING=3 CELLSPACING=1>
+ * <caption>Comparison of BlockingQueue and BlockingDeque methods</caption>
* <tr>
- * <td ALIGN=CENTER> <b><tt>BlockingQueue</tt> Method</b></td>
- * <td ALIGN=CENTER> <b>Equivalent <tt>BlockingDeque</tt> Method</b></td>
+ * <td ALIGN=CENTER> <b>{@code BlockingQueue} Method</b></td>
+ * <td ALIGN=CENTER> <b>Equivalent {@code BlockingDeque} Method</b></td>
* </tr>
* <tr>
* <td ALIGN=CENTER COLSPAN = 2> <b>Insert</b></td>
@@ -208,7 +210,7 @@
/**
* Inserts the specified element at the front of this deque if it is
* possible to do so immediately without violating capacity restrictions,
- * throwing an <tt>IllegalStateException</tt> if no space is currently
+ * throwing an {@code IllegalStateException} if no space is currently
* available. When using a capacity-restricted deque, it is generally
* preferable to use {@link #offerFirst(Object) offerFirst}.
*
@@ -223,7 +225,7 @@
/**
* Inserts the specified element at the end of this deque if it is
* possible to do so immediately without violating capacity restrictions,
- * throwing an <tt>IllegalStateException</tt> if no space is currently
+ * throwing an {@code IllegalStateException} if no space is currently
* available. When using a capacity-restricted deque, it is generally
* preferable to use {@link #offerLast(Object) offerLast}.
*
@@ -238,7 +240,7 @@
/**
* Inserts the specified element at the front of this deque if it is
* possible to do so immediately without violating capacity restrictions,
- * returning <tt>true</tt> upon success and <tt>false</tt> if no space is
+ * returning {@code true} upon success and {@code false} if no space is
* currently available.
* When using a capacity-restricted deque, this method is generally
* preferable to the {@link #addFirst(Object) addFirst} method, which can
@@ -254,7 +256,7 @@
/**
* Inserts the specified element at the end of this deque if it is
* possible to do so immediately without violating capacity restrictions,
- * returning <tt>true</tt> upon success and <tt>false</tt> if no space is
+ * returning {@code true} upon success and {@code false} if no space is
* currently available.
* When using a capacity-restricted deque, this method is generally
* preferable to the {@link #addLast(Object) addLast} method, which can
@@ -302,10 +304,10 @@
*
* @param e the element to add
* @param timeout how long to wait before giving up, in units of
- * <tt>unit</tt>
- * @param unit a <tt>TimeUnit</tt> determining how to interpret the
- * <tt>timeout</tt> parameter
- * @return <tt>true</tt> if successful, or <tt>false</tt> if
+ * {@code unit}
+ * @param unit a {@code TimeUnit} determining how to interpret the
+ * {@code timeout} parameter
+ * @return {@code true} if successful, or {@code false} if
* the specified waiting time elapses before space is available
* @throws InterruptedException if interrupted while waiting
* @throws ClassCastException if the class of the specified element
@@ -324,10 +326,10 @@
*
* @param e the element to add
* @param timeout how long to wait before giving up, in units of
- * <tt>unit</tt>
- * @param unit a <tt>TimeUnit</tt> determining how to interpret the
- * <tt>timeout</tt> parameter
- * @return <tt>true</tt> if successful, or <tt>false</tt> if
+ * {@code unit}
+ * @param unit a {@code TimeUnit} determining how to interpret the
+ * {@code timeout} parameter
+ * @return {@code true} if successful, or {@code false} if
* the specified waiting time elapses before space is available
* @throws InterruptedException if interrupted while waiting
* @throws ClassCastException if the class of the specified element
@@ -363,10 +365,10 @@
* become available.
*
* @param timeout how long to wait before giving up, in units of
- * <tt>unit</tt>
- * @param unit a <tt>TimeUnit</tt> determining how to interpret the
- * <tt>timeout</tt> parameter
- * @return the head of this deque, or <tt>null</tt> if the specified
+ * {@code unit}
+ * @param unit a {@code TimeUnit} determining how to interpret the
+ * {@code timeout} parameter
+ * @return the head of this deque, or {@code null} if the specified
* waiting time elapses before an element is available
* @throws InterruptedException if interrupted while waiting
*/
@@ -379,10 +381,10 @@
* become available.
*
* @param timeout how long to wait before giving up, in units of
- * <tt>unit</tt>
- * @param unit a <tt>TimeUnit</tt> determining how to interpret the
- * <tt>timeout</tt> parameter
- * @return the tail of this deque, or <tt>null</tt> if the specified
+ * {@code unit}
+ * @param unit a {@code TimeUnit} determining how to interpret the
+ * {@code timeout} parameter
+ * @return the tail of this deque, or {@code null} if the specified
* waiting time elapses before an element is available
* @throws InterruptedException if interrupted while waiting
*/
@@ -392,13 +394,13 @@
/**
* 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 <tt>e</tt> such that
- * <tt>o.equals(e)</tt> (if such an element exists).
- * Returns <tt>true</tt> if this deque contained the specified element
+ * 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 <tt>true</tt> if an element was removed as a result of this call
+ * @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
* (<a href="../Collection.html#optional-restrictions">optional</a>)
@@ -410,13 +412,13 @@
/**
* 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 <tt>e</tt> such that
- * <tt>o.equals(e)</tt> (if such an element exists).
- * Returns <tt>true</tt> if this deque contained the specified element
+ * 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 <tt>true</tt> if an element was removed as a result of this call
+ * @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
* (<a href="../Collection.html#optional-restrictions">optional</a>)
@@ -431,8 +433,8 @@
* Inserts the specified element into the queue represented by this deque
* (in other words, at the tail of this deque) if it is possible to do so
* immediately without violating capacity restrictions, returning
- * <tt>true</tt> upon success and throwing an
- * <tt>IllegalStateException</tt> if no space is currently available.
+ * {@code true} upon success and throwing an
+ * {@code IllegalStateException} if no space is currently available.
* When using a capacity-restricted deque, it is generally preferable to
* use {@link #offer(Object) offer}.
*
@@ -452,7 +454,7 @@
* Inserts the specified element into the queue represented by this deque
* (in other words, at the tail of this deque) if it is possible to do so
* immediately without violating capacity restrictions, returning
- * <tt>true</tt> upon success and <tt>false</tt> if no space is currently
+ * {@code true} upon success and {@code false} if no space is currently
* available. When using a capacity-restricted deque, this method is
* generally preferable to the {@link #add} method, which can fail to
* insert an element only by throwing an exception.
@@ -494,8 +496,8 @@
* {@link #offerLast(Object,long,TimeUnit) offerLast}.
*
* @param e the element to add
- * @return <tt>true</tt> if the element was added to this deque, else
- * <tt>false</tt>
+ * @return {@code true} if the element was added to this deque, else
+ * {@code false}
* @throws InterruptedException {@inheritDoc}
* @throws ClassCastException if the class of the specified element
* prevents it from being added to this deque
@@ -522,11 +524,11 @@
/**
* Retrieves and removes the head of the queue represented by this deque
* (in other words, the first element of this deque), or returns
- * <tt>null</tt> if this deque is empty.
+ * {@code null} if this deque is empty.
*
* <p>This method is equivalent to {@link #pollFirst()}.
*
- * @return the head of this deque, or <tt>null</tt> if this deque is empty
+ * @return the head of this deque, or {@code null} if this deque is empty
*/
E poll();
@@ -550,7 +552,7 @@
* <p>This method is equivalent to
* {@link #pollFirst(long,TimeUnit) pollFirst}.
*
- * @return the head of this deque, or <tt>null</tt> if the
+ * @return the head of this deque, or {@code null} if the
* specified waiting time elapses before an element is available
* @throws InterruptedException if interrupted while waiting
*/
@@ -573,27 +575,27 @@
/**
* Retrieves, but does not remove, the head of the queue represented by
* this deque (in other words, the first element of this deque), or
- * returns <tt>null</tt> if this deque is empty.
+ * returns {@code null} if this deque is empty.
*
* <p>This method is equivalent to {@link #peekFirst() peekFirst}.
*
- * @return the head of this deque, or <tt>null</tt> if this deque is empty
+ * @return the head of this deque, or {@code null} if this deque is empty
*/
E peek();
/**
* 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 <tt>e</tt> such that
- * <tt>o.equals(e)</tt> (if such an element exists).
- * Returns <tt>true</tt> if this deque contained the specified element
+ * 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).
*
* <p>This method is equivalent to
* {@link #removeFirstOccurrence(Object) removeFirstOccurrence}.
*
* @param o element to be removed from this deque, if present
- * @return <tt>true</tt> if this deque changed as a result of the call
+ * @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
* (<a href="../Collection.html#optional-restrictions">optional</a>)
@@ -603,12 +605,12 @@
boolean remove(Object o);
/**
- * Returns <tt>true</tt> if this deque contains the specified element.
- * More formally, returns <tt>true</tt> if and only if this deque contains
- * at least one element <tt>e</tt> such that <tt>o.equals(e)</tt>.
+ * 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 object to be checked for containment in this deque
- * @return <tt>true</tt> if this deque contains the specified element
+ * @return {@code true} if this deque contains the specified element
* @throws ClassCastException if the class of the specified element
* is incompatible with this deque
* (<a href="../Collection.html#optional-restrictions">optional</a>)
@@ -635,9 +637,10 @@
// *** Stack methods ***
/**
- * Pushes an element onto the stack represented by this deque. In other
- * words, inserts the element at the front of this deque unless it would
- * violate capacity restrictions.
+ * Pushes an element onto the stack represented by this deque (in other
+ * words, at the head of this deque) if it is possible to do so
+ * immediately without violating capacity restrictions, throwing an
+ * {@code IllegalStateException} if no space is currently available.
*
* <p>This method is equivalent to {@link #addFirst(Object) addFirst}.
*
--- a/jdk/src/share/classes/java/util/concurrent/BlockingQueue.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/java/util/concurrent/BlockingQueue.java Wed Jul 03 14:35:10 2013 -0700
@@ -44,17 +44,18 @@
* element, and wait for space to become available in the queue when
* storing an element.
*
- * <p><tt>BlockingQueue</tt> methods come in four forms, with different ways
+ * <p>{@code BlockingQueue} methods come in four forms, with different ways
* of handling operations that cannot be satisfied immediately, but may be
* satisfied at some point in the future:
* one throws an exception, the second returns a special value (either
- * <tt>null</tt> or <tt>false</tt>, depending on the operation), the third
+ * {@code null} or {@code false}, depending on the operation), the third
* blocks the current thread indefinitely until the operation can succeed,
* and the fourth blocks for only a given maximum time limit before giving
* up. These methods are summarized in the following table:
*
* <p>
* <table BORDER CELLPADDING=3 CELLSPACING=1>
+ * <caption>Summary of BlockingQueue methods</caption>
* <tr>
* <td></td>
* <td ALIGN=CENTER><em>Throws exception</em></td>
@@ -85,37 +86,37 @@
* </tr>
* </table>
*
- * <p>A <tt>BlockingQueue</tt> does not accept <tt>null</tt> elements.
- * Implementations throw <tt>NullPointerException</tt> on attempts
- * to <tt>add</tt>, <tt>put</tt> or <tt>offer</tt> a <tt>null</tt>. A
- * <tt>null</tt> is used as a sentinel value to indicate failure of
- * <tt>poll</tt> operations.
+ * <p>A {@code BlockingQueue} does not accept {@code null} elements.
+ * Implementations throw {@code NullPointerException} on attempts
+ * to {@code add}, {@code put} or {@code offer} a {@code null}. A
+ * {@code null} is used as a sentinel value to indicate failure of
+ * {@code poll} operations.
*
- * <p>A <tt>BlockingQueue</tt> may be capacity bounded. At any given
- * time it may have a <tt>remainingCapacity</tt> beyond which no
- * additional elements can be <tt>put</tt> without blocking.
- * A <tt>BlockingQueue</tt> without any intrinsic capacity constraints always
- * reports a remaining capacity of <tt>Integer.MAX_VALUE</tt>.
+ * <p>A {@code BlockingQueue} may be capacity bounded. At any given
+ * time it may have a {@code remainingCapacity} beyond which no
+ * additional elements can be {@code put} without blocking.
+ * A {@code BlockingQueue} without any intrinsic capacity constraints always
+ * reports a remaining capacity of {@code Integer.MAX_VALUE}.
*
- * <p> <tt>BlockingQueue</tt> implementations are designed to be used
+ * <p>{@code BlockingQueue} implementations are designed to be used
* primarily for producer-consumer queues, but additionally support
* the {@link java.util.Collection} interface. So, for example, it is
* possible to remove an arbitrary element from a queue using
- * <tt>remove(x)</tt>. However, such operations are in general
+ * {@code remove(x)}. However, such operations are in general
* <em>not</em> performed very efficiently, and are intended for only
* occasional use, such as when a queued message is cancelled.
*
- * <p> <tt>BlockingQueue</tt> implementations are thread-safe. All
+ * <p>{@code BlockingQueue} implementations are thread-safe. All
* queuing methods achieve their effects atomically using internal
* locks or other forms of concurrency control. However, the
- * <em>bulk</em> Collection operations <tt>addAll</tt>,
- * <tt>containsAll</tt>, <tt>retainAll</tt> and <tt>removeAll</tt> are
+ * <em>bulk</em> Collection operations {@code addAll},
+ * {@code containsAll}, {@code retainAll} and {@code removeAll} are
* <em>not</em> necessarily performed atomically unless specified
* otherwise in an implementation. So it is possible, for example, for
- * <tt>addAll(c)</tt> to fail (throwing an exception) after adding
- * only some of the elements in <tt>c</tt>.
+ * {@code addAll(c)} to fail (throwing an exception) after adding
+ * only some of the elements in {@code c}.
*
- * <p>A <tt>BlockingQueue</tt> does <em>not</em> intrinsically support
+ * <p>A {@code BlockingQueue} does <em>not</em> intrinsically support
* any kind of "close" or "shutdown" operation to
* indicate that no more items will be added. The needs and usage of
* such features tend to be implementation-dependent. For example, a
@@ -125,7 +126,7 @@
*
* <p>
* Usage example, based on a typical producer-consumer scenario.
- * Note that a <tt>BlockingQueue</tt> can safely be used with multiple
+ * Note that a {@code BlockingQueue} can safely be used with multiple
* producers and multiple consumers.
* <pre> {@code
* class Producer implements Runnable {
@@ -181,13 +182,13 @@
/**
* Inserts the specified element into this queue if it is possible to do
* so immediately without violating capacity restrictions, returning
- * <tt>true</tt> upon success and throwing an
- * <tt>IllegalStateException</tt> if no space is currently available.
+ * {@code true} upon success and throwing an
+ * {@code IllegalStateException} if no space is currently available.
* When using a capacity-restricted queue, it is generally preferable to
* use {@link #offer(Object) offer}.
*
* @param e the element to add
- * @return <tt>true</tt> (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
@@ -201,14 +202,14 @@
/**
* Inserts the specified element into this queue if it is possible to do
* so immediately without violating capacity restrictions, returning
- * <tt>true</tt> upon success and <tt>false</tt> if no space is currently
+ * {@code true} upon success and {@code false} if no space is currently
* available. When using a capacity-restricted queue, this method is
* generally preferable to {@link #add}, which can fail to insert an
* element only by throwing an exception.
*
* @param e the element to add
- * @return <tt>true</tt> if the element was added to this queue, else
- * <tt>false</tt>
+ * @return {@code true} if the element was added to this queue, else
+ * {@code false}
* @throws ClassCastException if the class of the specified element
* prevents it from being added to this queue
* @throws NullPointerException if the specified element is null
@@ -237,10 +238,10 @@
*
* @param e the element to add
* @param timeout how long to wait before giving up, in units of
- * <tt>unit</tt>
- * @param unit a <tt>TimeUnit</tt> determining how to interpret the
- * <tt>timeout</tt> parameter
- * @return <tt>true</tt> if successful, or <tt>false</tt> if
+ * {@code unit}
+ * @param unit a {@code TimeUnit} determining how to interpret the
+ * {@code timeout} parameter
+ * @return {@code true} if successful, or {@code false} if
* the specified waiting time elapses before space is available
* @throws InterruptedException if interrupted while waiting
* @throws ClassCastException if the class of the specified element
@@ -266,10 +267,10 @@
* specified wait time if necessary for an element to become available.
*
* @param timeout how long to wait before giving up, in units of
- * <tt>unit</tt>
- * @param unit a <tt>TimeUnit</tt> determining how to interpret the
- * <tt>timeout</tt> parameter
- * @return the head of this queue, or <tt>null</tt> if the
+ * {@code unit}
+ * @param unit a {@code TimeUnit} determining how to interpret the
+ * {@code timeout} parameter
+ * @return the head of this queue, or {@code null} if the
* specified waiting time elapses before an element is available
* @throws InterruptedException if interrupted while waiting
*/
@@ -279,11 +280,11 @@
/**
* Returns the number of additional elements that this queue can ideally
* (in the absence of memory or resource constraints) accept without
- * blocking, or <tt>Integer.MAX_VALUE</tt> if there is no intrinsic
+ * blocking, or {@code Integer.MAX_VALUE} if there is no intrinsic
* limit.
*
* <p>Note that you <em>cannot</em> always tell if an attempt to insert
- * an element will succeed by inspecting <tt>remainingCapacity</tt>
+ * an element will succeed by inspecting {@code remainingCapacity}
* because it may be the case that another thread is about to
* insert or remove an element.
*
@@ -293,14 +294,14 @@
/**
* Removes a single instance of the specified element from this queue,
- * if it is present. More formally, removes an element <tt>e</tt> such
- * that <tt>o.equals(e)</tt>, if this queue contains one or more such
+ * if it is present. More formally, removes an element {@code e} such
+ * that {@code o.equals(e)}, if this queue contains one or more such
* elements.
- * Returns <tt>true</tt> if this queue contained the specified element
+ * Returns {@code true} if this queue contained the specified element
* (or equivalently, if this queue changed as a result of the call).
*
* @param o element to be removed from this queue, if present
- * @return <tt>true</tt> 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 the specified element
* is incompatible with this queue
* (<a href="../Collection.html#optional-restrictions">optional</a>)
@@ -310,12 +311,12 @@
boolean remove(Object o);
/**
- * Returns <tt>true</tt> if this queue contains the specified element.
- * More formally, returns <tt>true</tt> if and only if this queue contains
- * at least one element <tt>e</tt> such that <tt>o.equals(e)</tt>.
+ * Returns {@code true} if this queue contains the specified element.
+ * More formally, returns {@code true} if and only if this queue contains
+ * at least one element {@code e} such that {@code o.equals(e)}.
*
* @param o object to be checked for containment in this queue
- * @return <tt>true</tt> if this queue contains the specified element
+ * @return {@code true} if this queue contains the specified element
* @throws ClassCastException if the class of the specified element
* is incompatible with this queue
* (<a href="../Collection.html#optional-restrictions">optional</a>)
@@ -329,10 +330,10 @@
* to the given collection. This operation may be more
* efficient than repeatedly polling this queue. A failure
* encountered while attempting to add elements to
- * collection <tt>c</tt> may result in elements being in neither,
+ * collection {@code c} may result in elements being in neither,
* either or both collections when the associated exception is
* thrown. Attempts to drain a queue to itself result in
- * <tt>IllegalArgumentException</tt>. 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.
*
@@ -353,10 +354,10 @@
* Removes at most the given number of available elements from
* this queue and adds them to the given collection. A failure
* encountered while attempting to add elements to
- * collection <tt>c</tt> may result in elements being in neither,
+ * collection {@code c} may result in elements being in neither,
* either or both collections when the associated exception is
* thrown. Attempts to drain a queue to itself result in
- * <tt>IllegalArgumentException</tt>. 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.
*
--- a/jdk/src/share/classes/java/util/concurrent/BrokenBarrierException.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/java/util/concurrent/BrokenBarrierException.java Wed Jul 03 14:35:10 2013 -0700
@@ -49,13 +49,13 @@
private static final long serialVersionUID = 7117394618823254244L;
/**
- * Constructs a <tt>BrokenBarrierException</tt> with no specified detail
+ * Constructs a {@code BrokenBarrierException} with no specified detail
* message.
*/
public BrokenBarrierException() {}
/**
- * Constructs a <tt>BrokenBarrierException</tt> with the specified
+ * Constructs a {@code BrokenBarrierException} with the specified
* detail message.
*
* @param message the detail message
--- a/jdk/src/share/classes/java/util/concurrent/ConcurrentLinkedDeque.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/java/util/concurrent/ConcurrentLinkedDeque.java Wed Jul 03 14:35:10 2013 -0700
@@ -42,6 +42,9 @@
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Queue;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import java.util.function.Consumer;
/**
* An unbounded concurrent {@linkplain Deque deque} based on linked nodes.
@@ -816,7 +819,7 @@
* Creates an array list and fills it with elements of this list.
* Used by toArray.
*
- * @return the arrayList
+ * @return the array list
*/
private ArrayList<E> toArrayList() {
ArrayList<E> list = new ArrayList<E>();
@@ -1024,11 +1027,27 @@
}
public E poll() { return pollFirst(); }
+ public E peek() { return peekFirst(); }
+
+ /**
+ * @throws NoSuchElementException {@inheritDoc}
+ */
public E remove() { return removeFirst(); }
- public E peek() { return peekFirst(); }
+
+ /**
+ * @throws NoSuchElementException {@inheritDoc}
+ */
+ public E pop() { return removeFirst(); }
+
+ /**
+ * @throws NoSuchElementException {@inheritDoc}
+ */
public E element() { return getFirst(); }
+
+ /**
+ * @throws NullPointerException {@inheritDoc}
+ */
public void push(E e) { addFirst(e); }
- public E pop() { return removeFirst(); }
/**
* Removes the first element {@code e} such that
@@ -1385,6 +1404,99 @@
Node<E> nextNode(Node<E> p) { return pred(p); }
}
+ /** A customized variant of Spliterators.IteratorSpliterator */
+ static final class CLDSpliterator<E> implements Spliterator<E> {
+ static final int MAX_BATCH = 1 << 25; // max batch array size;
+ final ConcurrentLinkedDeque<E> queue;
+ Node<E> current; // current node; null until initialized
+ int batch; // batch size for splits
+ boolean exhausted; // true when no more nodes
+ CLDSpliterator(ConcurrentLinkedDeque<E> queue) {
+ this.queue = queue;
+ }
+
+ public Spliterator<E> trySplit() {
+ Node<E> p;
+ final ConcurrentLinkedDeque<E> 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.first()) != null)) {
+ if (p.item == null && p == (p = p.next))
+ current = p = q.first();
+ if (p != null && p.next != null) {
+ Object[] a = new Object[n];
+ int i = 0;
+ do {
+ if ((a[i] = p.item) != null)
+ ++i;
+ if (p == (p = p.next))
+ p = q.first();
+ } while (p != null && i < n);
+ if ((current = p) == null)
+ exhausted = true;
+ if (i > 0) {
+ batch = i;
+ return Spliterators.spliterator
+ (a, 0, i, Spliterator.ORDERED | Spliterator.NONNULL |
+ Spliterator.CONCURRENT);
+ }
+ }
+ }
+ return null;
+ }
+
+ public void forEachRemaining(Consumer<? super E> action) {
+ Node<E> p;
+ if (action == null) throw new NullPointerException();
+ final ConcurrentLinkedDeque<E> q = this.queue;
+ if (!exhausted &&
+ ((p = current) != null || (p = q.first()) != null)) {
+ exhausted = true;
+ do {
+ E e = p.item;
+ if (p == (p = p.next))
+ p = q.first();
+ if (e != null)
+ action.accept(e);
+ } while (p != null);
+ }
+ }
+
+ public boolean tryAdvance(Consumer<? super E> action) {
+ Node<E> p;
+ if (action == null) throw new NullPointerException();
+ final ConcurrentLinkedDeque<E> q = this.queue;
+ if (!exhausted &&
+ ((p = current) != null || (p = q.first()) != null)) {
+ E e;
+ do {
+ e = p.item;
+ if (p == (p = p.next))
+ p = q.first();
+ } while (e == null && p != null);
+ if ((current = p) == null)
+ exhausted = true;
+ if (e != null) {
+ action.accept(e);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public long estimateSize() { return Long.MAX_VALUE; }
+
+ public int characteristics() {
+ return Spliterator.ORDERED | Spliterator.NONNULL |
+ Spliterator.CONCURRENT;
+ }
+ }
+
+ public Spliterator<E> spliterator() {
+ return new CLDSpliterator<E>(this);
+ }
+
/**
* Saves this deque to a stream (that is, serializes it).
*
--- a/jdk/src/share/classes/java/util/concurrent/ConcurrentLinkedQueue.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/java/util/concurrent/ConcurrentLinkedQueue.java Wed Jul 03 14:35:10 2013 -0700
@@ -41,6 +41,9 @@
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Queue;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import java.util.function.Consumer;
/**
* An unbounded thread-safe {@linkplain Queue queue} based on linked nodes.
@@ -56,7 +59,7 @@
* Like most other concurrent collection implementations, this class
* does not permit the use of {@code null} elements.
*
- * <p>This implementation employs an efficient "wait-free"
+ * <p>This implementation employs an efficient <em>non-blocking</em>
* algorithm based on one described in <a
* href="http://www.cs.rochester.edu/u/michael/PODC96.html"> Simple,
* Fast, and Practical Non-Blocking and Blocking Concurrent Queue
@@ -295,7 +298,7 @@
}
/**
- * Try to CAS head to p. If successful, repoint old head to itself
+ * Tries to CAS head to p. If successful, repoint old head to itself
* as sentinel for succ(), below.
*/
final void updateHead(Node<E> h, Node<E> p) {
@@ -792,6 +795,96 @@
tail = t;
}
+ /** A customized variant of Spliterators.IteratorSpliterator */
+ static final class CLQSpliterator<E> implements Spliterator<E> {
+ static final int MAX_BATCH = 1 << 25; // max batch array size;
+ final ConcurrentLinkedQueue<E> queue;
+ Node<E> current; // current node; null until initialized
+ int batch; // batch size for splits
+ boolean exhausted; // true when no more nodes
+ CLQSpliterator(ConcurrentLinkedQueue<E> queue) {
+ this.queue = queue;
+ }
+
+ public Spliterator<E> trySplit() {
+ Node<E> p;
+ final ConcurrentLinkedQueue<E> 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.first()) != null) &&
+ p.next != null) {
+ Object[] a = new Object[n];
+ int i = 0;
+ do {
+ if ((a[i] = p.item) != null)
+ ++i;
+ if (p == (p = p.next))
+ p = q.first();
+ } while (p != null && i < n);
+ if ((current = p) == null)
+ exhausted = true;
+ if (i > 0) {
+ batch = i;
+ return Spliterators.spliterator
+ (a, 0, i, Spliterator.ORDERED | Spliterator.NONNULL |
+ Spliterator.CONCURRENT);
+ }
+ }
+ return null;
+ }
+
+ public void forEachRemaining(Consumer<? super E> action) {
+ Node<E> p;
+ if (action == null) throw new NullPointerException();
+ final ConcurrentLinkedQueue<E> q = this.queue;
+ if (!exhausted &&
+ ((p = current) != null || (p = q.first()) != null)) {
+ exhausted = true;
+ do {
+ E e = p.item;
+ if (p == (p = p.next))
+ p = q.first();
+ if (e != null)
+ action.accept(e);
+ } while (p != null);
+ }
+ }
+
+ public boolean tryAdvance(Consumer<? super E> action) {
+ Node<E> p;
+ if (action == null) throw new NullPointerException();
+ final ConcurrentLinkedQueue<E> q = this.queue;
+ if (!exhausted &&
+ ((p = current) != null || (p = q.first()) != null)) {
+ E e;
+ do {
+ e = p.item;
+ if (p == (p = p.next))
+ p = q.first();
+ } while (e == null && p != null);
+ if ((current = p) == null)
+ exhausted = true;
+ if (e != null) {
+ action.accept(e);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public long estimateSize() { return Long.MAX_VALUE; }
+
+ public int characteristics() {
+ return Spliterator.ORDERED | Spliterator.NONNULL |
+ Spliterator.CONCURRENT;
+ }
+ }
+
+ public Spliterator<E> spliterator() {
+ return new CLQSpliterator<E>(this);
+ }
+
/**
* Throws NullPointerException if argument is null.
*
--- a/jdk/src/share/classes/java/util/concurrent/ConcurrentSkipListMap.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/java/util/concurrent/ConcurrentSkipListMap.java Wed Jul 03 14:35:10 2013 -0700
@@ -34,7 +34,25 @@
*/
package java.util.concurrent;
-import java.util.*;
+import java.util.AbstractCollection;
+import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.NavigableSet;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.Spliterator;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.function.BiConsumer;
+import java.util.function.Function;
/**
* A scalable concurrent {@link ConcurrentNavigableMap} implementation.
@@ -45,38 +63,38 @@
* <p>This class implements a concurrent variant of <a
* href="http://en.wikipedia.org/wiki/Skip_list" target="_top">SkipLists</a>
* providing expected average <i>log(n)</i> time cost for the
- * <tt>containsKey</tt>, <tt>get</tt>, <tt>put</tt> and
- * <tt>remove</tt> operations and their variants. Insertion, removal,
+ * {@code containsKey}, {@code get}, {@code put} and
+ * {@code remove} operations and their variants. Insertion, removal,
* update, and access operations safely execute concurrently by
* multiple threads. Iterators are <i>weakly consistent</i>, returning
* elements reflecting the state of the map at some point at or since
* the creation of the iterator. They do <em>not</em> throw {@link
- * ConcurrentModificationException}, and may proceed concurrently with
- * other operations. Ascending key ordered views and their iterators
- * are faster than descending ones.
+ * java.util.ConcurrentModificationException ConcurrentModificationException},
+ * and may proceed concurrently with other operations. Ascending key ordered
+ * views and their iterators are faster than descending ones.
*
- * <p>All <tt>Map.Entry</tt> pairs returned by methods in this class
+ * <p>All {@code Map.Entry} pairs returned by methods in this class
* and its views represent snapshots of mappings at the time they were
- * produced. They do <em>not</em> support the <tt>Entry.setValue</tt>
+ * produced. They do <em>not</em> support the {@code Entry.setValue}
* method. (Note however that it is possible to change mappings in the
- * associated map using <tt>put</tt>, <tt>putIfAbsent</tt>, or
- * <tt>replace</tt>, depending on exactly which effect you need.)
+ * associated map using {@code put}, {@code putIfAbsent}, or
+ * {@code replace}, depending on exactly which effect you need.)
*
- * <p>Beware that, unlike in most collections, the <tt>size</tt>
+ * <p>Beware that, unlike in most collections, the {@code size}
* method is <em>not</em> a constant-time operation. Because of the
* asynchronous nature of these maps, determining the current number
* of elements requires a traversal of the elements, and so may report
* inaccurate results if this collection is modified during traversal.
- * Additionally, the bulk operations <tt>putAll</tt>, <tt>equals</tt>,
- * <tt>toArray</tt>, <tt>containsValue</tt>, and <tt>clear</tt> are
+ * Additionally, the bulk operations {@code putAll}, {@code equals},
+ * {@code toArray}, {@code containsValue}, and {@code clear} are
* <em>not</em> guaranteed to be performed atomically. For example, an
- * iterator operating concurrently with a <tt>putAll</tt> operation
+ * iterator operating concurrently with a {@code putAll} operation
* might view only some of the added elements.
*
* <p>This class and its views and iterators implement all of the
* <em>optional</em> methods of the {@link Map} and {@link Iterator}
* interfaces. Like most other concurrent collections, this class does
- * <em>not</em> permit the use of <tt>null</tt> keys or values because some
+ * <em>not</em> permit the use of {@code null} keys or values because some
* null return values cannot be reliably distinguished from the absence of
* elements.
*
@@ -89,7 +107,6 @@
* @param <V> the type of mapped values
* @since 1.6
*/
-@SuppressWarnings("unchecked")
public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
implements ConcurrentNavigableMap<K,V>,
Cloneable,
@@ -257,7 +274,7 @@
*
* Indexing uses skip list parameters that maintain good search
* performance while using sparser-than-usual indices: The
- * hardwired parameters k=1, p=0.5 (see method randomLevel) mean
+ * hardwired parameters k=1, p=0.5 (see method doPut) mean
* that about one-quarter of the nodes have indices. Of those that
* do, half have one level, a quarter have two, and so on (see
* Pugh's Skip List Cookbook, sec 3.4). The expected total space
@@ -295,6 +312,20 @@
* there is a fair amount of near-duplication of code to handle
* variants.
*
+ * To produce random values without interference across threads,
+ * we use within-JDK thread local random support (via the
+ * "secondary seed", to avoid interference with user-level
+ * ThreadLocalRandom.)
+ *
+ * A previous version of this class wrapped non-comparable keys
+ * with their comparators to emulate Comparables when using
+ * comparators vs Comparables. However, JVMs now appear to better
+ * handle infusing comparator-vs-comparable choice into search
+ * loops. Static method cpr(comparator, x, y) is used for all
+ * comparisons, which works well as long as the comparator
+ * argument is set up outside of loops (thus sometimes passed as
+ * an argument to internal methods) to avoid field re-reads.
+ *
* For explanation of algorithms sharing at least a couple of
* features with this one, see Mikhail Fomitchev's thesis
* (http://www.cs.yorku.ca/~mikhail/), Keir Fraser's thesis
@@ -323,12 +354,6 @@
private static final long serialVersionUID = -8627078645895051609L;
/**
- * Generates the initial random seed for the cheaper per-instance
- * random number generators used in randomLevel.
- */
- private static final Random seedGenerator = new Random();
-
- /**
* Special value used to identify base-level header
*/
private static final Object BASE_HEADER = new Object();
@@ -339,17 +364,12 @@
private transient volatile HeadIndex<K,V> head;
/**
- * The comparator used to maintain order in this map, or null
- * if using natural ordering.
+ * The comparator used to maintain order in this map, or null if
+ * using natural ordering. (Non-private to simplify access in
+ * nested classes.)
* @serial
*/
- private final Comparator<? super K> comparator;
-
- /**
- * Seed for simple random number generator. Not volatile since it
- * doesn't matter too much if different threads don't see updates.
- */
- private transient int randomSeed;
+ final Comparator<? super K> comparator;
/** Lazily initialized key set */
private transient KeySet<K> keySet;
@@ -365,12 +385,11 @@
* clear, readObject. and ConcurrentSkipListSet.clone.
* (Note that comparator must be separately initialized.)
*/
- final void initialize() {
+ private void initialize() {
keySet = null;
entrySet = null;
values = null;
descendingMap = null;
- randomSeed = seedGenerator.nextInt() | 0x0100; // ensure nonzero
head = new HeadIndex<K,V>(new Node<K,V>(null, BASE_HEADER, null),
null, null, 1);
}
@@ -438,7 +457,7 @@
* because callers will have already read value field and need
* to use that read (not another done here) and so directly
* test if value points to node.
- * @param n a possibly null reference to a node
+ *
* @return true if this node is a marker node
*/
boolean isMarker() {
@@ -477,7 +496,7 @@
*/
if (f == next && this == b.next) {
if (f == null || f.value != f) // not already marked
- appendMarker(f);
+ casNext(f, new Node<K,V>(f));
else
b.casNext(this, f.next);
}
@@ -487,13 +506,14 @@
* Returns value if this node contains a valid key-value pair,
* else null.
* @return this node's value if it isn't a marker or header or
- * is deleted, else null.
+ * is deleted, else null
*/
V getValidValue() {
Object v = value;
if (v == this || v == BASE_HEADER)
return null;
- return (V)v;
+ @SuppressWarnings("unchecked") V vv = (V)v;
+ return vv;
}
/**
@@ -502,10 +522,11 @@
* @return new entry or null
*/
AbstractMap.SimpleImmutableEntry<K,V> createSnapshot() {
- V v = getValidValue();
- if (v == null)
+ Object v = value;
+ if (v == null || v == this || v == BASE_HEADER)
return null;
- return new AbstractMap.SimpleImmutableEntry<K,V>(key, v);
+ @SuppressWarnings("unchecked") V vv = (V)v;
+ return new AbstractMap.SimpleImmutableEntry<K,V>(key, vv);
}
// UNSAFE mechanics
@@ -588,7 +609,7 @@
* @return true if successful
*/
final boolean unlink(Index<K,V> succ) {
- return !indexesDeletedNode() && casRight(succ, succ.right);
+ return node.value != null && casRight(succ, succ.right);
}
// Unsafe mechanics
@@ -622,80 +643,12 @@
/* ---------------- Comparison utilities -------------- */
/**
- * Represents a key with a comparator as a Comparable.
- *
- * Because most sorted collections seem to use natural ordering on
- * Comparables (Strings, Integers, etc), most internal methods are
- * geared to use them. This is generally faster than checking
- * per-comparison whether to use comparator or comparable because
- * it doesn't require a (Comparable) cast for each comparison.
- * (Optimizers can only sometimes remove such redundant checks
- * themselves.) When Comparators are used,
- * ComparableUsingComparators are created so that they act in the
- * same way as natural orderings. This penalizes use of
- * Comparators vs Comparables, which seems like the right
- * tradeoff.
- */
- static final class ComparableUsingComparator<K> implements Comparable<K> {
- final K actualKey;
- final Comparator<? super K> cmp;
- ComparableUsingComparator(K key, Comparator<? super K> cmp) {
- this.actualKey = key;
- this.cmp = cmp;
- }
- public int compareTo(K k2) {
- return cmp.compare(actualKey, k2);
- }
- }
-
- /**
- * If using comparator, return a ComparableUsingComparator, else
- * cast key as Comparable, which may cause ClassCastException,
- * which is propagated back to caller.
+ * Compares using comparator or natural ordering if null.
+ * Called only by methods that have performed required type checks.
*/
- private Comparable<? super K> comparable(Object key)
- throws ClassCastException {
- if (key == null)
- throw new NullPointerException();
- if (comparator != null)
- return new ComparableUsingComparator<K>((K)key, comparator);
- else
- return (Comparable<? super K>)key;
- }
-
- /**
- * Compares using comparator or natural ordering. Used when the
- * ComparableUsingComparator approach doesn't apply.
- */
- int compare(K k1, K k2) throws ClassCastException {
- Comparator<? super K> cmp = comparator;
- if (cmp != null)
- return cmp.compare(k1, k2);
- else
- return ((Comparable<? super K>)k1).compareTo(k2);
- }
-
- /**
- * Returns true if given key greater than or equal to least and
- * strictly less than fence, bypassing either test if least or
- * fence are null. Needed mainly in submap operations.
- */
- boolean inHalfOpenRange(K key, K least, K fence) {
- if (key == null)
- throw new NullPointerException();
- return ((least == null || compare(key, least) >= 0) &&
- (fence == null || compare(key, fence) < 0));
- }
-
- /**
- * Returns true if given key greater than or equal to least and less
- * or equal to fence. Needed mainly in submap operations.
- */
- boolean inOpenRange(K key, K least, K fence) {
- if (key == null)
- throw new NullPointerException();
- return ((least == null || compare(key, least) >= 0) &&
- (fence == null || compare(key, fence) <= 0));
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ static final int cpr(Comparator c, Object x, Object y) {
+ return (c != null) ? c.compare(x, y) : ((Comparable)x).compareTo(y);
}
/* ---------------- Traversal -------------- */
@@ -708,13 +661,11 @@
* @param key the key
* @return a predecessor of key
*/
- private Node<K,V> findPredecessor(Comparable<? super K> key) {
+ private Node<K,V> findPredecessor(Object key, Comparator<? super K> cmp) {
if (key == null)
throw new NullPointerException(); // don't postpone errors
for (;;) {
- Index<K,V> q = head;
- Index<K,V> r = q.right;
- for (;;) {
+ for (Index<K,V> q = head, r = q.right, d;;) {
if (r != null) {
Node<K,V> n = r.node;
K k = n.key;
@@ -724,18 +675,16 @@
r = q.right; // reread r
continue;
}
- if (key.compareTo(k) > 0) {
+ if (cpr(cmp, key, k) > 0) {
q = r;
r = r.right;
continue;
}
}
- Index<K,V> d = q.down;
- if (d != null) {
- q = d;
- r = d.right;
- } else
+ if ((d = q.down) == null)
return q.node;
+ q = d;
+ r = d.right;
}
}
}
@@ -784,54 +733,71 @@
* @param key the key
* @return node holding key, or null if no such
*/
- private Node<K,V> findNode(Comparable<? super K> key) {
- for (;;) {
- Node<K,V> b = findPredecessor(key);
- Node<K,V> n = b.next;
- for (;;) {
+ private Node<K,V> findNode(Object key) {
+ if (key == null)
+ throw new NullPointerException(); // don't postpone errors
+ Comparator<? super K> cmp = comparator;
+ outer: for (;;) {
+ for (Node<K,V> b = findPredecessor(key, cmp), n = b.next;;) {
+ Object v; int c;
if (n == null)
- return null;
+ break outer;
Node<K,V> f = n.next;
if (n != b.next) // inconsistent read
break;
- Object v = n.value;
- if (v == null) { // n is deleted
+ if ((v = n.value) == null) { // n is deleted
n.helpDelete(b, f);
break;
}
- if (v == n || b.value == null) // b is deleted
+ if (b.value == null || v == n) // b is deleted
break;
- int c = key.compareTo(n.key);
- if (c == 0)
+ if ((c = cpr(cmp, key, n.key)) == 0)
return n;
if (c < 0)
- return null;
+ break outer;
b = n;
n = f;
}
}
+ return null;
}
/**
- * Gets value for key using findNode.
- * @param okey the key
+ * Gets value for key. Almost the same as findNode, but returns
+ * the found value (to avoid retries during re-reads)
+ *
+ * @param key the key
* @return the value, or null if absent
*/
- private V doGet(Object okey) {
- Comparable<? super K> key = comparable(okey);
- /*
- * Loop needed here and elsewhere in case value field goes
- * null just as it is about to be returned, in which case we
- * lost a race with a deletion, so must retry.
- */
- for (;;) {
- Node<K,V> n = findNode(key);
- if (n == null)
- return null;
- Object v = n.value;
- if (v != null)
- return (V)v;
+ private V doGet(Object key) {
+ if (key == null)
+ throw new NullPointerException();
+ Comparator<? super K> cmp = comparator;
+ outer: for (;;) {
+ for (Node<K,V> b = findPredecessor(key, cmp), n = b.next;;) {
+ Object v; int c;
+ if (n == null)
+ break outer;
+ Node<K,V> f = n.next;
+ if (n != b.next) // inconsistent read
+ break;
+ if ((v = n.value) == null) { // n is deleted
+ n.helpDelete(b, f);
+ break;
+ }
+ if (b.value == null || v == n) // b is deleted
+ break;
+ if ((c = cpr(cmp, key, n.key)) == 0) {
+ @SuppressWarnings("unchecked") V vv = (V)v;
+ return vv;
+ }
+ if (c < 0)
+ break outer;
+ b = n;
+ n = f;
+ }
}
+ return null;
}
/* ---------------- Insertion -------------- */
@@ -839,187 +805,126 @@
/**
* Main insertion method. Adds element if not present, or
* replaces value if present and onlyIfAbsent is false.
- * @param kkey the key
- * @param value the value that must be associated with key
+ * @param key the key
+ * @param value the value that must be associated with key
* @param onlyIfAbsent if should not insert if already present
* @return the old value, or null if newly inserted
*/
- private V doPut(K kkey, V value, boolean onlyIfAbsent) {
- Comparable<? super K> key = comparable(kkey);
- for (;;) {
- Node<K,V> b = findPredecessor(key);
- Node<K,V> n = b.next;
- for (;;) {
+ private V doPut(K key, V value, boolean onlyIfAbsent) {
+ Node<K,V> z; // added node
+ if (key == null)
+ throw new NullPointerException();
+ Comparator<? super K> cmp = comparator;
+ outer: for (;;) {
+ for (Node<K,V> b = findPredecessor(key, cmp), n = b.next;;) {
if (n != null) {
+ Object v; int c;
Node<K,V> f = n.next;
if (n != b.next) // inconsistent read
break;
- Object v = n.value;
- if (v == null) { // n is deleted
+ if ((v = n.value) == null) { // n is deleted
n.helpDelete(b, f);
break;
}
- if (v == n || b.value == null) // b is deleted
+ if (b.value == null || v == n) // b is deleted
break;
- int c = key.compareTo(n.key);
- if (c > 0) {
+ if ((c = cpr(cmp, key, n.key)) > 0) {
b = n;
n = f;
continue;
}
if (c == 0) {
- if (onlyIfAbsent || n.casValue(v, value))
- return (V)v;
- else
- break; // restart if lost race to replace value
+ if (onlyIfAbsent || n.casValue(v, value)) {
+ @SuppressWarnings("unchecked") V vv = (V)v;
+ return vv;
+ }
+ break; // restart if lost race to replace value
}
// else c < 0; fall through
}
- Node<K,V> z = new Node<K,V>(kkey, value, n);
+ z = new Node<K,V>(key, value, n);
if (!b.casNext(n, z))
break; // restart if lost race to append to b
- int level = randomLevel();
- if (level > 0)
- insertIndex(z, level);
- return null;
+ break outer;
}
}
- }
- /**
- * Returns a random level for inserting a new node.
- * Hardwired to k=1, p=0.5, max 31 (see above and
- * Pugh's "Skip List Cookbook", sec 3.4).
- *
- * This uses the simplest of the generators described in George
- * Marsaglia's "Xorshift RNGs" paper. This is not a high-quality
- * generator but is acceptable here.
- */
- private int randomLevel() {
- int x = randomSeed;
- x ^= x << 13;
- x ^= x >>> 17;
- randomSeed = x ^= x << 5;
- if ((x & 0x80000001) != 0) // test highest and lowest bits
- return 0;
- int level = 1;
- while (((x >>>= 1) & 1) != 0) ++level;
- return level;
- }
-
- /**
- * Creates and adds index nodes for the given node.
- * @param z the node
- * @param level the level of the index
- */
- private void insertIndex(Node<K,V> z, int level) {
- HeadIndex<K,V> h = head;
- int max = h.level;
-
- if (level <= max) {
+ int rnd = ThreadLocalRandom.nextSecondarySeed();
+ if ((rnd & 0x80000001) == 0) { // test highest and lowest bits
+ int level = 1, max;
+ while (((rnd >>>= 1) & 1) != 0)
+ ++level;
Index<K,V> idx = null;
- for (int i = 1; i <= level; ++i)
- idx = new Index<K,V>(z, idx, null);
- addIndex(idx, h, level);
-
- } else { // Add a new level
- /*
- * To reduce interference by other threads checking for
- * empty levels in tryReduceLevel, new levels are added
- * with initialized right pointers. Which in turn requires
- * keeping levels in an array to access them while
- * creating new head index nodes from the opposite
- * direction.
- */
- level = max + 1;
- Index<K,V>[] idxs = (Index<K,V>[])new Index<?,?>[level+1];
- Index<K,V> idx = null;
- for (int i = 1; i <= level; ++i)
- idxs[i] = idx = new Index<K,V>(z, idx, null);
-
- HeadIndex<K,V> oldh;
- int k;
- for (;;) {
- oldh = head;
- int oldLevel = oldh.level;
- if (level <= oldLevel) { // lost race to add level
- k = level;
- break;
- }
- HeadIndex<K,V> newh = oldh;
- Node<K,V> oldbase = oldh.node;
- for (int j = oldLevel+1; j <= level; ++j)
- newh = new HeadIndex<K,V>(oldbase, newh, idxs[j], j);
- if (casHead(oldh, newh)) {
- k = oldLevel;
- break;
+ HeadIndex<K,V> h = head;
+ if (level <= (max = h.level)) {
+ for (int i = 1; i <= level; ++i)
+ idx = new Index<K,V>(z, idx, null);
+ }
+ else { // try to grow by one level
+ level = max + 1; // hold in array and later pick the one to use
+ @SuppressWarnings("unchecked")Index<K,V>[] idxs =
+ (Index<K,V>[])new Index<?,?>[level+1];
+ for (int i = 1; i <= level; ++i)
+ idxs[i] = idx = new Index<K,V>(z, idx, null);
+ for (;;) {
+ h = head;
+ int oldLevel = h.level;
+ if (level <= oldLevel) // lost race to add level
+ break;
+ HeadIndex<K,V> newh = h;
+ Node<K,V> oldbase = h.node;
+ for (int j = oldLevel+1; j <= level; ++j)
+ newh = new HeadIndex<K,V>(oldbase, newh, idxs[j], j);
+ if (casHead(h, newh)) {
+ h = newh;
+ idx = idxs[level = oldLevel];
+ break;
+ }
}
}
- addIndex(idxs[k], oldh, k);
- }
- }
-
- /**
- * Adds given index nodes from given level down to 1.
- * @param idx the topmost index node being inserted
- * @param h the value of head to use to insert. This must be
- * snapshotted by callers to provide correct insertion level
- * @param indexLevel the level of the index
- */
- private void addIndex(Index<K,V> idx, HeadIndex<K,V> h, int indexLevel) {
- // Track next level to insert in case of retries
- int insertionLevel = indexLevel;
- Comparable<? super K> key = comparable(idx.node.key);
- if (key == null) throw new NullPointerException();
+ // find insertion points and splice in
+ splice: for (int insertionLevel = level;;) {
+ int j = h.level;
+ for (Index<K,V> q = h, r = q.right, t = idx;;) {
+ if (q == null || t == null)
+ break splice;
+ if (r != null) {
+ Node<K,V> n = r.node;
+ // compare before deletion check avoids needing recheck
+ int c = cpr(cmp, key, n.key);
+ if (n.value == null) {
+ if (!q.unlink(r))
+ break;
+ r = q.right;
+ continue;
+ }
+ if (c > 0) {
+ q = r;
+ r = r.right;
+ continue;
+ }
+ }
- // Similar to findPredecessor, but adding index nodes along
- // path to key.
- for (;;) {
- int j = h.level;
- Index<K,V> q = h;
- Index<K,V> r = q.right;
- Index<K,V> t = idx;
- for (;;) {
- if (r != null) {
- Node<K,V> n = r.node;
- // compare before deletion check avoids needing recheck
- int c = key.compareTo(n.key);
- if (n.value == null) {
- if (!q.unlink(r))
- break;
- r = q.right;
- continue;
- }
- if (c > 0) {
- q = r;
- r = r.right;
- continue;
+ if (j == insertionLevel) {
+ if (!q.link(r, t))
+ break; // restart
+ if (t.node.value == null) {
+ findNode(key);
+ break splice;
+ }
+ if (--insertionLevel == 0)
+ break splice;
}
+
+ if (--j >= insertionLevel && j < level)
+ t = t.down;
+ q = q.down;
+ r = q.right;
}
-
- if (j == insertionLevel) {
- // Don't insert index if node already deleted
- if (t.indexesDeletedNode()) {
- findNode(key); // cleans up
- return;
- }
- if (!q.link(r, t))
- break; // restart
- if (--insertionLevel == 0) {
- // need final deletion check before return
- if (t.indexesDeletedNode())
- findNode(key);
- return;
- }
- }
-
- if (--j >= insertionLevel && j < indexLevel)
- t = t.down;
- q = q.down;
- r = q.right;
}
}
+ return null;
}
/* ---------------- Deletion -------------- */
@@ -1038,51 +943,52 @@
* search for it, and we'd like to ensure lack of garbage
* retention, so must call to be sure.
*
- * @param okey the key
+ * @param key the key
* @param value if non-null, the value that must be
* associated with key
* @return the node, or null if not found
*/
- final V doRemove(Object okey, Object value) {
- Comparable<? super K> key = comparable(okey);
- for (;;) {
- Node<K,V> b = findPredecessor(key);
- Node<K,V> n = b.next;
- for (;;) {
+ final V doRemove(Object key, Object value) {
+ if (key == null)
+ throw new NullPointerException();
+ Comparator<? super K> cmp = comparator;
+ outer: for (;;) {
+ for (Node<K,V> b = findPredecessor(key, cmp), n = b.next;;) {
+ Object v; int c;
if (n == null)
- return null;
+ break outer;
Node<K,V> f = n.next;
if (n != b.next) // inconsistent read
break;
- Object v = n.value;
- if (v == null) { // n is deleted
+ if ((v = n.value) == null) { // n is deleted
n.helpDelete(b, f);
break;
}
- if (v == n || b.value == null) // b is deleted
+ if (b.value == null || v == n) // b is deleted
break;
- int c = key.compareTo(n.key);
- if (c < 0)
- return null;
+ if ((c = cpr(cmp, key, n.key)) < 0)
+ break outer;
if (c > 0) {
b = n;
n = f;
continue;
}
if (value != null && !value.equals(v))
- return null;
+ break outer;
if (!n.casValue(v, null))
break;
if (!n.appendMarker(f) || !b.casNext(n, f))
- findNode(key); // Retry via findNode
+ findNode(key); // retry via findNode
else {
- findPredecessor(key); // Clean index
+ findPredecessor(key, cmp); // clean index
if (head.right == null)
tryReduceLevel();
}
- return (V)v;
+ @SuppressWarnings("unchecked") V vv = (V)v;
+ return vv;
}
}
+ return null;
}
/**
@@ -1126,11 +1032,9 @@
* Specialized variant of findNode to get first valid node.
* @return first node or null if empty
*/
- Node<K,V> findFirst() {
- for (;;) {
- Node<K,V> b = head.node;
- Node<K,V> n = b.next;
- if (n == null)
+ final Node<K,V> findFirst() {
+ for (Node<K,V> b, n;;) {
+ if ((n = (b = head.node).next) == null)
return null;
if (n.value != null)
return n;
@@ -1142,11 +1046,9 @@
* Removes first entry; returns its snapshot.
* @return null if empty, else snapshot of first entry
*/
- Map.Entry<K,V> doRemoveFirstEntry() {
- for (;;) {
- Node<K,V> b = head.node;
- Node<K,V> n = b.next;
- if (n == null)
+ private Map.Entry<K,V> doRemoveFirstEntry() {
+ for (Node<K,V> b, n;;) {
+ if ((n = (b = head.node).next) == null)
return null;
Node<K,V> f = n.next;
if (n != b.next)
@@ -1161,7 +1063,8 @@
if (!n.appendMarker(f) || !b.casNext(n, f))
findFirst(); // retry
clearIndexToFirst();
- return new AbstractMap.SimpleImmutableEntry<K,V>(n.key, (V)v);
+ @SuppressWarnings("unchecked") V vv = (V)v;
+ return new AbstractMap.SimpleImmutableEntry<K,V>(n.key, vv);
}
}
@@ -1170,8 +1073,7 @@
*/
private void clearIndexToFirst() {
for (;;) {
- Index<K,V> q = head;
- for (;;) {
+ for (Index<K,V> q = head;;) {
Index<K,V> r = q.right;
if (r != null && r.indexesDeletedNode() && !q.unlink(r))
break;
@@ -1184,6 +1086,52 @@
}
}
+ /**
+ * Removes last entry; returns its snapshot.
+ * Specialized variant of doRemove.
+ * @return null if empty, else snapshot of last entry
+ */
+ private Map.Entry<K,V> doRemoveLastEntry() {
+ for (;;) {
+ Node<K,V> b = findPredecessorOfLast();
+ Node<K,V> n = b.next;
+ if (n == null) {
+ if (b.isBaseHeader()) // empty
+ return null;
+ else
+ continue; // all b's successors are deleted; retry
+ }
+ for (;;) {
+ Node<K,V> f = n.next;
+ if (n != b.next) // inconsistent read
+ break;
+ Object v = n.value;
+ if (v == null) { // n is deleted
+ n.helpDelete(b, f);
+ break;
+ }
+ if (b.value == null || v == n) // b is deleted
+ break;
+ if (f != null) {
+ b = n;
+ n = f;
+ continue;
+ }
+ if (!n.casValue(v, null))
+ break;
+ K key = n.key;
+ if (!n.appendMarker(f) || !b.casNext(n, f))
+ findNode(key); // retry via findNode
+ else { // clean index
+ findPredecessor(key, comparator);
+ if (head.right == null)
+ tryReduceLevel();
+ }
+ @SuppressWarnings("unchecked") V vv = (V)v;
+ return new AbstractMap.SimpleImmutableEntry<K,V>(key, vv);
+ }
+ }
+ }
/* ---------------- Finding and removing last element -------------- */
@@ -1191,7 +1139,7 @@
* Specialized version of find to get last valid node.
* @return last node or null if empty
*/
- Node<K,V> findLast() {
+ final Node<K,V> findLast() {
/*
* findPredecessor can't be used to traverse index level
* because this doesn't use comparisons. So traversals of
@@ -1210,9 +1158,7 @@
} else if ((d = q.down) != null) {
q = d;
} else {
- Node<K,V> b = q.node;
- Node<K,V> n = b.next;
- for (;;) {
+ for (Node<K,V> b = q.node, n = b.next;;) {
if (n == null)
return b.isBaseHeader() ? null : b;
Node<K,V> f = n.next; // inconsistent read
@@ -1223,7 +1169,7 @@
n.helpDelete(b, f);
break;
}
- if (v == n || b.value == null) // b is deleted
+ if (b.value == null || v == n) // b is deleted
break;
b = n;
n = f;
@@ -1242,8 +1188,7 @@
*/
private Node<K,V> findPredecessorOfLast() {
for (;;) {
- Index<K,V> q = head;
- for (;;) {
+ for (Index<K,V> q = head;;) {
Index<K,V> d, r;
if ((r = q.right) != null) {
if (r.indexesDeletedNode()) {
@@ -1264,53 +1209,6 @@
}
}
- /**
- * Removes last entry; returns its snapshot.
- * Specialized variant of doRemove.
- * @return null if empty, else snapshot of last entry
- */
- Map.Entry<K,V> doRemoveLastEntry() {
- for (;;) {
- Node<K,V> b = findPredecessorOfLast();
- Node<K,V> n = b.next;
- if (n == null) {
- if (b.isBaseHeader()) // empty
- return null;
- else
- continue; // all b's successors are deleted; retry
- }
- for (;;) {
- Node<K,V> f = n.next;
- if (n != b.next) // inconsistent read
- break;
- Object v = n.value;
- if (v == null) { // n is deleted
- n.helpDelete(b, f);
- break;
- }
- if (v == n || b.value == null) // b is deleted
- break;
- if (f != null) {
- b = n;
- n = f;
- continue;
- }
- if (!n.casValue(v, null))
- break;
- K key = n.key;
- Comparable<? super K> ck = comparable(key);
- if (!n.appendMarker(f) || !b.casNext(n, f))
- findNode(ck); // Retry via findNode
- else {
- findPredecessor(ck); // Clean index
- if (head.right == null)
- tryReduceLevel();
- }
- return new AbstractMap.SimpleImmutableEntry<K,V>(key, (V)v);
- }
- }
- }
-
/* ---------------- Relational operations -------------- */
// Control values OR'ed as arguments to findNear
@@ -1321,29 +1219,28 @@
/**
* Utility for ceiling, floor, lower, higher methods.
- * @param kkey the key
+ * @param key the key
* @param rel the relation -- OR'ed combination of EQ, LT, GT
* @return nearest node fitting relation, or null if no such
*/
- Node<K,V> findNear(K kkey, int rel) {
- Comparable<? super K> key = comparable(kkey);
+ final Node<K,V> findNear(K key, int rel, Comparator<? super K> cmp) {
+ if (key == null)
+ throw new NullPointerException();
for (;;) {
- Node<K,V> b = findPredecessor(key);
- Node<K,V> n = b.next;
- for (;;) {
+ for (Node<K,V> b = findPredecessor(key, cmp), n = b.next;;) {
+ Object v;
if (n == null)
return ((rel & LT) == 0 || b.isBaseHeader()) ? null : b;
Node<K,V> f = n.next;
if (n != b.next) // inconsistent read
break;
- Object v = n.value;
- if (v == null) { // n is deleted
+ if ((v = n.value) == null) { // n is deleted
n.helpDelete(b, f);
break;
}
- if (v == n || b.value == null) // b is deleted
+ if (b.value == null || v == n) // b is deleted
break;
- int c = key.compareTo(n.key);
+ int c = cpr(cmp, key, n.key);
if ((c == 0 && (rel & EQ) != 0) ||
(c < 0 && (rel & LT) == 0))
return n;
@@ -1361,9 +1258,10 @@
* @param rel the relation -- OR'ed combination of EQ, LT, GT
* @return Entry fitting relation, or null if no such
*/
- AbstractMap.SimpleImmutableEntry<K,V> getNear(K key, int rel) {
+ final AbstractMap.SimpleImmutableEntry<K,V> getNear(K key, int rel) {
+ Comparator<? super K> cmp = comparator;
for (;;) {
- Node<K,V> n = findNear(key, rel);
+ Node<K,V> n = findNear(key, rel, cmp);
if (n == null)
return null;
AbstractMap.SimpleImmutableEntry<K,V> e = n.createSnapshot();
@@ -1372,7 +1270,6 @@
}
}
-
/* ---------------- Constructors -------------- */
/**
@@ -1389,7 +1286,7 @@
* comparator.
*
* @param comparator the comparator that will be used to order this map.
- * If <tt>null</tt>, the {@linkplain Comparable natural
+ * If {@code null}, the {@linkplain Comparable natural
* ordering} of the keys will be used.
*/
public ConcurrentSkipListMap(Comparator<? super K> comparator) {
@@ -1403,7 +1300,7 @@
* the keys.
*
* @param m the map whose mappings are to be placed in this map
- * @throws ClassCastException if the keys in <tt>m</tt> are not
+ * @throws ClassCastException if the keys in {@code m} are not
* {@link Comparable}, or are not mutually comparable
* @throws NullPointerException if the specified map or any of its keys
* or values are null
@@ -1430,7 +1327,7 @@
}
/**
- * Returns a shallow copy of this <tt>ConcurrentSkipListMap</tt>
+ * Returns a shallow copy of this {@code ConcurrentSkipListMap}
* instance. (The keys and values themselves are not cloned.)
*
* @return a shallow copy of this map
@@ -1477,8 +1374,14 @@
map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<? extends K, ? extends V> e = it.next();
- int j = randomLevel();
- if (j > h.level) j = h.level + 1;
+ int rnd = ThreadLocalRandom.current().nextInt();
+ int j = 0;
+ if ((rnd & 0x80000001) == 0) {
+ do {
+ ++j;
+ } while (((rnd >>>= 1) & 1) != 0);
+ if (j > h.level) j = h.level + 1;
+ }
K k = e.getKey();
V v = e.getValue();
if (k == null || v == null)
@@ -1511,7 +1414,7 @@
*
* @serialData The key (Object) and value (Object) for each
* key-value mapping represented by the map, followed by
- * <tt>null</tt>. The key-value mappings are emitted in key-order
+ * {@code null}. The key-value mappings are emitted in key-order
* (as determined by the Comparator, or by the keys' natural
* ordering if no Comparator).
*/
@@ -1534,6 +1437,7 @@
/**
* Reconstitutes this map from a stream (that is, deserializes it).
*/
+ @SuppressWarnings("unchecked")
private void readObject(final java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// Read in the Comparator and any hidden stuff
@@ -1569,8 +1473,14 @@
throw new NullPointerException();
K key = (K) k;
V val = (V) v;
- int j = randomLevel();
- if (j > h.level) j = h.level + 1;
+ int rnd = ThreadLocalRandom.current().nextInt();
+ int j = 0;
+ if ((rnd & 0x80000001) == 0) {
+ do {
+ ++j;
+ } while (((rnd >>>= 1) & 1) != 0);
+ if (j > h.level) j = h.level + 1;
+ }
Node<K,V> z = new Node<K,V>(key, val, null);
basepred.next = z;
basepred = z;
@@ -1595,11 +1505,11 @@
/* ------ Map API methods ------ */
/**
- * Returns <tt>true</tt> if this map contains a mapping for the specified
+ * Returns {@code true} if this map contains a mapping for the specified
* key.
*
* @param key key whose presence in this map is to be tested
- * @return <tt>true</tt> if this map contains a mapping for the specified key
+ * @return {@code true} if this map contains a mapping for the specified key
* @throws ClassCastException if the specified key cannot be compared
* with the keys currently in the map
* @throws NullPointerException if the specified key is null
@@ -1627,6 +1537,22 @@
}
/**
+ * Returns the value to which the specified key is mapped,
+ * or the given defaultValue if this map contains no mapping for the key.
+ *
+ * @param key the key
+ * @param defaultValue the value to return if this map contains
+ * no mapping for the given key
+ * @return the mapping for the key, if present; else the defaultValue
+ * @throws NullPointerException if the specified key is null
+ * @since 1.8
+ */
+ public V getOrDefault(Object key, V defaultValue) {
+ V v;
+ return (v = doGet(key)) == null ? defaultValue : v;
+ }
+
+ /**
* Associates the specified value with the specified key in this map.
* If the map previously contained a mapping for the key, the old
* value is replaced.
@@ -1634,7 +1560,7 @@
* @param key key with which the specified value is to be associated
* @param value value to be associated with the specified key
* @return the previous value associated with the specified key, or
- * <tt>null</tt> if there was no mapping for the key
+ * {@code null} if there was no mapping for the key
* @throws ClassCastException if the specified key cannot be compared
* with the keys currently in the map
* @throws NullPointerException if the specified key or value is null
@@ -1650,7 +1576,7 @@
*
* @param key key for which mapping should be removed
* @return the previous value associated with the specified key, or
- * <tt>null</tt> if there was no mapping for the key
+ * {@code null} if there was no mapping for the key
* @throws ClassCastException if the specified key cannot be compared
* with the keys currently in the map
* @throws NullPointerException if the specified key is null
@@ -1660,15 +1586,15 @@
}
/**
- * Returns <tt>true</tt> if this map maps one or more keys to the
+ * Returns {@code true} if this map maps one or more keys to the
* specified value. This operation requires time linear in the
* map size. Additionally, it is possible for the map to change
* during execution of this method, in which case the returned
* result may be inaccurate.
*
* @param value value whose presence in this map is to be tested
- * @return <tt>true</tt> if a mapping to <tt>value</tt> exists;
- * <tt>false</tt> otherwise
+ * @return {@code true} if a mapping to {@code value} exists;
+ * {@code false} otherwise
* @throws NullPointerException if the specified value is null
*/
public boolean containsValue(Object value) {
@@ -1684,8 +1610,8 @@
/**
* Returns the number of key-value mappings in this map. If this map
- * contains more than <tt>Integer.MAX_VALUE</tt> elements, it
- * returns <tt>Integer.MAX_VALUE</tt>.
+ * contains more than {@code Integer.MAX_VALUE} elements, it
+ * returns {@code Integer.MAX_VALUE}.
*
* <p>Beware that, unlike in most collections, this method is
* <em>NOT</em> a constant-time operation. Because of the
@@ -1708,8 +1634,8 @@
}
/**
- * Returns <tt>true</tt> if this map contains no key-value mappings.
- * @return <tt>true</tt> if this map contains no key-value mappings
+ * Returns {@code true} if this map contains no key-value mappings.
+ * @return {@code true} if this map contains no key-value mappings
*/
public boolean isEmpty() {
return findFirst() == null;
@@ -1722,6 +1648,140 @@
initialize();
}
+ /**
+ * If the specified key is not already associated with a value,
+ * attempts to compute its value using the given mapping function
+ * and enters it into this map unless {@code null}. The function
+ * is <em>NOT</em> guaranteed to be applied once atomically only
+ * if the value is not present.
+ *
+ * @param key key with which the specified value is to be associated
+ * @param mappingFunction the function to compute a value
+ * @return the current (existing or computed) value associated with
+ * the specified key, or null if the computed value is null
+ * @throws NullPointerException if the specified key is null
+ * or the mappingFunction is null
+ * @since 1.8
+ */
+ public V computeIfAbsent(K key,
+ Function<? super K, ? extends V> mappingFunction) {
+ if (key == null || mappingFunction == null)
+ throw new NullPointerException();
+ V v, p, r;
+ if ((v = doGet(key)) == null &&
+ (r = mappingFunction.apply(key)) != null)
+ v = (p = doPut(key, r, true)) == null ? r : p;
+ return v;
+ }
+
+ /**
+ * If the value for the specified key is present, attempts to
+ * compute a new mapping given the key and its current mapped
+ * value. The function is <em>NOT</em> guaranteed to be applied
+ * once atomically.
+ *
+ * @param key key with which a value may be associated
+ * @param remappingFunction the function to compute a value
+ * @return the new value associated with the specified key, or null if none
+ * @throws NullPointerException if the specified key is null
+ * or the remappingFunction is null
+ * @since 1.8
+ */
+ public V computeIfPresent(K key,
+ BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
+ if (key == null || remappingFunction == null)
+ throw new NullPointerException();
+ Node<K,V> n; Object v;
+ while ((n = findNode(key)) != null) {
+ if ((v = n.value) != null) {
+ @SuppressWarnings("unchecked") V vv = (V) v;
+ V r = remappingFunction.apply(key, vv);
+ if (r != null) {
+ if (n.casValue(vv, r))
+ return r;
+ }
+ else if (doRemove(key, vv) != null)
+ break;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Attempts to compute a mapping for the specified key and its
+ * current mapped value (or {@code null} if there is no current
+ * mapping). The function is <em>NOT</em> guaranteed to be applied
+ * once atomically.
+ *
+ * @param key key with which the specified value is to be associated
+ * @param remappingFunction the function to compute a value
+ * @return the new value associated with the specified key, or null if none
+ * @throws NullPointerException if the specified key is null
+ * or the remappingFunction is null
+ * @since 1.8
+ */
+ public V compute(K key,
+ BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
+ if (key == null || remappingFunction == null)
+ throw new NullPointerException();
+ for (;;) {
+ Node<K,V> n; Object v; V r;
+ if ((n = findNode(key)) == null) {
+ if ((r = remappingFunction.apply(key, null)) == null)
+ break;
+ if (doPut(key, r, true) == null)
+ return r;
+ }
+ else if ((v = n.value) != null) {
+ @SuppressWarnings("unchecked") V vv = (V) v;
+ if ((r = remappingFunction.apply(key, vv)) != null) {
+ if (n.casValue(vv, r))
+ return r;
+ }
+ else if (doRemove(key, vv) != null)
+ break;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * If the specified key is not already associated with a value,
+ * associates it with the given value. Otherwise, replaces the
+ * value with the results of the given remapping function, or
+ * removes if {@code null}. The function is <em>NOT</em>
+ * guaranteed to be applied once atomically.
+ *
+ * @param key key with which the specified value is to be associated
+ * @param value the value to use if absent
+ * @param remappingFunction the function to recompute a value if present
+ * @return the new value associated with the specified key, or null if none
+ * @throws NullPointerException if the specified key or value is null
+ * or the remappingFunction is null
+ * @since 1.8
+ */
+ public V merge(K key, V value,
+ BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
+ if (key == null || value == null || remappingFunction == null)
+ throw new NullPointerException();
+ for (;;) {
+ Node<K,V> n; Object v; V r;
+ if ((n = findNode(key)) == null) {
+ if (doPut(key, value, true) == null)
+ return value;
+ }
+ else if ((v = n.value) != null) {
+ @SuppressWarnings("unchecked") V vv = (V) v;
+ if ((r = remappingFunction.apply(vv, value)) != null) {
+ if (n.casValue(vv, r))
+ return r;
+ }
+ else if (doRemove(key, vv) != null)
+ return null;
+ }
+ }
+ }
+
/* ---------------- View methods -------------- */
/*
@@ -1744,11 +1804,11 @@
* operations. It does not support the {@code add} or {@code addAll}
* operations.
*
- * <p>The view's {@code iterator} is a "weakly consistent" iterator
- * that will never throw {@link ConcurrentModificationException},
- * and guarantees to traverse elements as they existed upon
- * construction of the iterator, and may (but is not guaranteed to)
- * reflect any modifications subsequent to construction.
+ * <p>The view's {@code iterator} is a "weakly consistent" iterator that
+ * will never throw {@link java.util.ConcurrentModificationException
+ * ConcurrentModificationException}, and guarantees to traverse elements
+ * as they existed upon construction of the iterator, and may (but is not
+ * guaranteed to) reflect any modifications subsequent to construction.
*
* <p>This method is equivalent to method {@code navigableKeySet}.
*
@@ -1771,16 +1831,16 @@
* The collection is backed by the map, so changes to the map are
* reflected in the collection, and vice-versa. The collection
* supports element removal, which removes the corresponding
- * mapping from the map, via the <tt>Iterator.remove</tt>,
- * <tt>Collection.remove</tt>, <tt>removeAll</tt>,
- * <tt>retainAll</tt> and <tt>clear</tt> operations. It does not
- * support the <tt>add</tt> or <tt>addAll</tt> operations.
+ * mapping from the map, via the {@code Iterator.remove},
+ * {@code Collection.remove}, {@code removeAll},
+ * {@code retainAll} and {@code clear} operations. It does not
+ * support the {@code add} or {@code addAll} operations.
*
- * <p>The view's <tt>iterator</tt> is a "weakly consistent" iterator
- * that will never throw {@link ConcurrentModificationException},
- * and guarantees to traverse elements as they existed upon
- * construction of the iterator, and may (but is not guaranteed to)
- * reflect any modifications subsequent to construction.
+ * <p>The view's {@code iterator} is a "weakly consistent" iterator that
+ * will never throw {@link java.util.ConcurrentModificationException
+ * ConcurrentModificationException}, and guarantees to traverse elements
+ * as they existed upon construction of the iterator, and may (but is not
+ * guaranteed to) reflect any modifications subsequent to construction.
*/
public Collection<V> values() {
Values<V> vs = values;
@@ -1793,20 +1853,20 @@
* The set is backed by the map, so changes to the map are
* reflected in the set, and vice-versa. The set supports element
* removal, which removes the corresponding mapping from the map,
- * via the <tt>Iterator.remove</tt>, <tt>Set.remove</tt>,
- * <tt>removeAll</tt>, <tt>retainAll</tt> and <tt>clear</tt>
- * operations. It does not support the <tt>add</tt> or
- * <tt>addAll</tt> operations.
+ * via the {@code Iterator.remove}, {@code Set.remove},
+ * {@code removeAll}, {@code retainAll} and {@code clear}
+ * operations. It does not support the {@code add} or
+ * {@code addAll} operations.
*
- * <p>The view's <tt>iterator</tt> is a "weakly consistent" iterator
- * that will never throw {@link ConcurrentModificationException},
- * and guarantees to traverse elements as they existed upon
- * construction of the iterator, and may (but is not guaranteed to)
- * reflect any modifications subsequent to construction.
+ * <p>The view's {@code iterator} is a "weakly consistent" iterator that
+ * will never throw {@link java.util.ConcurrentModificationException
+ * ConcurrentModificationException}, and guarantees to traverse elements
+ * as they existed upon construction of the iterator, and may (but is not
+ * guaranteed to) reflect any modifications subsequent to construction.
*
- * <p>The <tt>Map.Entry</tt> elements returned by
- * <tt>iterator.next()</tt> do <em>not</em> support the
- * <tt>setValue</tt> operation.
+ * <p>The {@code Map.Entry} elements returned by
+ * {@code iterator.next()} do <em>not</em> support the
+ * {@code setValue} operation.
*
* @return a set view of the mappings contained in this map,
* sorted in ascending key order
@@ -1830,15 +1890,15 @@
/**
* Compares the specified object with this map for equality.
- * Returns <tt>true</tt> if the given object is also a map and the
+ * Returns {@code true} if the given object is also a map and the
* two maps represent the same mappings. More formally, two maps
- * <tt>m1</tt> and <tt>m2</tt> represent the same mappings if
- * <tt>m1.entrySet().equals(m2.entrySet())</tt>. This
+ * {@code m1} and {@code m2} represent the same mappings if
+ * {@code m1.entrySet().equals(m2.entrySet())}. This
* operation may return misleading results if either map is
* concurrently modified during execution of this method.
*
* @param o object to be compared for equality with this map
- * @return <tt>true</tt> if the specified object is equal to this map
+ * @return {@code true} if the specified object is equal to this map
*/
public boolean equals(Object o) {
if (o == this)
@@ -1870,7 +1930,7 @@
* {@inheritDoc}
*
* @return the previous value associated with the specified key,
- * or <tt>null</tt> if there was no mapping for the key
+ * or {@code null} if there was no mapping for the key
* @throws ClassCastException if the specified key cannot be compared
* with the keys currently in the map
* @throws NullPointerException if the specified key or value is null
@@ -1891,9 +1951,7 @@
public boolean remove(Object key, Object value) {
if (key == null)
throw new NullPointerException();
- if (value == null)
- return false;
- return doRemove(key, value) != null;
+ return value != null && doRemove(key, value) != null;
}
/**
@@ -1904,15 +1962,13 @@
* @throws NullPointerException if any of the arguments are null
*/
public boolean replace(K key, V oldValue, V newValue) {
- if (oldValue == null || newValue == null)
+ if (key == null || oldValue == null || newValue == null)
throw new NullPointerException();
- Comparable<? super K> k = comparable(key);
for (;;) {
- Node<K,V> n = findNode(k);
- if (n == null)
+ Node<K,V> n; Object v;
+ if ((n = findNode(key)) == null)
return false;
- Object v = n.value;
- if (v != null) {
+ if ((v = n.value) != null) {
if (!oldValue.equals(v))
return false;
if (n.casValue(v, newValue))
@@ -1925,22 +1981,22 @@
* {@inheritDoc}
*
* @return the previous value associated with the specified key,
- * or <tt>null</tt> if there was no mapping for the key
+ * or {@code null} if there was no mapping for the key
* @throws ClassCastException if the specified key cannot be compared
* with the keys currently in the map
* @throws NullPointerException if the specified key or value is null
*/
public V replace(K key, V value) {
- if (value == null)
+ if (key == null || value == null)
throw new NullPointerException();
- Comparable<? super K> k = comparable(key);
for (;;) {
- Node<K,V> n = findNode(k);
- if (n == null)
+ Node<K,V> n; Object v;
+ if ((n = findNode(key)) == null)
return null;
- Object v = n.value;
- if (v != null && n.casValue(v, value))
- return (V)v;
+ if ((v = n.value) != null && n.casValue(v, value)) {
+ @SuppressWarnings("unchecked") V vv = (V)v;
+ return vv;
+ }
}
}
@@ -2042,9 +2098,9 @@
/**
* Returns a key-value mapping associated with the greatest key
- * strictly less than the given key, or <tt>null</tt> if there is
+ * strictly less than the given key, or {@code null} if there is
* no such key. The returned entry does <em>not</em> support the
- * <tt>Entry.setValue</tt> method.
+ * {@code Entry.setValue} method.
*
* @throws ClassCastException {@inheritDoc}
* @throws NullPointerException if the specified key is null
@@ -2058,15 +2114,15 @@
* @throws NullPointerException if the specified key is null
*/
public K lowerKey(K key) {
- Node<K,V> n = findNear(key, LT);
+ Node<K,V> n = findNear(key, LT, comparator);
return (n == null) ? null : n.key;
}
/**
* Returns a key-value mapping associated with the greatest key
- * less than or equal to the given key, or <tt>null</tt> if there
+ * less than or equal to the given key, or {@code null} if there
* is no such key. The returned entry does <em>not</em> support
- * the <tt>Entry.setValue</tt> method.
+ * the {@code Entry.setValue} method.
*
* @param key the key
* @throws ClassCastException {@inheritDoc}
@@ -2082,15 +2138,15 @@
* @throws NullPointerException if the specified key is null
*/
public K floorKey(K key) {
- Node<K,V> n = findNear(key, LT|EQ);
+ Node<K,V> n = findNear(key, LT|EQ, comparator);
return (n == null) ? null : n.key;
}
/**
* Returns a key-value mapping associated with the least key
- * greater than or equal to the given key, or <tt>null</tt> if
+ * greater than or equal to the given key, or {@code null} if
* there is no such entry. The returned entry does <em>not</em>
- * support the <tt>Entry.setValue</tt> method.
+ * support the {@code Entry.setValue} method.
*
* @throws ClassCastException {@inheritDoc}
* @throws NullPointerException if the specified key is null
@@ -2104,15 +2160,15 @@
* @throws NullPointerException if the specified key is null
*/
public K ceilingKey(K key) {
- Node<K,V> n = findNear(key, GT|EQ);
+ Node<K,V> n = findNear(key, GT|EQ, comparator);
return (n == null) ? null : n.key;
}
/**
* Returns a key-value mapping associated with the least key
- * strictly greater than the given key, or <tt>null</tt> if there
+ * strictly greater than the given key, or {@code null} if there
* is no such key. The returned entry does <em>not</em> support
- * the <tt>Entry.setValue</tt> method.
+ * the {@code Entry.setValue} method.
*
* @param key the key
* @throws ClassCastException {@inheritDoc}
@@ -2128,15 +2184,15 @@
* @throws NullPointerException if the specified key is null
*/
public K higherKey(K key) {
- Node<K,V> n = findNear(key, GT);
+ Node<K,V> n = findNear(key, GT, comparator);
return (n == null) ? null : n.key;
}
/**
* Returns a key-value mapping associated with the least
- * key in this map, or <tt>null</tt> if the map is empty.
+ * key in this map, or {@code null} if the map is empty.
* The returned entry does <em>not</em> support
- * the <tt>Entry.setValue</tt> method.
+ * the {@code Entry.setValue} method.
*/
public Map.Entry<K,V> firstEntry() {
for (;;) {
@@ -2151,9 +2207,9 @@
/**
* Returns a key-value mapping associated with the greatest
- * key in this map, or <tt>null</tt> if the map is empty.
+ * key in this map, or {@code null} if the map is empty.
* The returned entry does <em>not</em> support
- * the <tt>Entry.setValue</tt> method.
+ * the {@code Entry.setValue} method.
*/
public Map.Entry<K,V> lastEntry() {
for (;;) {
@@ -2168,9 +2224,9 @@
/**
* Removes and returns a key-value mapping associated with
- * the least key in this map, or <tt>null</tt> if the map is empty.
+ * the least key in this map, or {@code null} if the map is empty.
* The returned entry does <em>not</em> support
- * the <tt>Entry.setValue</tt> method.
+ * the {@code Entry.setValue} method.
*/
public Map.Entry<K,V> pollFirstEntry() {
return doRemoveFirstEntry();
@@ -2178,9 +2234,9 @@
/**
* Removes and returns a key-value mapping associated with
- * the greatest key in this map, or <tt>null</tt> if the map is empty.
+ * the greatest key in this map, or {@code null} if the map is empty.
* The returned entry does <em>not</em> support
- * the <tt>Entry.setValue</tt> method.
+ * the {@code Entry.setValue} method.
*/
public Map.Entry<K,V> pollLastEntry() {
return doRemoveLastEntry();
@@ -2202,13 +2258,11 @@
/** Initializes ascending iterator for entire range. */
Iter() {
- for (;;) {
- next = findFirst();
- if (next == null)
- break;
+ while ((next = findFirst()) != null) {
Object x = next.value;
if (x != null && x != next) {
- nextValue = (V) x;
+ @SuppressWarnings("unchecked") V vv = (V)x;
+ nextValue = vv;
break;
}
}
@@ -2223,13 +2277,11 @@
if (next == null)
throw new NoSuchElementException();
lastReturned = next;
- for (;;) {
- next = next.next;
- if (next == null)
- break;
+ while ((next = next.next) != null) {
Object x = next.value;
if (x != null && x != next) {
- nextValue = (V) x;
+ @SuppressWarnings("unchecked") V vv = (V)x;
+ nextValue = vv;
break;
}
}
@@ -2296,7 +2348,7 @@
static final <E> List<E> toList(Collection<E> c) {
// Using size() here would be a pessimization.
- List<E> list = new ArrayList<E>();
+ ArrayList<E> list = new ArrayList<E>();
for (E e : c)
list.add(e);
return list;
@@ -2304,7 +2356,7 @@
static final class KeySet<E>
extends AbstractSet<E> implements NavigableSet<E> {
- private final ConcurrentNavigableMap<E,?> m;
+ final ConcurrentNavigableMap<E,?> m;
KeySet(ConcurrentNavigableMap<E,?> map) { m = map; }
public int size() { return m.size(); }
public boolean isEmpty() { return m.isEmpty(); }
@@ -2326,6 +2378,7 @@
Map.Entry<E,?> e = m.pollLastEntry();
return (e == null) ? null : e.getKey();
}
+ @SuppressWarnings("unchecked")
public Iterator<E> iterator() {
if (m instanceof ConcurrentSkipListMap)
return ((ConcurrentSkipListMap<E,Object>)m).keyIterator();
@@ -2376,13 +2429,21 @@
public NavigableSet<E> descendingSet() {
return new KeySet<E>(m.descendingMap());
}
+ @SuppressWarnings("unchecked")
+ public Spliterator<E> spliterator() {
+ if (m instanceof ConcurrentSkipListMap)
+ return ((ConcurrentSkipListMap<E,?>)m).keySpliterator();
+ else
+ return (Spliterator<E>)((SubMap<E,?>)m).keyIterator();
+ }
}
static final class Values<E> extends AbstractCollection<E> {
- private final ConcurrentNavigableMap<?, E> m;
+ final ConcurrentNavigableMap<?, E> m;
Values(ConcurrentNavigableMap<?, E> map) {
m = map;
}
+ @SuppressWarnings("unchecked")
public Iterator<E> iterator() {
if (m instanceof ConcurrentSkipListMap)
return ((ConcurrentSkipListMap<?,E>)m).valueIterator();
@@ -2403,14 +2464,21 @@
}
public Object[] toArray() { return toList(this).toArray(); }
public <T> T[] toArray(T[] a) { return toList(this).toArray(a); }
+ @SuppressWarnings("unchecked")
+ public Spliterator<E> spliterator() {
+ if (m instanceof ConcurrentSkipListMap)
+ return ((ConcurrentSkipListMap<?,E>)m).valueSpliterator();
+ else
+ return (Spliterator<E>)((SubMap<?,E>)m).valueIterator();
+ }
}
static final class EntrySet<K1,V1> extends AbstractSet<Map.Entry<K1,V1>> {
- private final ConcurrentNavigableMap<K1, V1> m;
+ final ConcurrentNavigableMap<K1, V1> m;
EntrySet(ConcurrentNavigableMap<K1, V1> map) {
m = map;
}
-
+ @SuppressWarnings("unchecked")
public Iterator<Map.Entry<K1,V1>> iterator() {
if (m instanceof ConcurrentSkipListMap)
return ((ConcurrentSkipListMap<K1,V1>)m).entryIterator();
@@ -2457,6 +2525,14 @@
}
public Object[] toArray() { return toList(this).toArray(); }
public <T> T[] toArray(T[] a) { return toList(this).toArray(a); }
+ @SuppressWarnings("unchecked")
+ public Spliterator<Map.Entry<K1,V1>> spliterator() {
+ if (m instanceof ConcurrentSkipListMap)
+ return ((ConcurrentSkipListMap<K1,V1>)m).entrySpliterator();
+ else
+ return (Spliterator<Map.Entry<K1,V1>>)
+ ((SubMap<K1,V1>)m).entryIterator();
+ }
}
/**
@@ -2466,8 +2542,8 @@
* 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 <tt>subMap</tt>, <tt>headMap</tt>, and
- * <tt>tailMap</tt> methods of their underlying maps.
+ * constructed only using the {@code subMap}, {@code headMap}, and
+ * {@code tailMap} methods of their underlying maps.
*
* @serial include
*/
@@ -2495,14 +2571,15 @@
private transient Collection<V> valuesView;
/**
- * Creates a new submap, initializing all fields
+ * Creates a new submap, initializing all fields.
*/
SubMap(ConcurrentSkipListMap<K,V> map,
K fromKey, boolean fromInclusive,
K toKey, boolean toInclusive,
boolean isDescending) {
+ Comparator<? super K> cmp = map.comparator;
if (fromKey != null && toKey != null &&
- map.compare(fromKey, toKey) > 0)
+ cpr(cmp, fromKey, toKey) > 0)
throw new IllegalArgumentException("inconsistent range");
this.m = map;
this.lo = fromKey;
@@ -2514,39 +2591,34 @@
/* ---------------- Utilities -------------- */
- private boolean tooLow(K key) {
- if (lo != null) {
- int c = m.compare(key, lo);
- if (c < 0 || (c == 0 && !loInclusive))
- return true;
- }
- return false;
+ boolean tooLow(Object key, Comparator<? super K> cmp) {
+ int c;
+ return (lo != null && ((c = cpr(cmp, key, lo)) < 0 ||
+ (c == 0 && !loInclusive)));
}
- private boolean tooHigh(K key) {
- if (hi != null) {
- int c = m.compare(key, hi);
- if (c > 0 || (c == 0 && !hiInclusive))
- return true;
- }
- return false;
+ boolean tooHigh(Object key, Comparator<? super K> cmp) {
+ int c;
+ return (hi != null && ((c = cpr(cmp, key, hi)) > 0 ||
+ (c == 0 && !hiInclusive)));
}
- private boolean inBounds(K key) {
- return !tooLow(key) && !tooHigh(key);
+ boolean inBounds(Object key, Comparator<? super K> cmp) {
+ return !tooLow(key, cmp) && !tooHigh(key, cmp);
}
- private void checkKeyBounds(K key) throws IllegalArgumentException {
+ void checkKeyBounds(K key, Comparator<? super K> cmp) {
if (key == null)
throw new NullPointerException();
- if (!inBounds(key))
+ if (!inBounds(key, cmp))
throw new IllegalArgumentException("key out of range");
}
/**
- * Returns true if node key is less than upper bound of range
+ * Returns true if node key is less than upper bound of range.
*/
- private boolean isBeforeEnd(ConcurrentSkipListMap.Node<K,V> n) {
+ boolean isBeforeEnd(ConcurrentSkipListMap.Node<K,V> n,
+ Comparator<? super K> cmp) {
if (n == null)
return false;
if (hi == null)
@@ -2554,7 +2626,7 @@
K k = n.key;
if (k == null) // pass by markers and headers
return true;
- int c = m.compare(k, hi);
+ int c = cpr(cmp, k, hi);
if (c > 0 || (c == 0 && !hiInclusive))
return false;
return true;
@@ -2562,58 +2634,61 @@
/**
* Returns lowest node. This node might not be in range, so
- * most usages need to check bounds
+ * most usages need to check bounds.
*/
- private ConcurrentSkipListMap.Node<K,V> loNode() {
+ ConcurrentSkipListMap.Node<K,V> loNode(Comparator<? super K> cmp) {
if (lo == null)
return m.findFirst();
else if (loInclusive)
- return m.findNear(lo, GT|EQ);
+ return m.findNear(lo, GT|EQ, cmp);
else
- return m.findNear(lo, GT);
+ return m.findNear(lo, GT, cmp);
}
/**
* Returns highest node. This node might not be in range, so
- * most usages need to check bounds
+ * most usages need to check bounds.
*/
- private ConcurrentSkipListMap.Node<K,V> hiNode() {
+ ConcurrentSkipListMap.Node<K,V> hiNode(Comparator<? super K> cmp) {
if (hi == null)
return m.findLast();
else if (hiInclusive)
- return m.findNear(hi, LT|EQ);
+ return m.findNear(hi, LT|EQ, cmp);
else
- return m.findNear(hi, LT);
+ return m.findNear(hi, LT, cmp);
}
/**
- * Returns lowest absolute key (ignoring directonality)
+ * Returns lowest absolute key (ignoring directonality).
*/
- private K lowestKey() {
- ConcurrentSkipListMap.Node<K,V> n = loNode();
- if (isBeforeEnd(n))
+ K lowestKey() {
+ Comparator<? super K> cmp = m.comparator;
+ ConcurrentSkipListMap.Node<K,V> n = loNode(cmp);
+ if (isBeforeEnd(n, cmp))
return n.key;
else
throw new NoSuchElementException();
}
/**
- * Returns highest absolute key (ignoring directonality)
+ * Returns highest absolute key (ignoring directonality).
*/
- private K highestKey() {
- ConcurrentSkipListMap.Node<K,V> n = hiNode();
+ K highestKey() {
+ Comparator<? super K> cmp = m.comparator;
+ ConcurrentSkipListMap.Node<K,V> n = hiNode(cmp);
if (n != null) {
K last = n.key;
- if (inBounds(last))
+ if (inBounds(last, cmp))
return last;
}
throw new NoSuchElementException();
}
- private Map.Entry<K,V> lowestEntry() {
+ Map.Entry<K,V> lowestEntry() {
+ Comparator<? super K> cmp = m.comparator;
for (;;) {
- ConcurrentSkipListMap.Node<K,V> n = loNode();
- if (!isBeforeEnd(n))
+ ConcurrentSkipListMap.Node<K,V> n = loNode(cmp);
+ if (!isBeforeEnd(n, cmp))
return null;
Map.Entry<K,V> e = n.createSnapshot();
if (e != null)
@@ -2621,10 +2696,11 @@
}
}
- private Map.Entry<K,V> highestEntry() {
+ Map.Entry<K,V> highestEntry() {
+ Comparator<? super K> cmp = m.comparator;
for (;;) {
- ConcurrentSkipListMap.Node<K,V> n = hiNode();
- if (n == null || !inBounds(n.key))
+ ConcurrentSkipListMap.Node<K,V> n = hiNode(cmp);
+ if (n == null || !inBounds(n.key, cmp))
return null;
Map.Entry<K,V> e = n.createSnapshot();
if (e != null)
@@ -2632,13 +2708,14 @@
}
}
- private Map.Entry<K,V> removeLowest() {
+ Map.Entry<K,V> removeLowest() {
+ Comparator<? super K> cmp = m.comparator;
for (;;) {
- Node<K,V> n = loNode();
+ Node<K,V> n = loNode(cmp);
if (n == null)
return null;
K k = n.key;
- if (!inBounds(k))
+ if (!inBounds(k, cmp))
return null;
V v = m.doRemove(k, null);
if (v != null)
@@ -2646,13 +2723,14 @@
}
}
- private Map.Entry<K,V> removeHighest() {
+ Map.Entry<K,V> removeHighest() {
+ Comparator<? super K> cmp = m.comparator;
for (;;) {
- Node<K,V> n = hiNode();
+ Node<K,V> n = hiNode(cmp);
if (n == null)
return null;
K k = n.key;
- if (!inBounds(k))
+ if (!inBounds(k, cmp))
return null;
V v = m.doRemove(k, null);
if (v != null)
@@ -2663,20 +2741,21 @@
/**
* Submap version of ConcurrentSkipListMap.getNearEntry
*/
- private Map.Entry<K,V> getNearEntry(K key, int rel) {
+ Map.Entry<K,V> getNearEntry(K key, int rel) {
+ Comparator<? super K> cmp = m.comparator;
if (isDescending) { // adjust relation for direction
if ((rel & LT) == 0)
rel |= LT;
else
rel &= ~LT;
}
- if (tooLow(key))
+ if (tooLow(key, cmp))
return ((rel & LT) != 0) ? null : lowestEntry();
- if (tooHigh(key))
+ if (tooHigh(key, cmp))
return ((rel & LT) != 0) ? highestEntry() : null;
for (;;) {
- Node<K,V> n = m.findNear(key, rel);
- if (n == null || !inBounds(n.key))
+ Node<K,V> n = m.findNear(key, rel, cmp);
+ if (n == null || !inBounds(n.key, cmp))
return null;
K k = n.key;
V v = n.getValidValue();
@@ -2686,35 +2765,36 @@
}
// Almost the same as getNearEntry, except for keys
- private K getNearKey(K key, int rel) {
+ K getNearKey(K key, int rel) {
+ Comparator<? super K> cmp = m.comparator;
if (isDescending) { // adjust relation for direction
if ((rel & LT) == 0)
rel |= LT;
else
rel &= ~LT;
}
- if (tooLow(key)) {
+ if (tooLow(key, cmp)) {
if ((rel & LT) == 0) {
- ConcurrentSkipListMap.Node<K,V> n = loNode();
- if (isBeforeEnd(n))
+ ConcurrentSkipListMap.Node<K,V> n = loNode(cmp);
+ if (isBeforeEnd(n, cmp))
return n.key;
}
return null;
}
- if (tooHigh(key)) {
+ if (tooHigh(key, cmp)) {
if ((rel & LT) != 0) {
- ConcurrentSkipListMap.Node<K,V> n = hiNode();
+ ConcurrentSkipListMap.Node<K,V> n = hiNode(cmp);
if (n != null) {
K last = n.key;
- if (inBounds(last))
+ if (inBounds(last, cmp))
return last;
}
}
return null;
}
for (;;) {
- Node<K,V> n = m.findNear(key, rel);
- if (n == null || !inBounds(n.key))
+ Node<K,V> n = m.findNear(key, rel, cmp);
+ if (n == null || !inBounds(n.key, cmp))
return null;
K k = n.key;
V v = n.getValidValue();
@@ -2727,30 +2807,28 @@
public boolean containsKey(Object key) {
if (key == null) throw new NullPointerException();
- K k = (K)key;
- return inBounds(k) && m.containsKey(k);
+ return inBounds(key, m.comparator) && m.containsKey(key);
}
public V get(Object key) {
if (key == null) throw new NullPointerException();
- K k = (K)key;
- return (!inBounds(k)) ? null : m.get(k);
+ return (!inBounds(key, m.comparator)) ? null : m.get(key);
}
public V put(K key, V value) {
- checkKeyBounds(key);
+ checkKeyBounds(key, m.comparator);
return m.put(key, value);
}
public V remove(Object key) {
- K k = (K)key;
- return (!inBounds(k)) ? null : m.remove(k);
+ return (!inBounds(key, m.comparator)) ? null : m.remove(key);
}
public int size() {
+ Comparator<? super K> cmp = m.comparator;
long count = 0;
- for (ConcurrentSkipListMap.Node<K,V> n = loNode();
- isBeforeEnd(n);
+ for (ConcurrentSkipListMap.Node<K,V> n = loNode(cmp);
+ isBeforeEnd(n, cmp);
n = n.next) {
if (n.getValidValue() != null)
++count;
@@ -2759,14 +2837,16 @@
}
public boolean isEmpty() {
- return !isBeforeEnd(loNode());
+ Comparator<? super K> cmp = m.comparator;
+ return !isBeforeEnd(loNode(cmp), cmp);
}
public boolean containsValue(Object value) {
if (value == null)
throw new NullPointerException();
- for (ConcurrentSkipListMap.Node<K,V> n = loNode();
- isBeforeEnd(n);
+ Comparator<? super K> cmp = m.comparator;
+ for (ConcurrentSkipListMap.Node<K,V> n = loNode(cmp);
+ isBeforeEnd(n, cmp);
n = n.next) {
V v = n.getValidValue();
if (v != null && value.equals(v))
@@ -2776,8 +2856,9 @@
}
public void clear() {
- for (ConcurrentSkipListMap.Node<K,V> n = loNode();
- isBeforeEnd(n);
+ Comparator<? super K> cmp = m.comparator;
+ for (ConcurrentSkipListMap.Node<K,V> n = loNode(cmp);
+ isBeforeEnd(n, cmp);
n = n.next) {
if (n.getValidValue() != null)
m.remove(n.key);
@@ -2787,22 +2868,21 @@
/* ---------------- ConcurrentMap API methods -------------- */
public V putIfAbsent(K key, V value) {
- checkKeyBounds(key);
+ checkKeyBounds(key, m.comparator);
return m.putIfAbsent(key, value);
}
public boolean remove(Object key, Object value) {
- K k = (K)key;
- return inBounds(k) && m.remove(k, value);
+ return inBounds(key, m.comparator) && m.remove(key, value);
}
public boolean replace(K key, V oldValue, V newValue) {
- checkKeyBounds(key);
+ checkKeyBounds(key, m.comparator);
return m.replace(key, oldValue, newValue);
}
public V replace(K key, V value) {
- checkKeyBounds(key);
+ checkKeyBounds(key, m.comparator);
return m.replace(key, value);
}
@@ -2820,10 +2900,9 @@
* Utility to create submaps, where given bounds override
* unbounded(null) ones and/or are checked against bounded ones.
*/
- private SubMap<K,V> newSubMap(K fromKey,
- boolean fromInclusive,
- K toKey,
- boolean toInclusive) {
+ SubMap<K,V> newSubMap(K fromKey, boolean fromInclusive,
+ K toKey, boolean toInclusive) {
+ Comparator<? super K> cmp = m.comparator;
if (isDescending) { // flip senses
K tk = fromKey;
fromKey = toKey;
@@ -2838,7 +2917,7 @@
fromInclusive = loInclusive;
}
else {
- int c = m.compare(fromKey, lo);
+ int c = cpr(cmp, fromKey, lo);
if (c < 0 || (c == 0 && !loInclusive && fromInclusive))
throw new IllegalArgumentException("key out of range");
}
@@ -2849,7 +2928,7 @@
toInclusive = hiInclusive;
}
else {
- int c = m.compare(toKey, hi);
+ int c = cpr(cmp, toKey, hi);
if (c > 0 || (c == 0 && !hiInclusive && toInclusive))
throw new IllegalArgumentException("key out of range");
}
@@ -2858,24 +2937,20 @@
toKey, toInclusive, isDescending);
}
- public SubMap<K,V> subMap(K fromKey,
- boolean fromInclusive,
- K toKey,
- boolean toInclusive) {
+ public SubMap<K,V> subMap(K fromKey, boolean fromInclusive,
+ K toKey, boolean toInclusive) {
if (fromKey == null || toKey == null)
throw new NullPointerException();
return newSubMap(fromKey, fromInclusive, toKey, toInclusive);
}
- public SubMap<K,V> headMap(K toKey,
- boolean inclusive) {
+ public SubMap<K,V> headMap(K toKey, boolean inclusive) {
if (toKey == null)
throw new NullPointerException();
return newSubMap(null, false, toKey, inclusive);
}
- public SubMap<K,V> tailMap(K fromKey,
- boolean inclusive) {
+ public SubMap<K,V> tailMap(K fromKey, boolean inclusive) {
if (fromKey == null)
throw new NullPointerException();
return newSubMap(fromKey, inclusive, null, false);
@@ -2996,8 +3071,9 @@
/**
* Variant of main Iter class to traverse through submaps.
+ * Also serves as back-up Spliterator for views
*/
- abstract class SubMapIter<T> implements Iterator<T> {
+ abstract class SubMapIter<T> implements Iterator<T>, Spliterator<T> {
/** the last node returned by next() */
Node<K,V> lastReturned;
/** the next node to return from next(); */
@@ -3006,16 +3082,19 @@
V nextValue;
SubMapIter() {
+ Comparator<? super K> cmp = m.comparator;
for (;;) {
- next = isDescending ? hiNode() : loNode();
+ next = isDescending ? hiNode(cmp) : loNode(cmp);
if (next == null)
break;
Object x = next.value;
if (x != null && x != next) {
- if (! inBounds(next.key))
+ if (! inBounds(next.key, cmp))
next = null;
- else
- nextValue = (V) x;
+ else {
+ @SuppressWarnings("unchecked") V vv = (V)x;
+ nextValue = vv;
+ }
break;
}
}
@@ -3036,32 +3115,38 @@
}
private void ascend() {
+ Comparator<? super K> cmp = m.comparator;
for (;;) {
next = next.next;
if (next == null)
break;
Object x = next.value;
if (x != null && x != next) {
- if (tooHigh(next.key))
+ if (tooHigh(next.key, cmp))
next = null;
- else
- nextValue = (V) x;
+ else {
+ @SuppressWarnings("unchecked") V vv = (V)x;
+ nextValue = vv;
+ }
break;
}
}
}
private void descend() {
+ Comparator<? super K> cmp = m.comparator;
for (;;) {
- next = m.findNear(lastReturned.key, LT);
+ next = m.findNear(lastReturned.key, LT, cmp);
if (next == null)
break;
Object x = next.value;
if (x != null && x != next) {
- if (tooLow(next.key))
+ if (tooLow(next.key, cmp))
next = null;
- else
- nextValue = (V) x;
+ else {
+ @SuppressWarnings("unchecked") V vv = (V)x;
+ nextValue = vv;
+ }
break;
}
}
@@ -3075,6 +3160,26 @@
lastReturned = null;
}
+ public Spliterator<T> trySplit() {
+ return null;
+ }
+
+ public boolean tryAdvance(Consumer<? super T> action) {
+ if (hasNext()) {
+ action.accept(next());
+ return true;
+ }
+ return false;
+ }
+
+ public void forEachRemaining(Consumer<? super T> action) {
+ while (hasNext())
+ action.accept(next());
+ }
+
+ public long estimateSize() {
+ return Long.MAX_VALUE;
+ }
}
final class SubMapValueIterator extends SubMapIter<V> {
@@ -3083,6 +3188,9 @@
advance();
return v;
}
+ public int characteristics() {
+ return 0;
+ }
}
final class SubMapKeyIterator extends SubMapIter<K> {
@@ -3091,6 +3199,13 @@
advance();
return n.key;
}
+ public int characteristics() {
+ return Spliterator.DISTINCT | Spliterator.ORDERED |
+ Spliterator.SORTED;
+ }
+ public final Comparator<? super K> getComparator() {
+ return SubMap.this.comparator();
+ }
}
final class SubMapEntryIterator extends SubMapIter<Map.Entry<K,V>> {
@@ -3100,18 +3215,351 @@
advance();
return new AbstractMap.SimpleImmutableEntry<K,V>(n.key, v);
}
+ public int characteristics() {
+ return Spliterator.DISTINCT;
+ }
+ }
+ }
+
+ // default Map method overrides
+
+ public void forEach(BiConsumer<? super K, ? super V> action) {
+ if (action == null) throw new NullPointerException();
+ V v;
+ for (Node<K,V> n = findFirst(); n != null; n = n.next) {
+ if ((v = n.getValidValue()) != null)
+ action.accept(n.key, v);
+ }
+ }
+
+ public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
+ if (function == null) throw new NullPointerException();
+ V v;
+ for (Node<K,V> n = findFirst(); n != null; n = n.next) {
+ while ((v = n.getValidValue()) != null) {
+ V r = function.apply(n.key, v);
+ if (r == null) throw new NullPointerException();
+ if (n.casValue(v, r))
+ break;
+ }
+ }
+ }
+
+ /**
+ * Base class providing common structure for Spliterators.
+ * (Although not all that much common functionality; as usual for
+ * view classes, details annoyingly vary in key, value, and entry
+ * subclasses in ways that are not worth abstracting out for
+ * internal classes.)
+ *
+ * The basic split strategy is to recursively descend from top
+ * level, row by row, descending to next row when either split
+ * off, or the end of row is encountered. Control of the number of
+ * splits relies on some statistical estimation: The expected
+ * remaining number of elements of a skip list when advancing
+ * either across or down decreases by about 25%. To make this
+ * observation useful, we need to know initial size, which we
+ * don't. But we can just use Integer.MAX_VALUE so that we
+ * don't prematurely zero out while splitting.
+ */
+ abstract static class CSLMSpliterator<K,V> {
+ final Comparator<? super K> comparator;
+ final K fence; // exclusive upper bound for keys, or null if to end
+ Index<K,V> row; // the level to split out
+ Node<K,V> current; // current traversal node; initialize at origin
+ int est; // pseudo-size estimate
+ CSLMSpliterator(Comparator<? super K> comparator, Index<K,V> row,
+ Node<K,V> origin, K fence, int est) {
+ this.comparator = comparator; this.row = row;
+ this.current = origin; this.fence = fence; this.est = est;
+ }
+
+ public final long estimateSize() { return (long)est; }
+ }
+
+ static final class KeySpliterator<K,V> extends CSLMSpliterator<K,V>
+ implements Spliterator<K> {
+ KeySpliterator(Comparator<? super K> comparator, Index<K,V> row,
+ Node<K,V> origin, K fence, int est) {
+ super(comparator, row, origin, fence, est);
+ }
+
+ public Spliterator<K> trySplit() {
+ Node<K,V> e; K ek;
+ Comparator<? super K> cmp = comparator;
+ K f = fence;
+ if ((e = current) != null && (ek = e.key) != null) {
+ for (Index<K,V> q = row; q != null; q = row = q.down) {
+ Index<K,V> s; Node<K,V> b, n; K sk;
+ if ((s = q.right) != null && (b = s.node) != null &&
+ (n = b.next) != null && n.value != null &&
+ (sk = n.key) != null && cpr(cmp, sk, ek) > 0 &&
+ (f == null || cpr(cmp, sk, f) < 0)) {
+ current = n;
+ Index<K,V> r = q.down;
+ row = (s.right != null) ? s : s.down;
+ est -= est >>> 2;
+ return new KeySpliterator<K,V>(cmp, r, e, sk, est);
+ }
+ }
+ }
+ return null;
+ }
+
+ public void forEachRemaining(Consumer<? super K> action) {
+ if (action == null) throw new NullPointerException();
+ Comparator<? super K> cmp = comparator;
+ K f = fence;
+ Node<K,V> e = current;
+ current = null;
+ for (; e != null; e = e.next) {
+ K k; Object v;
+ if ((k = e.key) != null && f != null && cpr(cmp, f, k) <= 0)
+ break;
+ if ((v = e.value) != null && v != e)
+ action.accept(k);
+ }
+ }
+
+ public boolean tryAdvance(Consumer<? super K> action) {
+ if (action == null) throw new NullPointerException();
+ Comparator<? super K> cmp = comparator;
+ K f = fence;
+ Node<K,V> e = current;
+ for (; e != null; e = e.next) {
+ K k; Object v;
+ if ((k = e.key) != null && f != null && cpr(cmp, f, k) <= 0) {
+ e = null;
+ break;
+ }
+ if ((v = e.value) != null && v != e) {
+ current = e.next;
+ action.accept(k);
+ return true;
+ }
+ }
+ current = e;
+ return false;
+ }
+
+ public int characteristics() {
+ return Spliterator.DISTINCT | Spliterator.SORTED |
+ Spliterator.ORDERED | Spliterator.CONCURRENT |
+ Spliterator.NONNULL;
+ }
+
+ public final Comparator<? super K> getComparator() {
+ return comparator;
+ }
+ }
+ // factory method for KeySpliterator
+ final KeySpliterator<K,V> keySpliterator() {
+ Comparator<? super K> cmp = comparator;
+ for (;;) { // ensure h corresponds to origin p
+ HeadIndex<K,V> h; Node<K,V> p;
+ Node<K,V> b = (h = head).node;
+ if ((p = b.next) == null || p.value != null)
+ return new KeySpliterator<K,V>(cmp, h, p, null, (p == null) ?
+ 0 : Integer.MAX_VALUE);
+ p.helpDelete(b, p.next);
+ }
+ }
+
+ static final class ValueSpliterator<K,V> extends CSLMSpliterator<K,V>
+ implements Spliterator<V> {
+ ValueSpliterator(Comparator<? super K> comparator, Index<K,V> row,
+ Node<K,V> origin, K fence, int est) {
+ super(comparator, row, origin, fence, est);
+ }
+
+ public Spliterator<V> trySplit() {
+ Node<K,V> e; K ek;
+ Comparator<? super K> cmp = comparator;
+ K f = fence;
+ if ((e = current) != null && (ek = e.key) != null) {
+ for (Index<K,V> q = row; q != null; q = row = q.down) {
+ Index<K,V> s; Node<K,V> b, n; K sk;
+ if ((s = q.right) != null && (b = s.node) != null &&
+ (n = b.next) != null && n.value != null &&
+ (sk = n.key) != null && cpr(cmp, sk, ek) > 0 &&
+ (f == null || cpr(cmp, sk, f) < 0)) {
+ current = n;
+ Index<K,V> r = q.down;
+ row = (s.right != null) ? s : s.down;
+ est -= est >>> 2;
+ return new ValueSpliterator<K,V>(cmp, r, e, sk, est);
+ }
+ }
+ }
+ return null;
+ }
+
+ public void forEachRemaining(Consumer<? super V> action) {
+ if (action == null) throw new NullPointerException();
+ Comparator<? super K> cmp = comparator;
+ K f = fence;
+ Node<K,V> e = current;
+ current = null;
+ for (; e != null; e = e.next) {
+ K k; Object v;
+ if ((k = e.key) != null && f != null && cpr(cmp, f, k) <= 0)
+ break;
+ if ((v = e.value) != null && v != e) {
+ @SuppressWarnings("unchecked") V vv = (V)v;
+ action.accept(vv);
+ }
+ }
+ }
+
+ public boolean tryAdvance(Consumer<? super V> action) {
+ if (action == null) throw new NullPointerException();
+ Comparator<? super K> cmp = comparator;
+ K f = fence;
+ Node<K,V> e = current;
+ for (; e != null; e = e.next) {
+ K k; Object v;
+ if ((k = e.key) != null && f != null && cpr(cmp, f, k) <= 0) {
+ e = null;
+ break;
+ }
+ if ((v = e.value) != null && v != e) {
+ current = e.next;
+ @SuppressWarnings("unchecked") V vv = (V)v;
+ action.accept(vv);
+ return true;
+ }
+ }
+ current = e;
+ return false;
+ }
+
+ public int characteristics() {
+ return Spliterator.CONCURRENT | Spliterator.NONNULL;
+ }
+ }
+
+ // Almost the same as keySpliterator()
+ final ValueSpliterator<K,V> valueSpliterator() {
+ Comparator<? super K> cmp = comparator;
+ for (;;) {
+ HeadIndex<K,V> h; Node<K,V> p;
+ Node<K,V> b = (h = head).node;
+ if ((p = b.next) == null || p.value != null)
+ return new ValueSpliterator<K,V>(cmp, h, p, null, (p == null) ?
+ 0 : Integer.MAX_VALUE);
+ p.helpDelete(b, p.next);
+ }
+ }
+
+ static final class EntrySpliterator<K,V> extends CSLMSpliterator<K,V>
+ implements Spliterator<Map.Entry<K,V>> {
+ EntrySpliterator(Comparator<? super K> comparator, Index<K,V> row,
+ Node<K,V> origin, K fence, int est) {
+ super(comparator, row, origin, fence, est);
+ }
+
+ public Spliterator<Map.Entry<K,V>> trySplit() {
+ Node<K,V> e; K ek;
+ Comparator<? super K> cmp = comparator;
+ K f = fence;
+ if ((e = current) != null && (ek = e.key) != null) {
+ for (Index<K,V> q = row; q != null; q = row = q.down) {
+ Index<K,V> s; Node<K,V> b, n; K sk;
+ if ((s = q.right) != null && (b = s.node) != null &&
+ (n = b.next) != null && n.value != null &&
+ (sk = n.key) != null && cpr(cmp, sk, ek) > 0 &&
+ (f == null || cpr(cmp, sk, f) < 0)) {
+ current = n;
+ Index<K,V> r = q.down;
+ row = (s.right != null) ? s : s.down;
+ est -= est >>> 2;
+ return new EntrySpliterator<K,V>(cmp, r, e, sk, est);
+ }
+ }
+ }
+ return null;
+ }
+
+ public void forEachRemaining(Consumer<? super Map.Entry<K,V>> action) {
+ if (action == null) throw new NullPointerException();
+ Comparator<? super K> cmp = comparator;
+ K f = fence;
+ Node<K,V> e = current;
+ current = null;
+ for (; e != null; e = e.next) {
+ K k; Object v;
+ if ((k = e.key) != null && f != null && cpr(cmp, f, k) <= 0)
+ break;
+ if ((v = e.value) != null && v != e) {
+ @SuppressWarnings("unchecked") V vv = (V)v;
+ action.accept
+ (new AbstractMap.SimpleImmutableEntry<K,V>(k, vv));
+ }
+ }
+ }
+
+ public boolean tryAdvance(Consumer<? super Map.Entry<K,V>> action) {
+ if (action == null) throw new NullPointerException();
+ Comparator<? super K> cmp = comparator;
+ K f = fence;
+ Node<K,V> e = current;
+ for (; e != null; e = e.next) {
+ K k; Object v;
+ if ((k = e.key) != null && f != null && cpr(cmp, f, k) <= 0) {
+ e = null;
+ break;
+ }
+ if ((v = e.value) != null && v != e) {
+ current = e.next;
+ @SuppressWarnings("unchecked") V vv = (V)v;
+ action.accept
+ (new AbstractMap.SimpleImmutableEntry<K,V>(k, vv));
+ return true;
+ }
+ }
+ current = e;
+ return false;
+ }
+
+ public int characteristics() {
+ return Spliterator.DISTINCT | Spliterator.SORTED |
+ Spliterator.ORDERED | Spliterator.CONCURRENT |
+ Spliterator.NONNULL;
+ }
+
+ public final Comparator<Map.Entry<K,V>> getComparator() {
+ return comparator == null ? null :
+ Map.Entry.comparingByKey(comparator);
+ }
+ }
+
+ // Almost the same as keySpliterator()
+ final EntrySpliterator<K,V> entrySpliterator() {
+ Comparator<? super K> cmp = comparator;
+ for (;;) { // almost same as key version
+ HeadIndex<K,V> h; Node<K,V> p;
+ Node<K,V> b = (h = head).node;
+ if ((p = b.next) == null || p.value != null)
+ return new EntrySpliterator<K,V>(cmp, h, p, null, (p == null) ?
+ 0 : Integer.MAX_VALUE);
+ p.helpDelete(b, p.next);
}
}
// Unsafe mechanics
private static final sun.misc.Unsafe UNSAFE;
private static final long headOffset;
+ private static final long SECONDARY;
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) {
throw new Error(e);
}
--- a/jdk/src/share/classes/java/util/concurrent/ConcurrentSkipListSet.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/java/util/concurrent/ConcurrentSkipListSet.java Wed Jul 03 14:35:10 2013 -0700
@@ -34,7 +34,17 @@
*/
package java.util.concurrent;
-import java.util.*;
+import java.util.AbstractSet;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NavigableMap;
+import java.util.NavigableSet;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.Spliterator;
/**
* A scalable concurrent {@link NavigableSet} implementation based on
@@ -44,33 +54,33 @@
* on which constructor is used.
*
* <p>This implementation provides expected average <i>log(n)</i> time
- * cost for the <tt>contains</tt>, <tt>add</tt>, and <tt>remove</tt>
+ * cost for the {@code contains}, {@code add}, and {@code remove}
* operations and their variants. Insertion, removal, and access
* operations safely execute concurrently by multiple threads.
* Iterators are <i>weakly consistent</i>, returning elements
* reflecting the state of the set at some point at or since the
* creation of the iterator. They do <em>not</em> throw {@link
- * ConcurrentModificationException}, and may proceed concurrently with
- * other operations. Ascending ordered views and their iterators are
- * faster than descending ones.
+ * java.util.ConcurrentModificationException}, and may proceed
+ * concurrently with other operations. Ascending ordered views and
+ * their iterators are faster than descending ones.
*
- * <p>Beware that, unlike in most collections, the <tt>size</tt>
+ * <p>Beware that, unlike in most collections, the {@code size}
* method is <em>not</em> a constant-time operation. Because of the
* asynchronous nature of these sets, determining the current number
* of elements requires a traversal of the elements, and so may report
* inaccurate results if this collection is modified during traversal.
- * Additionally, the bulk operations <tt>addAll</tt>,
- * <tt>removeAll</tt>, <tt>retainAll</tt>, <tt>containsAll</tt>,
- * <tt>equals</tt>, and <tt>toArray</tt> are <em>not</em> guaranteed
+ * Additionally, the bulk operations {@code addAll},
+ * {@code removeAll}, {@code retainAll}, {@code containsAll},
+ * {@code equals}, and {@code toArray} are <em>not</em> guaranteed
* to be performed atomically. For example, an iterator operating
- * concurrently with an <tt>addAll</tt> operation might view only some
+ * concurrently with an {@code addAll} operation might view only some
* of the added elements.
*
* <p>This class and its iterators implement all of the
* <em>optional</em> methods of the {@link Set} and {@link Iterator}
* interfaces. Like most other concurrent collection implementations,
- * this class does not permit the use of <tt>null</tt> elements,
- * because <tt>null</tt> arguments and return values cannot be reliably
+ * this class does not permit the use of {@code null} elements,
+ * because {@code null} arguments and return values cannot be reliably
* distinguished from the absence of elements.
*
* <p>This class is a member of the
@@ -90,7 +100,7 @@
/**
* The underlying map. Uses Boolean.TRUE as value for each
* element. This field is declared final for the sake of thread
- * safety, which entails some ugliness in clone()
+ * safety, which entails some ugliness in clone().
*/
private final ConcurrentNavigableMap<E,Object> m;
@@ -107,7 +117,7 @@
* the specified comparator.
*
* @param comparator the comparator that will be used to order this set.
- * If <tt>null</tt>, the {@linkplain Comparable natural
+ * If {@code null}, the {@linkplain Comparable natural
* ordering} of the elements will be used.
*/
public ConcurrentSkipListSet(Comparator<? super E> comparator) {
@@ -120,7 +130,7 @@
* {@linkplain Comparable natural ordering}.
*
* @param c The elements that will comprise the new set
- * @throws ClassCastException if the elements in <tt>c</tt> are
+ * @throws ClassCastException if the elements in {@code c} are
* not {@link Comparable}, or are not mutually comparable
* @throws NullPointerException if the specified collection or any
* of its elements are null
@@ -151,7 +161,7 @@
}
/**
- * Returns a shallow copy of this <tt>ConcurrentSkipListSet</tt>
+ * Returns a shallow copy of this {@code ConcurrentSkipListSet}
* instance. (The elements themselves are not cloned.)
*
* @return a shallow copy of this set
@@ -172,8 +182,8 @@
/**
* Returns the number of elements in this set. If this set
- * contains more than <tt>Integer.MAX_VALUE</tt> elements, it
- * returns <tt>Integer.MAX_VALUE</tt>.
+ * contains more than {@code Integer.MAX_VALUE} elements, it
+ * returns {@code Integer.MAX_VALUE}.
*
* <p>Beware that, unlike in most collections, this method is
* <em>NOT</em> a constant-time operation. Because of the
@@ -191,20 +201,20 @@
}
/**
- * Returns <tt>true</tt> if this set contains no elements.
- * @return <tt>true</tt> if this set contains no elements
+ * Returns {@code true} if this set contains no elements.
+ * @return {@code true} if this set contains no elements
*/
public boolean isEmpty() {
return m.isEmpty();
}
/**
- * Returns <tt>true</tt> if this set contains the specified element.
- * More formally, returns <tt>true</tt> if and only if this set
- * contains an element <tt>e</tt> such that <tt>o.equals(e)</tt>.
+ * 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 {@code o.equals(e)}.
*
* @param o object to be checked for containment in this set
- * @return <tt>true</tt> if this set contains the specified element
+ * @return {@code true} if this set contains the specified element
* @throws ClassCastException if the specified element cannot be
* compared with the elements currently in this set
* @throws NullPointerException if the specified element is null
@@ -215,15 +225,15 @@
/**
* Adds the specified element to this set if it is not already present.
- * More formally, adds the specified element <tt>e</tt> to this set if
- * the set contains no element <tt>e2</tt> such that <tt>e.equals(e2)</tt>.
+ * More formally, adds the specified element {@code e} to this set if
+ * the set contains no element {@code e2} such that {@code e.equals(e2)}.
* If this set already contains the element, the call leaves the set
- * unchanged and returns <tt>false</tt>.
+ * unchanged and returns {@code false}.
*
* @param e element to be added to this set
- * @return <tt>true</tt> if this set did not already contain the
+ * @return {@code true} if this set did not already contain the
* specified element
- * @throws ClassCastException if <tt>e</tt> cannot be compared
+ * @throws ClassCastException if {@code e} cannot be compared
* with the elements currently in this set
* @throws NullPointerException if the specified element is null
*/
@@ -233,15 +243,15 @@
/**
* Removes the specified element from this set if it is present.
- * More formally, removes an element <tt>e</tt> such that
- * <tt>o.equals(e)</tt>, if this set contains such an element.
- * Returns <tt>true</tt> if this set contained the element (or
+ * More formally, removes an element {@code e} such that
+ * {@code 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.)
*
* @param o object to be removed from this set, if present
- * @return <tt>true</tt> if this set contained the specified element
- * @throws ClassCastException if <tt>o</tt> cannot be compared
+ * @return {@code true} if this set contained the specified element
+ * @throws ClassCastException if {@code o} cannot be compared
* with the elements currently in this set
* @throws NullPointerException if the specified element is null
*/
@@ -279,7 +289,7 @@
/**
* Compares the specified object with this set for equality. Returns
- * <tt>true</tt> if the specified object is also a set, the two sets
+ * {@code true} if the specified object is also a set, the two sets
* have the same size, and every member of the specified set is
* contained in this set (or equivalently, every member of this set is
* contained in the specified set). This definition ensures that the
@@ -287,7 +297,7 @@
* set interface.
*
* @param o the object to be compared for equality with this set
- * @return <tt>true</tt> if the specified object is equal to this set
+ * @return {@code true} if the specified object is equal to this set
*/
public boolean equals(Object o) {
// Override AbstractSet version to avoid calling size()
@@ -312,7 +322,7 @@
* value is the <i>asymmetric set difference</i> of the two sets.
*
* @param c collection containing elements to be removed from this set
- * @return <tt>true</tt> if this set changed as a result of the call
+ * @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 NullPointerException if the specified collection or any
@@ -380,14 +390,14 @@
}
/**
- * @throws NoSuchElementException {@inheritDoc}
+ * @throws java.util.NoSuchElementException {@inheritDoc}
*/
public E first() {
return m.firstKey();
}
/**
- * @throws NoSuchElementException {@inheritDoc}
+ * @throws java.util.NoSuchElementException {@inheritDoc}
*/
public E last() {
return m.lastKey();
@@ -460,7 +470,7 @@
* reflected in the descending set, and vice-versa.
*
* <p>The returned set has an ordering equivalent to
- * <tt>{@link Collections#reverseOrder(Comparator) Collections.reverseOrder}(comparator())</tt>.
+ * {@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}.
*
@@ -470,6 +480,14 @@
return new ConcurrentSkipListSet<E>(m.descendingMap());
}
+ @SuppressWarnings("unchecked")
+ public Spliterator<E> spliterator() {
+ if (m instanceof ConcurrentSkipListMap)
+ return ((ConcurrentSkipListMap<E,?>)m).keySpliterator();
+ else
+ return (Spliterator<E>)((ConcurrentSkipListMap.SubMap<E,?>)m).keyIterator();
+ }
+
// Support for resetting map in clone
private void setMap(ConcurrentNavigableMap<E,Object> map) {
UNSAFE.putObjectVolatile(this, mapOffset, map);
--- a/jdk/src/share/classes/java/util/concurrent/CopyOnWriteArrayList.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/java/util/concurrent/CopyOnWriteArrayList.java Wed Jul 03 14:35:10 2013 -0700
@@ -1,5 +1,4 @@
/*
- * Copyright (c) 2003, 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
@@ -34,7 +33,19 @@
*/
package java.util.concurrent;
-import java.util.*;
+import java.util.AbstractList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.ConcurrentModificationException;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+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;
@@ -42,10 +53,10 @@
/**
* A thread-safe variant of {@link java.util.ArrayList} in which all mutative
- * operations (<tt>add</tt>, <tt>set</tt>, and so on) are implemented by
+ * operations ({@code add}, {@code set}, and so on) are implemented by
* making a fresh copy of the underlying array.
*
- * <p> This is ordinarily too costly, but may be <em>more</em> efficient
+ * <p>This is ordinarily too costly, but may be <em>more</em> efficient
* than alternatives when traversal operations vastly outnumber
* mutations, and is useful when you cannot or don't want to
* synchronize traversals, yet need to preclude interference among
@@ -53,14 +64,14 @@
* reference to the state of the array at the point that the iterator
* was created. This array never changes during the lifetime of the
* iterator, so interference is impossible and the iterator is
- * guaranteed not to throw <tt>ConcurrentModificationException</tt>.
+ * guaranteed not to throw {@code ConcurrentModificationException}.
* The iterator will not reflect additions, removals, or changes to
* the list since the iterator was created. Element-changing
- * operations on iterators themselves (<tt>remove</tt>, <tt>set</tt>, and
- * <tt>add</tt>) are not supported. These methods throw
- * <tt>UnsupportedOperationException</tt>.
+ * operations on iterators themselves ({@code remove}, {@code set}, and
+ * {@code add}) are not supported. These methods throw
+ * {@code UnsupportedOperationException}.
*
- * <p>All elements are permitted, including <tt>null</tt>.
+ * <p>All elements are permitted, including {@code null}.
*
* <p>Memory consistency effects: As with other concurrent
* collections, actions in a thread prior to placing an object into a
@@ -82,10 +93,10 @@
private static final long serialVersionUID = 8673264195747942595L;
/** The lock protecting all mutators */
- transient final ReentrantLock lock = new ReentrantLock();
+ final transient ReentrantLock lock = new ReentrantLock();
/** The array, accessed only via getArray/setArray. */
- private volatile transient Object[] array;
+ private transient volatile Object[] array;
/**
* Gets the array. Non-private so as to also be accessible
@@ -118,10 +129,15 @@
* @throws NullPointerException if the specified collection is null
*/
public CopyOnWriteArrayList(Collection<? extends E> c) {
- Object[] elements = c.toArray();
- // c.toArray might (incorrectly) not return Object[] (see 6260652)
- if (elements.getClass() != Object[].class)
- elements = Arrays.copyOf(elements, elements.length, Object[].class);
+ Object[] elements;
+ if (c.getClass() == CopyOnWriteArrayList.class)
+ elements = ((CopyOnWriteArrayList<?>)c).getArray();
+ else {
+ elements = c.toArray();
+ // c.toArray might (incorrectly) not return Object[] (see 6260652)
+ if (elements.getClass() != Object[].class)
+ elements = Arrays.copyOf(elements, elements.length, Object[].class);
+ }
setArray(elements);
}
@@ -146,9 +162,9 @@
}
/**
- * Returns <tt>true</tt> if this list contains no elements.
+ * Returns {@code true} if this list contains no elements.
*
- * @return <tt>true</tt> if this list contains no elements
+ * @return {@code true} if this list contains no elements
*/
public boolean isEmpty() {
return size() == 0;
@@ -158,7 +174,7 @@
* Tests for equality, coping with nulls.
*/
private static boolean eq(Object o1, Object o2) {
- return (o1 == null ? o2 == null : o1.equals(o2));
+ return (o1 == null) ? o2 == null : o1.equals(o2);
}
/**
@@ -205,13 +221,13 @@
}
/**
- * Returns <tt>true</tt> if this list contains the specified element.
- * More formally, returns <tt>true</tt> if and only if this list contains
- * at least one element <tt>e</tt> such that
+ * 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
* <tt>(o==null ? e==null : o.equals(e))</tt>.
*
* @param o element whose presence in this list is to be tested
- * @return <tt>true</tt> if this list contains the specified element
+ * @return {@code true} if this list contains the specified element
*/
public boolean contains(Object o) {
Object[] elements = getArray();
@@ -228,17 +244,17 @@
/**
* Returns the index of the first occurrence of the specified element in
- * this list, searching forwards from <tt>index</tt>, or returns -1 if
+ * this list, searching forwards from {@code index}, or returns -1 if
* the element is not found.
- * More formally, returns the lowest index <tt>i</tt> such that
+ * More formally, returns the lowest index {@code i} such that
* <tt>(i >= index && (e==null ? get(i)==null : e.equals(get(i))))</tt>,
* or -1 if there is no such index.
*
* @param e element to search for
* @param index index to start searching from
* @return the index of the first occurrence of the element in
- * this list at position <tt>index</tt> or later in the list;
- * <tt>-1</tt> if the element is not found.
+ * this list at position {@code index} or later in the list;
+ * {@code -1} if the element is not found.
* @throws IndexOutOfBoundsException if the specified index is negative
*/
public int indexOf(E e, int index) {
@@ -256,16 +272,16 @@
/**
* Returns the index of the last occurrence of the specified element in
- * this list, searching backwards from <tt>index</tt>, or returns -1 if
+ * this list, searching backwards from {@code index}, or returns -1 if
* the element is not found.
- * More formally, returns the highest index <tt>i</tt> such that
+ * More formally, returns the highest index {@code i} such that
* <tt>(i <= index && (e==null ? get(i)==null : e.equals(get(i))))</tt>,
* or -1 if there is no such index.
*
* @param e element to search for
* @param index index to start searching backwards from
* @return the index of the last occurrence of the element at position
- * less than or equal to <tt>index</tt> in this list;
+ * less than or equal to {@code index} in this list;
* -1 if the element is not found.
* @throws IndexOutOfBoundsException if the specified index is greater
* than or equal to the current size of this list
@@ -323,7 +339,7 @@
* <p>If this list fits in the specified array with room to spare
* (i.e., the array has more elements than this list), the element in
* the array immediately following the end of the list is set to
- * <tt>null</tt>. (This is useful in determining the length of this
+ * {@code null}. (This is useful in determining the length of this
* list <i>only</i> if the caller knows that this list does not contain
* any null elements.)
*
@@ -332,14 +348,14 @@
* precise control over the runtime type of the output array, and may,
* under certain circumstances, be used to save allocation costs.
*
- * <p>Suppose <tt>x</tt> is a list known to contain only strings.
+ * <p>Suppose {@code x} is a list known to contain only strings.
* The following code can be used to dump the list into a newly
- * allocated array of <tt>String</tt>:
+ * allocated array of {@code String}:
*
* <pre> {@code String[] y = x.toArray(new String[0]);}</pre>
*
- * Note that <tt>toArray(new Object[0])</tt> is identical in function to
- * <tt>toArray()</tt>.
+ * Note that {@code toArray(new Object[0])} is identical in function to
+ * {@code toArray()}.
*
* @param a the array into which the elements of the list are to
* be stored, if it is big enough; otherwise, a new array of the
@@ -412,7 +428,7 @@
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
- * @return <tt>true</tt> (as specified by {@link Collection#add})
+ * @return {@code true} (as specified by {@link Collection#add})
*/
public boolean add(E e) {
final ReentrantLock lock = this.lock;
@@ -496,45 +512,54 @@
* 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
- * <tt>i</tt> such that
+ * {@code i} such that
* <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>
- * (if such an element exists). Returns <tt>true</tt> if this list
+ * (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).
*
* @param o element to be removed from this list, if present
- * @return <tt>true</tt> if this list contained the specified element
+ * @return {@code true} if this list contained the specified element
*/
public boolean remove(Object o) {
+ Object[] snapshot = getArray();
+ int index = indexOf(o, snapshot, 0, snapshot.length);
+ return (index < 0) ? false : remove(o, snapshot, index);
+ }
+
+ /**
+ * A version of remove(Object) using the strong hint that given
+ * 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 {
- Object[] elements = getArray();
- int len = elements.length;
- if (len != 0) {
- // Copy while searching for element to remove
- // This wins in the normal case of element being present
- int newlen = len - 1;
- Object[] newElements = new Object[newlen];
-
- for (int i = 0; i < newlen; ++i) {
- if (eq(o, elements[i])) {
- // found one; copy remaining and exit
- for (int k = i + 1; k < len; ++k)
- newElements[k-1] = elements[k];
- setArray(newElements);
- return true;
- } else
- newElements[i] = elements[i];
+ 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])) {
+ index = i;
+ break findIndex;
+ }
}
-
- // special handling for last cell
- if (eq(o, elements[newlen])) {
- setArray(newElements);
- return true;
- }
+ if (index >= len)
+ return false;
+ if (current[index] == o)
+ break findIndex;
+ index = indexOf(o, current, index, len);
+ if (index < 0)
+ return false;
}
- return false;
+ Object[] newElements = new Object[len - 1];
+ System.arraycopy(current, 0, newElements, 0, index);
+ System.arraycopy(current, index + 1,
+ newElements, index,
+ len - index - 1);
+ setArray(newElements);
+ return true;
} finally {
lock.unlock();
}
@@ -542,10 +567,10 @@
/**
* Removes from this list all of the elements whose index is between
- * <tt>fromIndex</tt>, inclusive, and <tt>toIndex</tt>, exclusive.
+ * {@code fromIndex}, inclusive, and {@code toIndex}, exclusive.
* Shifts any succeeding elements to the left (reduces their index).
- * This call shortens the list by <tt>(toIndex - fromIndex)</tt> elements.
- * (If <tt>toIndex==fromIndex</tt>, this operation has no effect.)
+ * This call shortens the list by {@code (toIndex - fromIndex)} elements.
+ * (If {@code toIndex==fromIndex}, this operation has no effect.)
*
* @param fromIndex index of first element to be removed
* @param toIndex index after last element to be removed
@@ -581,23 +606,34 @@
* Appends the element, if not present.
*
* @param e element to be added to this list, if absent
- * @return <tt>true</tt> if the element was added
+ * @return {@code true} if the element was added
*/
public boolean addIfAbsent(E e) {
+ Object[] snapshot = getArray();
+ return indexOf(e, snapshot, 0, snapshot.length) >= 0 ? false :
+ addIfAbsent(e, snapshot);
+ }
+
+ /**
+ * A version of addIfAbsent using the strong hint that given
+ * recent snapshot does not contain e.
+ */
+ private boolean addIfAbsent(E e, Object[] snapshot) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
- // Copy while checking if already present.
- // This wins in the most common case where it is not present
- Object[] elements = getArray();
- int len = elements.length;
- Object[] newElements = new Object[len + 1];
- for (int i = 0; i < len; ++i) {
- if (eq(e, elements[i]))
- return false; // exit, throwing away copy
- else
- newElements[i] = elements[i];
+ 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]))
+ return false;
+ if (indexOf(e, current, common, len) >= 0)
+ return false;
}
+ Object[] newElements = Arrays.copyOf(current, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
@@ -607,11 +643,11 @@
}
/**
- * Returns <tt>true</tt> if this list contains all of the elements of the
+ * Returns {@code true} if this list contains all of the elements of the
* specified collection.
*
* @param c collection to be checked for containment in this list
- * @return <tt>true</tt> if this list contains all of the elements of the
+ * @return {@code true} if this list contains all of the elements of the
* specified collection
* @throws NullPointerException if the specified collection is null
* @see #contains(Object)
@@ -632,7 +668,7 @@
* in this class because of the need for an internal temporary array.
*
* @param c collection containing elements to be removed from this list
- * @return <tt>true</tt> if this list changed as a result of the call
+ * @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
* (<a href="../Collection.html#optional-restrictions">optional</a>)
@@ -675,7 +711,7 @@
* its elements that are not contained in the specified collection.
*
* @param c collection containing elements to be retained in this list
- * @return <tt>true</tt> if this list changed as a result of the call
+ * @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
* (<a href="../Collection.html#optional-restrictions">optional</a>)
@@ -727,22 +763,22 @@
Object[] cs = c.toArray();
if (cs.length == 0)
return 0;
- Object[] uniq = new Object[cs.length];
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
int added = 0;
- for (int i = 0; i < cs.length; ++i) { // scan for duplicates
+ // uniquify and compact elements in cs
+ for (int i = 0; i < cs.length; ++i) {
Object e = cs[i];
if (indexOf(e, elements, 0, len) < 0 &&
- indexOf(e, uniq, 0, added) < 0)
- uniq[added++] = e;
+ indexOf(e, cs, 0, added) < 0)
+ cs[added++] = e;
}
if (added > 0) {
Object[] newElements = Arrays.copyOf(elements, len + added);
- System.arraycopy(uniq, 0, newElements, len, added);
+ System.arraycopy(cs, 0, newElements, len, added);
setArray(newElements);
}
return added;
@@ -771,12 +807,13 @@
* collection's iterator.
*
* @param c collection containing elements to be added to this list
- * @return <tt>true</tt> if this list changed as a result of the call
+ * @return {@code true} if this list changed as a result of the call
* @throws NullPointerException if the specified collection is null
* @see #add(Object)
*/
public boolean addAll(Collection<? extends E> c) {
- Object[] cs = c.toArray();
+ Object[] cs = (c.getClass() == CopyOnWriteArrayList.class) ?
+ ((CopyOnWriteArrayList<?>)c).getArray() : c.toArray();
if (cs.length == 0)
return false;
final ReentrantLock lock = this.lock;
@@ -784,9 +821,13 @@
try {
Object[] elements = getArray();
int len = elements.length;
- Object[] newElements = Arrays.copyOf(elements, len + cs.length);
- System.arraycopy(cs, 0, newElements, len, cs.length);
- setArray(newElements);
+ if (len == 0 && cs.getClass() == Object[].class)
+ setArray(cs);
+ else {
+ Object[] newElements = Arrays.copyOf(elements, len + cs.length);
+ System.arraycopy(cs, 0, newElements, len, cs.length);
+ setArray(newElements);
+ }
return true;
} finally {
lock.unlock();
@@ -804,7 +845,7 @@
* @param index index at which to insert the first element
* from the specified collection
* @param c collection containing elements to be added to this list
- * @return <tt>true</tt> if this list changed as a result of the call
+ * @return {@code true} if this list changed as a result of the call
* @throws IndexOutOfBoundsException {@inheritDoc}
* @throws NullPointerException if the specified collection is null
* @see #add(int,Object)
@@ -840,6 +881,74 @@
}
}
+ public void forEach(Consumer<? super E> 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];
+ action.accept(e);
+ }
+ }
+
+ public boolean removeIf(Predicate<? super E> 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));
+ return true;
+ }
+ }
+ return false;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ public void replaceAll(UnaryOperator<E> operator) {
+ if (operator == null) throw new NullPointerException();
+ final ReentrantLock lock = this.lock;
+ lock.lock();
+ try {
+ Object[] elements = getArray();
+ int len = elements.length;
+ Object[] newElements = Arrays.copyOf(elements, len);
+ for (int i = 0; i < len; ++i) {
+ @SuppressWarnings("unchecked") E e = (E) elements[i];
+ newElements[i] = operator.apply(e);
+ }
+ setArray(newElements);
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ public void sort(Comparator<? super E> c) {
+ final ReentrantLock lock = this.lock;
+ lock.lock();
+ try {
+ 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();
+ }
+ }
+
/**
* Saves this list to a stream (that is, serializes it).
*
@@ -886,8 +995,8 @@
* Returns a string representation of this list. The string
* representation consists of the string representations of the list's
* elements in the order they are returned by its iterator, enclosed in
- * square brackets (<tt>"[]"</tt>). Adjacent elements are separated by
- * the characters <tt>", "</tt> (comma and space). Elements are
+ * square brackets ({@code "[]"}). Adjacent elements are separated by
+ * the characters {@code ", "} (comma and space). Elements are
* converted to strings as by {@link String#valueOf(Object)}.
*
* @return a string representation of this list
@@ -953,7 +1062,7 @@
* <p>The returned iterator provides a snapshot of the state of the list
* when the iterator was constructed. No synchronization is needed while
* traversing the iterator. The iterator does <em>NOT</em> support the
- * <tt>remove</tt> method.
+ * {@code remove} method.
*
* @return an iterator over the elements in this list in proper sequence
*/
@@ -967,7 +1076,7 @@
* <p>The returned iterator provides a snapshot of the state of the list
* when the iterator was constructed. No synchronization is needed while
* traversing the iterator. The iterator does <em>NOT</em> support the
- * <tt>remove</tt>, <tt>set</tt> or <tt>add</tt> methods.
+ * {@code remove}, {@code set} or {@code add} methods.
*/
public ListIterator<E> listIterator() {
return new COWIterator<E>(getArray(), 0);
@@ -979,7 +1088,7 @@
* <p>The returned iterator provides a snapshot of the state of the list
* when the iterator was constructed. No synchronization is needed while
* traversing the iterator. The iterator does <em>NOT</em> support the
- * <tt>remove</tt>, <tt>set</tt> or <tt>add</tt> methods.
+ * {@code remove}, {@code set} or {@code add} methods.
*
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
@@ -992,7 +1101,12 @@
return new COWIterator<E>(elements, index);
}
- private static class COWIterator<E> implements ListIterator<E> {
+ public Spliterator<E> spliterator() {
+ return Spliterators.spliterator
+ (getArray(), Spliterator.IMMUTABLE | Spliterator.ORDERED);
+ }
+
+ static final class COWIterator<E> implements ListIterator<E> {
/** Snapshot of the array */
private final Object[] snapshot;
/** Index of element to be returned by subsequent call to next. */
@@ -1035,7 +1149,7 @@
/**
* Not supported. Always throws UnsupportedOperationException.
- * @throws UnsupportedOperationException always; <tt>remove</tt>
+ * @throws UnsupportedOperationException always; {@code remove}
* is not supported by this iterator.
*/
public void remove() {
@@ -1044,7 +1158,7 @@
/**
* Not supported. Always throws UnsupportedOperationException.
- * @throws UnsupportedOperationException always; <tt>set</tt>
+ * @throws UnsupportedOperationException always; {@code set}
* is not supported by this iterator.
*/
public void set(E e) {
@@ -1053,7 +1167,7 @@
/**
* Not supported. Always throws UnsupportedOperationException.
- * @throws UnsupportedOperationException always; <tt>add</tt>
+ * @throws UnsupportedOperationException always; {@code add}
* is not supported by this iterator.
*/
public void add(E e) {
@@ -1061,12 +1175,13 @@
}
@Override
- @SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
- final int size = snapshot.length;
- for (int i=cursor; i < size; i++) {
- action.accept((E) snapshot[i]);
+ Object[] elements = snapshot;
+ final int size = elements.length;
+ for (int i = cursor; i < size; i++) {
+ @SuppressWarnings("unchecked") E e = (E) elements[i];
+ action.accept(e);
}
cursor = size;
}
@@ -1074,7 +1189,7 @@
/**
* Returns a view of the portion of this list between
- * <tt>fromIndex</tt>, inclusive, and <tt>toIndex</tt>, exclusive.
+ * {@code fromIndex}, inclusive, and {@code toIndex}, exclusive.
* The returned list is backed by this list, so changes in the
* returned list are reflected in this list.
*
@@ -1274,55 +1389,196 @@
}
}
- @Override
public void forEach(Consumer<? super E> action) {
- @SuppressWarnings("unchecked")
- final E[] elements = (E[]) l.getArray();
- checkForComodification();
- l.forEach(action, elements, offset, offset + size);
+ if (action == null) throw new NullPointerException();
+ int lo = offset;
+ int hi = offset + size;
+ Object[] a = expectedArray;
+ if (l.getArray() != a)
+ throw new ConcurrentModificationException();
+ if (lo < 0 || hi > a.length)
+ throw new IndexOutOfBoundsException();
+ for (int i = lo; i < hi; ++i) {
+ @SuppressWarnings("unchecked") E e = (E) a[i];
+ action.accept(e);
+ }
}
- @Override
+ public void replaceAll(UnaryOperator<E> operator) {
+ if (operator == null) throw new NullPointerException();
+ final ReentrantLock lock = l.lock;
+ lock.lock();
+ try {
+ int lo = offset;
+ int hi = offset + size;
+ Object[] elements = expectedArray;
+ if (l.getArray() != elements)
+ throw new ConcurrentModificationException();
+ int len = elements.length;
+ if (lo < 0 || hi > len)
+ throw new IndexOutOfBoundsException();
+ Object[] newElements = Arrays.copyOf(elements, len);
+ for (int i = lo; i < hi; ++i) {
+ @SuppressWarnings("unchecked") E e = (E) elements[i];
+ newElements[i] = operator.apply(e);
+ }
+ l.setArray(expectedArray = newElements);
+ } finally {
+ lock.unlock();
+ }
+ }
+
public void sort(Comparator<? super E> c) {
final ReentrantLock lock = l.lock;
lock.lock();
try {
- checkForComodification();
- l.sort(c, offset, offset + size);
- expectedArray = l.getArray();
+ int lo = offset;
+ int hi = offset + size;
+ Object[] elements = expectedArray;
+ if (l.getArray() != elements)
+ throw new ConcurrentModificationException();
+ int len = elements.length;
+ if (lo < 0 || hi > len)
+ throw new IndexOutOfBoundsException();
+ Object[] newElements = Arrays.copyOf(elements, len);
+ @SuppressWarnings("unchecked") E[] es = (E[])newElements;
+ Arrays.sort(es, lo, hi, c);
+ l.setArray(expectedArray = newElements);
} finally {
lock.unlock();
}
}
- @Override
- public boolean removeIf(Predicate<? super E> filter) {
- Objects.requireNonNull(filter);
+ public boolean removeAll(Collection<?> c) {
+ if (c == null) throw new NullPointerException();
+ boolean removed = false;
+ final ReentrantLock lock = l.lock;
+ lock.lock();
+ try {
+ int n = size;
+ if (n > 0) {
+ int lo = offset;
+ int hi = offset + n;
+ Object[] elements = expectedArray;
+ if (l.getArray() != elements)
+ throw new ConcurrentModificationException();
+ int len = elements.length;
+ if (lo < 0 || hi > len)
+ throw new IndexOutOfBoundsException();
+ int newSize = 0;
+ Object[] temp = new Object[n];
+ for (int i = lo; i < hi; ++i) {
+ Object element = elements[i];
+ if (!c.contains(element))
+ temp[newSize++] = element;
+ }
+ if (newSize != n) {
+ Object[] newElements = new Object[len - n + newSize];
+ System.arraycopy(elements, 0, newElements, 0, lo);
+ System.arraycopy(temp, 0, newElements, lo, newSize);
+ System.arraycopy(elements, hi, newElements,
+ lo + newSize, len - hi);
+ size = newSize;
+ removed = true;
+ l.setArray(expectedArray = newElements);
+ }
+ }
+ } finally {
+ lock.unlock();
+ }
+ return removed;
+ }
+
+ public boolean retainAll(Collection<?> c) {
+ if (c == null) throw new NullPointerException();
+ boolean removed = false;
final ReentrantLock lock = l.lock;
lock.lock();
try {
- checkForComodification();
- final int removeCount =
- l.removeIf(filter, offset, offset + size);
- expectedArray = l.getArray();
- size -= removeCount;
- return removeCount > 0;
+ int n = size;
+ if (n > 0) {
+ int lo = offset;
+ int hi = offset + n;
+ Object[] elements = expectedArray;
+ if (l.getArray() != elements)
+ throw new ConcurrentModificationException();
+ int len = elements.length;
+ if (lo < 0 || hi > len)
+ throw new IndexOutOfBoundsException();
+ int newSize = 0;
+ Object[] temp = new Object[n];
+ for (int i = lo; i < hi; ++i) {
+ Object element = elements[i];
+ if (c.contains(element))
+ temp[newSize++] = element;
+ }
+ if (newSize != n) {
+ Object[] newElements = new Object[len - n + newSize];
+ System.arraycopy(elements, 0, newElements, 0, lo);
+ System.arraycopy(temp, 0, newElements, lo, newSize);
+ System.arraycopy(elements, hi, newElements,
+ lo + newSize, len - hi);
+ size = newSize;
+ removed = true;
+ l.setArray(expectedArray = newElements);
+ }
+ }
} finally {
lock.unlock();
}
+ return removed;
}
- @Override
- public void replaceAll(UnaryOperator<E> operator) {
+ public boolean removeIf(Predicate<? super E> filter) {
+ if (filter == null) throw new NullPointerException();
+ boolean removed = false;
final ReentrantLock lock = l.lock;
lock.lock();
try {
- checkForComodification();
- l.replaceAll(operator, offset, offset + size);
- expectedArray = l.getArray();
+ int n = size;
+ if (n > 0) {
+ int lo = offset;
+ int hi = offset + n;
+ Object[] elements = expectedArray;
+ if (l.getArray() != elements)
+ throw new ConcurrentModificationException();
+ int len = elements.length;
+ if (lo < 0 || hi > len)
+ throw new IndexOutOfBoundsException();
+ int newSize = 0;
+ Object[] temp = new Object[n];
+ for (int i = lo; i < hi; ++i) {
+ @SuppressWarnings("unchecked") E e = (E) elements[i];
+ if (!filter.test(e))
+ temp[newSize++] = e;
+ }
+ if (newSize != n) {
+ Object[] newElements = new Object[len - n + newSize];
+ System.arraycopy(elements, 0, newElements, 0, lo);
+ System.arraycopy(temp, 0, newElements, lo, newSize);
+ System.arraycopy(elements, hi, newElements,
+ lo + newSize, len - hi);
+ size = newSize;
+ removed = true;
+ l.setArray(expectedArray = newElements);
+ }
+ }
} finally {
lock.unlock();
}
+ return removed;
+ }
+
+ public Spliterator<E> spliterator() {
+ int lo = offset;
+ int hi = offset + size;
+ Object[] a = expectedArray;
+ if (l.getArray() != a)
+ throw new ConcurrentModificationException();
+ if (lo < 0 || hi > a.length)
+ throw new IndexOutOfBoundsException();
+ return Spliterators.spliterator
+ (a, lo, hi, Spliterator.IMMUTABLE | Spliterator.ORDERED);
}
}
@@ -1380,11 +1636,12 @@
}
@Override
- @SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
- while (nextIndex() < size) {
- action.accept(it.next());
+ int s = size;
+ ListIterator<E> i = it;
+ while (nextIndex() < s) {
+ action.accept(i.next());
}
}
}
@@ -1405,139 +1662,4 @@
throw new Error(e);
}
}
-
- @Override
- @SuppressWarnings("unchecked")
- public void forEach(Consumer<? super E> action) {
- forEach(action, (E[]) getArray(), 0, size());
- }
-
- private void forEach(Consumer<? super E> action,
- final E[] elements,
- final int from, final int to) {
- Objects.requireNonNull(action);
- for (int i = from; i < to; i++) {
- action.accept(elements[i]);
- }
- }
-
- @Override
- public void sort(Comparator<? super E> c) {
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- sort(c, 0, size());
- } finally {
- lock.unlock();
- }
- }
-
- // must be called with this.lock held
- @SuppressWarnings("unchecked")
- private void sort(Comparator<? super E> c, final int from, final int to) {
- final E[] elements = (E[]) getArray();
- final E[] newElements = Arrays.copyOf(elements, elements.length);
- // only elements [from, to) are sorted
- Arrays.sort(newElements, from, to, c);
- setArray(newElements);
- }
-
- @Override
- public boolean removeIf(Predicate<? super E> filter) {
- Objects.requireNonNull(filter);
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- return removeIf(filter, 0, size()) > 0;
- } finally {
- lock.unlock();
- }
- }
-
- // must be called with this.lock held
- private int removeIf(Predicate<? super E> filter, final int from, final int to) {
- Objects.requireNonNull(filter);
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- @SuppressWarnings("unchecked")
- final E[] elements = (E[]) getArray();
-
- // figure out which elements are to be removed
- // any exception thrown from the filter predicate at this stage
- // will leave the collection unmodified
- int removeCount = 0;
- final int range = to - from;
- final BitSet removeSet = new BitSet(range);
- for (int i = 0; i < range; i++) {
- final E element = elements[from + i];
- if (filter.test(element)) {
- // removeSet is zero-based to keep its size small
- removeSet.set(i);
- removeCount++;
- }
- }
-
- // copy surviving elements into a new array
- if (removeCount > 0) {
- final int newSize = elements.length - removeCount;
- final int newRange = newSize - from;
- @SuppressWarnings("unchecked")
- final E[] newElements = (E[]) new Object[newSize];
- // copy elements before [from, to) unmodified
- for (int i = 0; i < from; i++) {
- newElements[i] = elements[i];
- }
- // elements [from, to) are subject to removal
- int j = 0;
- for (int i = 0; (i < range) && (j < newRange); i++) {
- i = removeSet.nextClearBit(i);
- if (i >= range) {
- break;
- }
- newElements[from + (j++)] = elements[from + i];
- }
- // copy any remaining elements beyond [from, to)
- j += from;
- for (int i = to; (i < elements.length) && (j < newSize); i++) {
- newElements[j++] = elements[i];
- }
- setArray(newElements);
- }
-
- return removeCount;
- } finally {
- lock.unlock();
- }
- }
-
- @Override
- public void replaceAll(UnaryOperator<E> operator) {
- Objects.requireNonNull(operator);
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- replaceAll(operator, 0, size());
- } finally {
- lock.unlock();
- }
- }
-
- // must be called with this.lock held
- @SuppressWarnings("unchecked")
- private void replaceAll(UnaryOperator<E> operator, final int from, final int to) {
- final E[] elements = (E[]) getArray();
- final E[] newElements = (E[]) new Object[elements.length];
- for (int i = 0; i < from; i++) {
- newElements[i] = elements[i];
- }
- // the operator is only applied to elements [from, to)
- for (int i = from; i < to; i++) {
- newElements[i] = operator.apply(elements[i]);
- }
- for (int i = to; i < elements.length; i++) {
- newElements[i] = elements[i];
- }
- setArray(newElements);
- }
}
--- a/jdk/src/share/classes/java/util/concurrent/CopyOnWriteArraySet.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/java/util/concurrent/CopyOnWriteArraySet.java Wed Jul 03 14:35:10 2013 -0700
@@ -34,7 +34,14 @@
*/
package java.util.concurrent;
-import java.util.*;
+import java.util.Collection;
+import java.util.Set;
+import java.util.AbstractSet;
+import java.util.Iterator;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import java.util.function.Predicate;
+import java.util.function.Consumer;
/**
* A {@link java.util.Set} that uses an internal {@link CopyOnWriteArrayList}
@@ -45,17 +52,17 @@
* vastly outnumber mutative operations, and you need
* to prevent interference among threads during traversal.
* <li>It is thread-safe.
- * <li>Mutative operations (<tt>add</tt>, <tt>set</tt>, <tt>remove</tt>, etc.)
+ * <li>Mutative operations ({@code add}, {@code set}, {@code remove}, etc.)
* are expensive since they usually entail copying the entire underlying
* array.
- * <li>Iterators do not support the mutative <tt>remove</tt> operation.
+ * <li>Iterators do not support the mutative {@code remove} operation.
* <li>Traversal via iterators is fast and cannot encounter
* interference from other threads. Iterators rely on
* unchanging snapshots of the array at the time the iterators were
* constructed.
* </ul>
*
- * <p> <b>Sample Usage.</b> The following code sketch uses a
+ * <p><b>Sample Usage.</b> The following code sketch uses a
* copy-on-write set to maintain a set of Handler objects that
* perform some action upon state updates.
*
@@ -73,7 +80,7 @@
* public void update() {
* changeState();
* for (Handler handler : handlers)
- * handler.handle();
+ * handler.handle();
* }
* }}</pre>
*
@@ -107,8 +114,15 @@
* @throws NullPointerException if the specified collection is null
*/
public CopyOnWriteArraySet(Collection<? extends E> c) {
- al = new CopyOnWriteArrayList<E>();
- al.addAllAbsent(c);
+ if (c.getClass() == CopyOnWriteArraySet.class) {
+ @SuppressWarnings("unchecked") CopyOnWriteArraySet<E> cc =
+ (CopyOnWriteArraySet<E>)c;
+ al = new CopyOnWriteArrayList<E>(cc.al);
+ }
+ else {
+ al = new CopyOnWriteArrayList<E>();
+ al.addAllAbsent(c);
+ }
}
/**
@@ -121,22 +135,22 @@
}
/**
- * Returns <tt>true</tt> if this set contains no elements.
+ * Returns {@code true} if this set contains no elements.
*
- * @return <tt>true</tt> if this set contains no elements
+ * @return {@code true} if this set contains no elements
*/
public boolean isEmpty() {
return al.isEmpty();
}
/**
- * Returns <tt>true</tt> if this set contains the specified element.
- * More formally, returns <tt>true</tt> if and only if this set
- * contains an element <tt>e</tt> such that
+ * 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
* <tt>(o==null ? e==null : o.equals(e))</tt>.
*
* @param o element whose presence in this set is to be tested
- * @return <tt>true</tt> if this set contains the specified element
+ * @return {@code true} if this set contains the specified element
*/
public boolean contains(Object o) {
return al.contains(o);
@@ -172,7 +186,7 @@
* <p>If this set fits in the specified array with room to spare
* (i.e., the array has more elements than this set), the element in
* the array immediately following the end of the set is set to
- * <tt>null</tt>. (This is useful in determining the length of this
+ * {@code null}. (This is useful in determining the length of this
* set <i>only</i> if the caller knows that this set does not contain
* any null elements.)
*
@@ -185,14 +199,14 @@
* precise control over the runtime type of the output array, and may,
* under certain circumstances, be used to save allocation costs.
*
- * <p>Suppose <tt>x</tt> is a set known to contain only strings.
+ * <p>Suppose {@code x} is a set known to contain only strings.
* The following code can be used to dump the set into a newly allocated
- * array of <tt>String</tt>:
+ * array of {@code String}:
*
* <pre> {@code String[] y = x.toArray(new String[0]);}</pre>
*
- * Note that <tt>toArray(new Object[0])</tt> is identical in function to
- * <tt>toArray()</tt>.
+ * Note that {@code toArray(new Object[0])} is identical in function to
+ * {@code toArray()}.
*
* @param a the array into which the elements of this set are to be
* stored, if it is big enough; otherwise, a new array of the same
@@ -217,15 +231,15 @@
/**
* Removes the specified element from this set if it is present.
- * More formally, removes an element <tt>e</tt> such that
+ * More formally, removes an element {@code e} such that
* <tt>(o==null ? e==null : o.equals(e))</tt>,
- * if this set contains such an element. Returns <tt>true</tt> if
+ * 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 <tt>true</tt> if this set contained the specified element
+ * @return {@code true} if this set contained the specified element
*/
public boolean remove(Object o) {
return al.remove(o);
@@ -233,14 +247,14 @@
/**
* Adds the specified element to this set if it is not already present.
- * More formally, adds the specified element <tt>e</tt> to this set if
- * the set contains no element <tt>e2</tt> such that
+ * More formally, adds the specified element {@code e} to this set if
+ * the set contains no element {@code e2} such that
* <tt>(e==null ? e2==null : e.equals(e2))</tt>.
* If this set already contains the element, the call leaves the set
- * unchanged and returns <tt>false</tt>.
+ * unchanged and returns {@code false}.
*
* @param e element to be added to this set
- * @return <tt>true</tt> if this set did not already contain the specified
+ * @return {@code true} if this set did not already contain the specified
* element
*/
public boolean add(E e) {
@@ -248,12 +262,12 @@
}
/**
- * Returns <tt>true</tt> if this set contains all of the elements of the
+ * Returns {@code true} if this set contains all of the elements of the
* specified collection. If the specified collection is also a set, this
- * method returns <tt>true</tt> if it is a <i>subset</i> of this set.
+ * method returns {@code true} if it is a <i>subset</i> of this set.
*
* @param c collection to be checked for containment in this set
- * @return <tt>true</tt> if this set contains all of the elements of the
+ * @return {@code true} if this set contains all of the elements of the
* specified collection
* @throws NullPointerException if the specified collection is null
* @see #contains(Object)
@@ -265,13 +279,13 @@
/**
* Adds all of the elements in the specified collection to this set if
* they're not already present. If the specified collection is also a
- * set, the <tt>addAll</tt> operation effectively modifies this set so
+ * set, the {@code addAll} operation effectively modifies this set so
* that its value is the <i>union</i> of the two sets. The behavior of
* this operation is undefined if the specified collection is modified
* while the operation is in progress.
*
* @param c collection containing elements to be added to this set
- * @return <tt>true</tt> if this set changed as a result of the call
+ * @return {@code true} if this set changed as a result of the call
* @throws NullPointerException if the specified collection is null
* @see #add(Object)
*/
@@ -286,7 +300,7 @@
* <i>asymmetric set difference</i> of the two sets.
*
* @param c collection containing elements to be removed from this set
- * @return <tt>true</tt> if this set changed as a result of the call
+ * @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)
* @throws NullPointerException if this set contains a null element and the
@@ -307,7 +321,7 @@
* two sets.
*
* @param c collection containing elements to be retained in this set
- * @return <tt>true</tt> if this set changed as a result of the call
+ * @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)
* @throws NullPointerException if this set contains a null element and the
@@ -326,7 +340,7 @@
* <p>The returned iterator provides a snapshot of the state of the set
* when the iterator was constructed. No synchronization is needed while
* traversing the iterator. The iterator does <em>NOT</em> support the
- * <tt>remove</tt> method.
+ * {@code remove} method.
*
* @return an iterator over the elements in this set
*/
@@ -338,7 +352,7 @@
* Compares the specified object with this set for equality.
* Returns {@code true} if the specified object is the same object
* as this object, or if it is also a {@link Set} and the elements
- * returned by an {@linkplain List#iterator() iterator} over the
+ * returned by an {@linkplain Set#iterator() iterator} over the
* specified set are the same as the elements returned by an
* iterator over this set. More formally, the two iterators are
* considered to return the same elements if they return the same
@@ -382,6 +396,19 @@
return k == len;
}
+ public boolean removeIf(Predicate<? super E> filter) {
+ return al.removeIf(filter);
+ }
+
+ public void forEach(Consumer<? super E> action) {
+ al.forEach(action);
+ }
+
+ public Spliterator<E> spliterator() {
+ return Spliterators.spliterator
+ (al.getArray(), Spliterator.IMMUTABLE | Spliterator.DISTINCT);
+ }
+
/**
* Tests for equality, coping with nulls.
*/
--- a/jdk/src/share/classes/java/util/concurrent/CountDownLatch.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/java/util/concurrent/CountDownLatch.java Wed Jul 03 14:35:10 2013 -0700
@@ -92,15 +92,15 @@
* private final CountDownLatch startSignal;
* private final CountDownLatch doneSignal;
* Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
- * this.startSignal = startSignal;
- * this.doneSignal = doneSignal;
+ * this.startSignal = startSignal;
+ * this.doneSignal = doneSignal;
* }
* public void run() {
- * try {
- * startSignal.await();
- * doWork();
- * doneSignal.countDown();
- * } catch (InterruptedException ex) {} // return;
+ * try {
+ * startSignal.await();
+ * doWork();
+ * doneSignal.countDown();
+ * } catch (InterruptedException ex) {} // return;
* }
*
* void doWork() { ... }
@@ -130,14 +130,14 @@
* private final CountDownLatch doneSignal;
* private final int i;
* WorkerRunnable(CountDownLatch doneSignal, int i) {
- * this.doneSignal = doneSignal;
- * this.i = i;
+ * this.doneSignal = doneSignal;
+ * this.i = i;
* }
* public void run() {
- * try {
- * doWork(i);
- * doneSignal.countDown();
- * } catch (InterruptedException ex) {} // return;
+ * try {
+ * doWork(i);
+ * doneSignal.countDown();
+ * } catch (InterruptedException ex) {} // return;
* }
*
* void doWork() { ... }
--- a/jdk/src/share/classes/java/util/concurrent/CyclicBarrier.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/java/util/concurrent/CyclicBarrier.java Wed Jul 03 14:35:10 2013 -0700
@@ -45,14 +45,14 @@
* <em>cyclic</em> because it can be re-used after the waiting threads
* are released.
*
- * <p>A <tt>CyclicBarrier</tt> supports an optional {@link Runnable} command
+ * <p>A {@code CyclicBarrier} supports an optional {@link Runnable} command
* that is run once per barrier point, after the last thread in the party
* arrives, but before any threads are released.
* This <em>barrier action</em> is useful
* for updating shared-state before any of the parties continue.
*
- * <p><b>Sample usage:</b> Here is an example of
- * using a barrier in a parallel decomposition design:
+ * <p><b>Sample usage:</b> Here is an example of using a barrier in a
+ * parallel decomposition design:
*
* <pre> {@code
* class Solver {
@@ -81,16 +81,20 @@
* public Solver(float[][] matrix) {
* data = matrix;
* N = matrix.length;
- * barrier = new CyclicBarrier(N,
- * new Runnable() {
- * public void run() {
- * mergeRows(...);
- * }
- * });
- * for (int i = 0; i < N; ++i)
- * new Thread(new Worker(i)).start();
+ * Runnable barrierAction =
+ * new Runnable() { public void run() { mergeRows(...); }};
+ * barrier = new CyclicBarrier(N, barrierAction);
*
- * waitUntilDone();
+ * List<Thread> threads = new ArrayList<Thread>(N);
+ * for (int i = 0; i < N; i++) {
+ * Thread thread = new Thread(new Worker(i));
+ * threads.add(thread);
+ * thread.start();
+ * }
+ *
+ * // wait until done
+ * for (Thread thread : threads)
+ * thread.join();
* }
* }}</pre>
*
@@ -98,8 +102,8 @@
* barrier until all rows have been processed. When all rows are processed
* the supplied {@link Runnable} barrier action is executed and merges the
* rows. If the merger
- * determines that a solution has been found then <tt>done()</tt> will return
- * <tt>true</tt> and each worker will terminate.
+ * determines that a solution has been found then {@code done()} will return
+ * {@code true} and each worker will terminate.
*
* <p>If the barrier action does not rely on the parties being suspended when
* it is executed, then any of the threads in the party could execute that
@@ -112,7 +116,7 @@
* // log the completion of this iteration
* }}</pre>
*
- * <p>The <tt>CyclicBarrier</tt> uses an all-or-none breakage model
+ * <p>The {@code CyclicBarrier} uses an all-or-none breakage model
* for failed synchronization attempts: If a thread leaves a barrier
* point prematurely because of interruption, failure, or timeout, all
* other threads waiting at that barrier point will also leave
@@ -139,7 +143,7 @@
* is reset. There can be many generations associated with threads
* using the barrier - due to the non-deterministic way the lock
* may be allocated to waiting threads - but only one of these
- * can be active at a time (the one to which <tt>count</tt> applies)
+ * can be active at a time (the one to which {@code count} applies)
* and all the rest are either broken or tripped.
* There need not be an active generation if there has been a break
* but no subsequent reset.
@@ -259,7 +263,7 @@
}
/**
- * Creates a new <tt>CyclicBarrier</tt> that will trip when the
+ * Creates a new {@code CyclicBarrier} that will trip when the
* given number of parties (threads) are waiting upon it, and which
* will execute the given barrier action when the barrier is tripped,
* performed by the last thread entering the barrier.
@@ -278,7 +282,7 @@
}
/**
- * Creates a new <tt>CyclicBarrier</tt> that will trip when the
+ * Creates a new {@code CyclicBarrier} that will trip when the
* given number of parties (threads) are waiting upon it, and
* does not perform a predefined action when the barrier is tripped.
*
@@ -301,7 +305,7 @@
/**
* Waits until all {@linkplain #getParties parties} have invoked
- * <tt>await</tt> on this barrier.
+ * {@code await} on this barrier.
*
* <p>If the current thread is not the last to arrive then it is
* disabled for thread scheduling purposes and lies dormant until
@@ -326,7 +330,7 @@
*
* <p>If the barrier is {@link #reset} while any thread is waiting,
* or if the barrier {@linkplain #isBroken is broken} when
- * <tt>await</tt> is invoked, or while any thread is waiting, then
+ * {@code await} is invoked, or while any thread is waiting, then
* {@link BrokenBarrierException} is thrown.
*
* <p>If any thread is {@linkplain Thread#interrupt interrupted} while waiting,
@@ -343,7 +347,7 @@
* the broken state.
*
* @return the arrival index of the current thread, where index
- * <tt>{@link #getParties()} - 1</tt> indicates the first
+ * {@code getParties() - 1} indicates the first
* to arrive and zero indicates the last to arrive
* @throws InterruptedException if the current thread was interrupted
* while waiting
@@ -351,7 +355,7 @@
* interrupted or timed out while the current thread was
* waiting, or the barrier was reset, or the barrier was
* broken when {@code await} was called, or the barrier
- * action (if present) failed due an exception.
+ * action (if present) failed due to an exception
*/
public int await() throws InterruptedException, BrokenBarrierException {
try {
@@ -363,7 +367,7 @@
/**
* Waits until all {@linkplain #getParties parties} have invoked
- * <tt>await</tt> on this barrier, or the specified waiting time elapses.
+ * {@code await} on this barrier, or the specified waiting time elapses.
*
* <p>If the current thread is not the last to arrive then it is
* disabled for thread scheduling purposes and lies dormant until
@@ -393,7 +397,7 @@
*
* <p>If the barrier is {@link #reset} while any thread is waiting,
* or if the barrier {@linkplain #isBroken is broken} when
- * <tt>await</tt> is invoked, or while any thread is waiting, then
+ * {@code await} is invoked, or while any thread is waiting, then
* {@link BrokenBarrierException} is thrown.
*
* <p>If any thread is {@linkplain Thread#interrupt interrupted} while
@@ -412,16 +416,17 @@
* @param timeout the time to wait for the barrier
* @param unit the time unit of the timeout parameter
* @return the arrival index of the current thread, where index
- * <tt>{@link #getParties()} - 1</tt> indicates the first
+ * {@code getParties() - 1} indicates the first
* to arrive and zero indicates the last to arrive
* @throws InterruptedException if the current thread was interrupted
* while waiting
- * @throws TimeoutException if the specified timeout elapses
+ * @throws TimeoutException if the specified timeout elapses.
+ * In this case the barrier will be broken.
* @throws BrokenBarrierException if <em>another</em> thread was
* interrupted or timed out while the current thread was
* waiting, or the barrier was reset, or the barrier was broken
* when {@code await} was called, or the barrier action (if
- * present) failed due an exception
+ * present) failed due to an exception
*/
public int await(long timeout, TimeUnit unit)
throws InterruptedException,
--- a/jdk/src/share/classes/java/util/concurrent/DelayQueue.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/java/util/concurrent/DelayQueue.java Wed Jul 03 14:35:10 2013 -0700
@@ -33,28 +33,31 @@
* http://creativecommons.org/publicdomain/zero/1.0/
*/
-
package java.util.concurrent;
-import java.util.concurrent.locks.*;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
import java.util.*;
/**
* An unbounded {@linkplain BlockingQueue blocking queue} of
- * <tt>Delayed</tt> elements, in which an element can only be taken
+ * {@code Delayed} elements, in which an element can only be taken
* when its delay has expired. The <em>head</em> of the queue is that
- * <tt>Delayed</tt> element whose delay expired furthest in the
- * past. If no delay has expired there is no head and <tt>poll</tt>
- * will return <tt>null</tt>. Expiration occurs when an element's
- * <tt>getDelay(TimeUnit.NANOSECONDS)</tt> method returns a value less
+ * {@code Delayed} element whose delay expired furthest in the
+ * past. If no delay has expired there is no head and {@code poll}
+ * will return {@code null}. Expiration occurs when an element's
+ * {@code getDelay(TimeUnit.NANOSECONDS)} method returns a value less
* than or equal to zero. Even though unexpired elements cannot be
- * removed using <tt>take</tt> or <tt>poll</tt>, they are otherwise
- * treated as normal elements. For example, the <tt>size</tt> method
+ * removed using {@code take} or {@code poll}, they are otherwise
+ * treated as normal elements. For example, the {@code size} method
* returns the count of both expired and unexpired elements.
* This queue does not permit null elements.
*
* <p>This class and its iterator implement all of the
* <em>optional</em> methods of the {@link Collection} and {@link
- * Iterator} interfaces.
+ * Iterator} interfaces. The Iterator provided in method {@link
+ * #iterator()} is <em>not</em> guaranteed to traverse the elements of
+ * the DelayQueue in any particular order.
*
* <p>This class is a member of the
* <a href="{@docRoot}/../technotes/guides/collections/index.html">
@@ -64,11 +67,10 @@
* @author Doug Lea
* @param <E> the type of elements held in this collection
*/
-
public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
implements BlockingQueue<E> {
- private transient final ReentrantLock lock = new ReentrantLock();
+ private final transient ReentrantLock lock = new ReentrantLock();
private final PriorityQueue<E> q = new PriorityQueue<E>();
/**
@@ -97,12 +99,12 @@
private final Condition available = lock.newCondition();
/**
- * Creates a new <tt>DelayQueue</tt> that is initially empty.
+ * Creates a new {@code DelayQueue} that is initially empty.
*/
public DelayQueue() {}
/**
- * Creates a <tt>DelayQueue</tt> initially containing the elements of the
+ * Creates a {@code DelayQueue} initially containing the elements of the
* given collection of {@link Delayed} instances.
*
* @param c the collection of elements to initially contain
@@ -117,7 +119,7 @@
* Inserts the specified element into this delay queue.
*
* @param e the element to add
- * @return <tt>true</tt> (as specified by {@link Collection#add})
+ * @return {@code true} (as specified by {@link Collection#add})
* @throws NullPointerException if the specified element is null
*/
public boolean add(E e) {
@@ -128,7 +130,7 @@
* Inserts the specified element into this delay queue.
*
* @param e the element to add
- * @return <tt>true</tt>
+ * @return {@code true}
* @throws NullPointerException if the specified element is null
*/
public boolean offer(E e) {
@@ -164,7 +166,7 @@
* @param e the element to add
* @param timeout This parameter is ignored as the method never blocks
* @param unit This parameter is ignored as the method never blocks
- * @return <tt>true</tt>
+ * @return {@code true}
* @throws NullPointerException {@inheritDoc}
*/
public boolean offer(E e, long timeout, TimeUnit unit) {
@@ -172,10 +174,10 @@
}
/**
- * Retrieves and removes the head of this queue, or returns <tt>null</tt>
+ * Retrieves and removes the head of this queue, or returns {@code null}
* if this queue has no elements with an expired delay.
*
- * @return the head of this queue, or <tt>null</tt> if this
+ * @return the head of this queue, or {@code null} if this
* queue has no elements with an expired delay
*/
public E poll() {
@@ -183,7 +185,7 @@
lock.lock();
try {
E first = q.peek();
- if (first == null || first.getDelay(TimeUnit.NANOSECONDS) > 0)
+ if (first == null || first.getDelay(NANOSECONDS) > 0)
return null;
else
return q.poll();
@@ -208,10 +210,11 @@
if (first == null)
available.await();
else {
- long delay = first.getDelay(TimeUnit.NANOSECONDS);
+ long delay = first.getDelay(NANOSECONDS);
if (delay <= 0)
return q.poll();
- else if (leader != null)
+ first = null; // don't retain ref while waiting
+ if (leader != null)
available.await();
else {
Thread thisThread = Thread.currentThread();
@@ -237,7 +240,7 @@
* until an element with an expired delay is available on this queue,
* or the specified wait time expires.
*
- * @return the head of this queue, or <tt>null</tt> if the
+ * @return the head of this queue, or {@code null} if the
* specified waiting time elapses before an element with
* an expired delay becomes available
* @throws InterruptedException {@inheritDoc}
@@ -255,11 +258,12 @@
else
nanos = available.awaitNanos(nanos);
} else {
- long delay = first.getDelay(TimeUnit.NANOSECONDS);
+ long delay = first.getDelay(NANOSECONDS);
if (delay <= 0)
return q.poll();
if (nanos <= 0)
return null;
+ first = null; // don't retain ref while waiting
if (nanos < delay || leader != null)
nanos = available.awaitNanos(nanos);
else {
@@ -284,13 +288,13 @@
/**
* Retrieves, but does not remove, the head of this queue, or
- * returns <tt>null</tt> if this queue is empty. Unlike
- * <tt>poll</tt>, if no expired elements are available in the queue,
+ * returns {@code null} if this queue is empty. Unlike
+ * {@code poll}, if no expired elements are available in the queue,
* this method returns the element that will expire next,
* if one exists.
*
- * @return the head of this queue, or <tt>null</tt> if this
- * queue is empty.
+ * @return the head of this queue, or {@code null} if this
+ * queue is empty
*/
public E peek() {
final ReentrantLock lock = this.lock;
@@ -313,6 +317,17 @@
}
/**
+ * Returns first element only if it is expired.
+ * Used only by drainTo. Call only when holding lock.
+ */
+ private E peekExpired() {
+ // assert lock.isHeldByCurrentThread();
+ E first = q.peek();
+ return (first == null || first.getDelay(NANOSECONDS) > 0) ?
+ null : first;
+ }
+
+ /**
* @throws UnsupportedOperationException {@inheritDoc}
* @throws ClassCastException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
@@ -327,11 +342,9 @@
lock.lock();
try {
int n = 0;
- for (;;) {
- E first = q.peek();
- if (first == null || first.getDelay(TimeUnit.NANOSECONDS) > 0)
- break;
- c.add(q.poll());
+ for (E e; (e = peekExpired()) != null;) {
+ c.add(e); // In this order, in case add() throws.
+ q.poll();
++n;
}
return n;
@@ -357,11 +370,9 @@
lock.lock();
try {
int n = 0;
- while (n < maxElements) {
- E first = q.peek();
- if (first == null || first.getDelay(TimeUnit.NANOSECONDS) > 0)
- break;
- c.add(q.poll());
+ for (E e; n < maxElements && (e = peekExpired()) != null;) {
+ c.add(e); // In this order, in case add() throws.
+ q.poll();
++n;
}
return n;
@@ -387,10 +398,10 @@
}
/**
- * Always returns <tt>Integer.MAX_VALUE</tt> because
- * a <tt>DelayQueue</tt> is not capacity constrained.
+ * Always returns {@code Integer.MAX_VALUE} because
+ * a {@code DelayQueue} is not capacity constrained.
*
- * @return <tt>Integer.MAX_VALUE</tt>
+ * @return {@code Integer.MAX_VALUE}
*/
public int remainingCapacity() {
return Integer.MAX_VALUE;
@@ -430,7 +441,7 @@
* <p>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
- * <tt>null</tt>.
+ * {@code null}.
*
* <p>Like the {@link #toArray()} method, this method acts as bridge between
* array-based and collection-based APIs. Further, this method allows
@@ -438,13 +449,12 @@
* under certain circumstances, be used to save allocation costs.
*
* <p>The following code can be used to dump a delay queue into a newly
- * allocated array of <tt>Delayed</tt>:
+ * allocated array of {@code Delayed}:
*
- * <pre>
- * Delayed[] a = q.toArray(new Delayed[0]);</pre>
+ * <pre> {@code Delayed[] a = q.toArray(new Delayed[0]);}</pre>
*
- * Note that <tt>toArray(new Object[0])</tt> is identical in function to
- * <tt>toArray()</tt>.
+ * 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
@@ -480,6 +490,24 @@
}
/**
+ * Identity-based version for use in Itr.remove
+ */
+ void removeEQ(Object o) {
+ final ReentrantLock lock = this.lock;
+ lock.lock();
+ try {
+ for (Iterator<E> it = q.iterator(); it.hasNext(); ) {
+ if (o == it.next()) {
+ it.remove();
+ break;
+ }
+ }
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
* Returns an iterator over all the elements (both expired and
* unexpired) in this queue. The iterator does not return the
* elements in any particular order.
@@ -502,7 +530,7 @@
*/
private class Itr implements Iterator<E> {
final Object[] array; // Array of all elements
- int cursor; // index of next element to return;
+ int cursor; // index of next element to return
int lastRet; // index of last element, or -1 if no such
Itr(Object[] array) {
@@ -525,21 +553,8 @@
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
- Object x = array[lastRet];
+ removeEQ(array[lastRet]);
lastRet = -1;
- // Traverse underlying queue to find == element,
- // not just a .equals element.
- lock.lock();
- try {
- for (Iterator<E> it = q.iterator(); it.hasNext(); ) {
- if (it.next() == x) {
- it.remove();
- return;
- }
- }
- } finally {
- lock.unlock();
- }
}
}
--- a/jdk/src/share/classes/java/util/concurrent/Delayed.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/java/util/concurrent/Delayed.java Wed Jul 03 14:35:10 2013 -0700
@@ -40,8 +40,8 @@
* acted upon after a given delay.
*
* <p>An implementation of this interface must define a
- * <tt>compareTo</tt> method that provides an ordering consistent with
- * its <tt>getDelay</tt> method.
+ * {@code compareTo} method that provides an ordering consistent with
+ * its {@code getDelay} method.
*
* @since 1.5
* @author Doug Lea
--- a/jdk/src/share/classes/java/util/concurrent/Exchanger.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/java/util/concurrent/Exchanger.java Wed Jul 03 14:35:10 2013 -0700
@@ -35,7 +35,8 @@
*/
package java.util.concurrent;
-import java.util.concurrent.atomic.*;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.LockSupport;
/**
@@ -52,7 +53,7 @@
* 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.
- * <pre>{@code
+ * <pre> {@code
* class FillAndEmpty {
* Exchanger<DataBuffer> exchanger = new Exchanger<DataBuffer>();
* DataBuffer initialEmptyBuffer = ... a made-up type
@@ -88,8 +89,7 @@
* new Thread(new FillingLoop()).start();
* new Thread(new EmptyingLoop()).start();
* }
- * }
- * }</pre>
+ * }}</pre>
*
* <p>Memory consistency effects: For each pair of threads that
* successfully exchange objects via an {@code Exchanger}, actions
@@ -103,486 +103,425 @@
* @param <V> The type of objects that may be exchanged
*/
public class Exchanger<V> {
+
/*
- * Algorithm Description:
+ * Overview: The core algorithm is, for an exchange "slot",
+ * and a participant (caller) with an item:
*
- * The basic idea is to maintain a "slot", which is a reference to
- * a Node containing both an Item to offer and a "hole" waiting to
- * get filled in. If an incoming "occupying" thread sees that the
- * slot is null, it CAS'es (compareAndSets) a Node there and waits
- * for another to invoke exchange. That second "fulfilling" thread
- * sees that the slot is non-null, and so CASes it back to null,
- * also exchanging items by CASing the hole, plus waking up the
- * occupying thread if it is blocked. In each case CAS'es may
- * fail because a slot at first appears non-null but is null upon
- * CAS, or vice-versa. So threads may need to retry these
- * actions.
+ * for (;;) {
+ * if (slot is empty) { // offer
+ * place item in a Node;
+ * if (can CAS slot from empty to node) {
+ * wait for release;
+ * return matching item in node;
+ * }
+ * }
+ * else if (can CAS slot from node to empty) { // release
+ * get the item in node;
+ * set matching item in node;
+ * release waiting thread;
+ * }
+ * // else retry on CAS failure
+ * }
+ *
+ * This is among the simplest forms of a "dual data structure" --
+ * see Scott and Scherer's DISC 04 paper and
+ * http://www.cs.rochester.edu/research/synchronization/pseudocode/duals.html
*
- * This simple approach works great when there are only a few
- * threads using an Exchanger, but performance rapidly
- * deteriorates due to CAS contention on the single slot when
- * there are lots of threads using an exchanger. So instead we use
- * an "arena"; basically a kind of hash table with a dynamically
- * varying number of slots, any one of which can be used by
- * threads performing an exchange. Incoming threads pick slots
- * based on a hash of their Thread ids. If an incoming thread
- * fails to CAS in its chosen slot, it picks an alternative slot
- * instead. And similarly from there. If a thread successfully
- * CASes into a slot but no other thread arrives, it tries
- * another, heading toward the zero slot, which always exists even
- * if the table shrinks. The particular mechanics controlling this
- * are as follows:
+ * This works great in principle. But in practice, like many
+ * algorithms centered on atomic updates to a single location, it
+ * scales horribly when there are more than a few participants
+ * using the same Exchanger. So the implementation instead uses a
+ * form of elimination arena, that spreads out this contention by
+ * arranging that some threads typically use different slots,
+ * while still ensuring that eventually, any two parties will be
+ * able to exchange items. That is, we cannot completely partition
+ * across threads, but instead give threads arena indices that
+ * will on average grow under contention and shrink under lack of
+ * contention. We approach this by defining the Nodes that we need
+ * anyway as ThreadLocals, and include in them per-thread index
+ * and related bookkeeping state. (We can safely reuse per-thread
+ * nodes rather than creating them fresh each time because slots
+ * alternate between pointing to a node vs null, so cannot
+ * encounter ABA problems. However, we do need some care in
+ * resetting them between uses.)
*
- * Waiting: Slot zero is special in that it is the only slot that
- * exists when there is no contention. A thread occupying slot
- * zero will block if no thread fulfills it after a short spin.
- * In other cases, occupying threads eventually give up and try
- * another slot. Waiting threads spin for a while (a period that
- * should be a little less than a typical context-switch time)
- * before either blocking (if slot zero) or giving up (if other
- * slots) and restarting. There is no reason for threads to block
- * unless there are unlikely to be any other threads present.
- * Occupants are mainly avoiding memory contention so sit there
- * quietly polling for a shorter period than it would take to
- * block and then unblock them. Non-slot-zero waits that elapse
- * because of lack of other threads waste around one extra
- * context-switch time per try, which is still on average much
- * faster than alternative approaches.
+ * Implementing an effective arena requires allocating a bunch of
+ * space, so we only do so upon detecting contention (except on
+ * uniprocessors, where they wouldn't help, so aren't used).
+ * Otherwise, exchanges use the single-slot slotExchange method.
+ * On contention, not only must the slots be in different
+ * locations, but the locations must not encounter memory
+ * contention due to being on the same cache line (or more
+ * generally, the same coherence unit). Because, as of this
+ * writing, there is no way to determine cacheline size, we define
+ * a value that is enough for common platforms. Additionally,
+ * extra care elsewhere is taken to avoid other false/unintended
+ * sharing and to enhance locality, including adding padding (via
+ * sun.misc.Contended) to Nodes, embedding "bound" as an Exchanger
+ * field, and reworking some park/unpark mechanics compared to
+ * LockSupport versions.
+ *
+ * The arena starts out with only one used slot. We expand the
+ * effective arena size by tracking collisions; i.e., failed CASes
+ * while trying to exchange. By nature of the above algorithm, the
+ * only kinds of collision that reliably indicate contention are
+ * when two attempted releases collide -- one of two attempted
+ * offers can legitimately fail to CAS without indicating
+ * contention by more than one other thread. (Note: it is possible
+ * but not worthwhile to more precisely detect contention by
+ * reading slot values after CAS failures.) When a thread has
+ * collided at each slot within the current arena bound, it tries
+ * to expand the arena size by one. We track collisions within
+ * bounds by using a version (sequence) number on the "bound"
+ * field, and conservatively reset collision counts when a
+ * participant notices that bound has been updated (in either
+ * direction).
*
- * Sizing: Usually, using only a few slots suffices to reduce
- * contention. Especially with small numbers of threads, using
- * too many slots can lead to just as poor performance as using
- * too few of them, and there's not much room for error. The
- * variable "max" maintains the number of slots actually in
- * use. It is increased when a thread sees too many CAS
- * failures. (This is analogous to resizing a regular hash table
- * based on a target load factor, except here, growth steps are
- * just one-by-one rather than proportional.) Growth requires
- * contention failures in each of three tried slots. Requiring
- * multiple failures for expansion copes with the fact that some
- * failed CASes are not due to contention but instead to simple
- * races between two threads or thread pre-emptions occurring
- * between reading and CASing. Also, very transient peak
- * contention can be much higher than the average sustainable
- * levels. An attempt to decrease the max limit is usually made
- * when a non-slot-zero wait elapses without being fulfilled.
- * Threads experiencing elapsed waits move closer to zero, so
- * eventually find existing (or future) threads even if the table
- * has been shrunk due to inactivity. The chosen mechanics and
- * thresholds for growing and shrinking are intrinsically
- * entangled with indexing and hashing inside the exchange code,
- * and can't be nicely abstracted out.
+ * The effective arena size is reduced (when there is more than
+ * one slot) by giving up on waiting after a while and trying to
+ * decrement the arena size on expiration. The value of "a while"
+ * is an empirical matter. We implement by piggybacking on the
+ * use of spin->yield->block that is essential for reasonable
+ * waiting performance anyway -- in a busy exchanger, offers are
+ * usually almost immediately released, in which case context
+ * switching on multiprocessors is extremely slow/wasteful. Arena
+ * waits just omit the blocking part, and instead cancel. The spin
+ * count is empirically chosen to be a value that avoids blocking
+ * 99% of the time under maximum sustained exchange rates on a
+ * range of test machines. Spins and yields entail some limited
+ * randomness (using a cheap xorshift) to avoid regular patterns
+ * that can induce unproductive grow/shrink cycles. (Using a
+ * pseudorandom also helps regularize spin cycle duration by
+ * making branches unpredictable.) Also, during an offer, a
+ * waiter can "know" that it will be released when its slot has
+ * changed, but cannot yet proceed until match is set. In the
+ * mean time it cannot cancel the offer, so instead spins/yields.
+ * Note: It is possible to avoid this secondary check by changing
+ * the linearization point to be a CAS of the match field (as done
+ * in one case in the Scott & Scherer DISC paper), which also
+ * increases asynchrony a bit, at the expense of poorer collision
+ * detection and inability to always reuse per-thread nodes. So
+ * the current scheme is typically a better tradeoff.
+ *
+ * On collisions, indices traverse the arena cyclically in reverse
+ * order, restarting at the maximum index (which will tend to be
+ * sparsest) when bounds change. (On expirations, indices instead
+ * are halved until reaching 0.) It is possible (and has been
+ * tried) to use randomized, prime-value-stepped, or double-hash
+ * style traversal instead of simple cyclic traversal to reduce
+ * bunching. But empirically, whatever benefits these may have
+ * don't overcome their added overhead: We are managing operations
+ * that occur very quickly unless there is sustained contention,
+ * so simpler/faster control policies work better than more
+ * accurate but slower ones.
+ *
+ * Because we use expiration for arena size control, we cannot
+ * throw TimeoutExceptions in the timed version of the public
+ * exchange method until the arena size has shrunken to zero (or
+ * the arena isn't enabled). This may delay response to timeout
+ * but is still within spec.
*
- * Hashing: Each thread picks its initial slot to use in accord
- * with a simple hashcode. The sequence is the same on each
- * encounter by any given thread, but effectively random across
- * threads. Using arenas encounters the classic cost vs quality
- * tradeoffs of all hash tables. Here, we use a one-step FNV-1a
- * hash code based on the current thread's Thread.getId(), along
- * with a cheap approximation to a mod operation to select an
- * index. The downside of optimizing index selection in this way
- * is that the code is hardwired to use a maximum table size of
- * 32. But this value more than suffices for known platforms and
- * applications.
+ * Essentially all of the implementation is in methods
+ * slotExchange and arenaExchange. These have similar overall
+ * structure, but differ in too many details to combine. The
+ * slotExchange method uses the single Exchanger field "slot"
+ * rather than arena array elements. However, it still needs
+ * minimal collision detection to trigger arena construction.
+ * (The messiest part is making sure interrupt status and
+ * InterruptedExceptions come out right during transitions when
+ * both methods may be called. This is done by using null return
+ * as a sentinel to recheck interrupt status.)
*
- * Probing: On sensed contention of a selected slot, we probe
- * sequentially through the table, analogously to linear probing
- * after collision in a hash table. (We move circularly, in
- * reverse order, to mesh best with table growth and shrinkage
- * rules.) Except that to minimize the effects of false-alarms
- * and cache thrashing, we try the first selected slot twice
- * before moving.
- *
- * Padding: Even with contention management, slots are heavily
- * contended, so use cache-padding to avoid poor memory
- * performance. Because of this, slots are lazily constructed
- * only when used, to avoid wasting this space unnecessarily.
- * While isolation of locations is not much of an issue at first
- * in an application, as time goes on and garbage-collectors
- * perform compaction, slots are very likely to be moved adjacent
- * to each other, which can cause much thrashing of cache lines on
- * MPs unless padding is employed.
- *
- * This is an improvement of the algorithm described in the paper
- * "A Scalable Elimination-based Exchange Channel" by William
- * Scherer, Doug Lea, and Michael Scott in Proceedings of SCOOL05
- * workshop. Available at: http://hdl.handle.net/1802/2104
+ * As is too common in this sort of code, methods are monolithic
+ * because most of the logic relies on reads of fields that are
+ * maintained as local variables so can't be nicely factored --
+ * mainly, here, bulky spin->yield->block/cancel code), and
+ * heavily dependent on intrinsics (Unsafe) to use inlined
+ * embedded CAS and related memory access operations (that tend
+ * not to be as readily inlined by dynamic compilers when they are
+ * hidden behind other methods that would more nicely name and
+ * encapsulate the intended effects). This includes the use of
+ * putOrderedX to clear fields of the per-thread Nodes between
+ * uses. Note that field Node.item is not declared as volatile
+ * even though it is read by releasing threads, because they only
+ * do so after CAS operations that must precede access, and all
+ * uses by the owning thread are otherwise acceptably ordered by
+ * other operations. (Because the actual points of atomicity are
+ * slot CASes, it would also be legal for the write to Node.match
+ * in a release to be weaker than a full volatile write. However,
+ * this is not done because it could allow further postponement of
+ * the write, delaying progress.)
*/
+ /**
+ * The byte distance (as a shift value) between any two used slots
+ * in the arena. 1 << ASHIFT should be at least cacheline size.
+ */
+ private static final int ASHIFT = 7;
+
+ /**
+ * The maximum supported arena index. The maximum allocatable
+ * arena size is MMASK + 1. Must be a power of two minus one, less
+ * than (1<<(31-ASHIFT)). The cap of 255 (0xff) more than suffices
+ * for the expected scaling limits of the main algorithms.
+ */
+ private static final int MMASK = 0xff;
+
+ /**
+ * Unit for sequence/version bits of bound field. Each successful
+ * change to the bound also adds SEQ.
+ */
+ private static final int SEQ = MMASK + 1;
+
/** The number of CPUs, for sizing and spin control */
private static final int NCPU = Runtime.getRuntime().availableProcessors();
/**
- * The capacity of the arena. Set to a value that provides more
- * than enough space to handle contention. On small machines
- * most slots won't be used, but it is still not wasted because
- * the extra space provides some machine-level address padding
- * to minimize interference with heavily CAS'ed Slot locations.
- * And on very large machines, performance eventually becomes
- * bounded by memory bandwidth, not numbers of threads/CPUs.
- * This constant cannot be changed without also modifying
- * indexing and hashing algorithms.
+ * The maximum slot index of the arena: The number of slots that
+ * can in principle hold all threads without contention, or at
+ * most the maximum indexable value.
*/
- private static final int CAPACITY = 32;
-
- /**
- * The value of "max" that will hold all threads without
- * contention. When this value is less than CAPACITY, some
- * otherwise wasted expansion can be avoided.
- */
- private static final int FULL =
- Math.max(0, Math.min(CAPACITY, NCPU / 2) - 1);
+ static final int FULL = (NCPU >= (MMASK << 1)) ? MMASK : NCPU >>> 1;
/**
- * The number of times to spin (doing nothing except polling a
- * memory location) before blocking or giving up while waiting to
- * be fulfilled. Should be zero on uniprocessors. On
- * multiprocessors, this value should be large enough so that two
- * threads exchanging items as fast as possible block only when
- * one of them is stalled (due to GC or preemption), but not much
- * longer, to avoid wasting CPU resources. Seen differently, this
- * value is a little over half the number of cycles of an average
- * context switch time on most systems. The value here is
- * approximately the average of those across a range of tested
- * systems.
+ * The bound for spins while waiting for a match. The actual
+ * number of iterations will on average be about twice this value
+ * due to randomization. Note: Spinning is disabled when NCPU==1.
*/
- private static final int SPINS = (NCPU == 1) ? 0 : 2000;
-
- /**
- * The number of times to spin before blocking in timed waits.
- * Timed waits spin more slowly because checking the time takes
- * time. The best value relies mainly on the relative rate of
- * System.nanoTime vs memory accesses. The value is empirically
- * derived to work well across a variety of systems.
- */
- private static final int TIMED_SPINS = SPINS / 20;
-
- /**
- * Sentinel item representing cancellation of a wait due to
- * interruption, timeout, or elapsed spin-waits. This value is
- * placed in holes on cancellation, and used as a return value
- * from waiting methods to indicate failure to set or get hole.
- */
- private static final Object CANCEL = new Object();
+ private static final int SPINS = 1 << 10;
/**
* Value representing null arguments/returns from public
- * methods. This disambiguates from internal requirement that
- * holes start out as null to mean they are not yet set.
+ * methods. Needed because the API originally didn't disallow null
+ * arguments, which it should have.
*/
private static final Object NULL_ITEM = new Object();
/**
- * Nodes hold partially exchanged data. This class
- * opportunistically subclasses AtomicReference to represent the
- * hole. So get() returns hole, and compareAndSet CAS'es value
- * into hole. This class cannot be parameterized as "V" because
- * of the use of non-V CANCEL sentinels.
+ * Sentinel value returned by internal exchange methods upon
+ * timeout, to avoid need for separate timed versions of these
+ * methods.
*/
- @SuppressWarnings("serial")
- private static final class Node extends AtomicReference<Object> {
- /** The element offered by the Thread creating this node. */
- public final Object item;
+ private static final Object TIMED_OUT = new Object();
- /** The Thread waiting to be signalled; null until waiting. */
- public volatile Thread waiter;
-
- /**
- * Creates node with given item and empty hole.
- * @param item the item
- */
- public Node(Object item) {
- this.item = item;
- }
+ /**
+ * Nodes hold partially exchanged data, plus other per-thread
+ * bookkeeping. Padded via @sun.misc.Contended to reduce memory
+ * contention.
+ */
+ @sun.misc.Contended static final class Node {
+ int index; // Arena index
+ int bound; // Last recorded value of Exchanger.bound
+ int collides; // Number of CAS failures at current bound
+ int hash; // Pseudo-random for spins
+ Object item; // This thread's current item
+ volatile Object match; // Item provided by releasing thread
+ volatile Thread parked; // Set to this thread when parked, else null
}
- /**
- * A Slot is an AtomicReference with heuristic padding to lessen
- * cache effects of this heavily CAS'ed location. While the
- * padding adds noticeable space, all slots are created only on
- * demand, and there will be more than one of them only when it
- * would improve throughput more than enough to outweigh using
- * extra space.
- */
- @SuppressWarnings("serial")
- private static final class Slot extends AtomicReference<Object> {
- // Improve likelihood of isolation on <= 64 byte cache lines
- long q0, q1, q2, q3, q4, q5, q6, q7, q8, q9, qa, qb, qc, qd, qe;
+ /** The corresponding thread local class */
+ static final class Participant extends ThreadLocal<Node> {
+ public Node initialValue() { return new Node(); }
}
/**
- * Slot array. Elements are lazily initialized when needed.
- * Declared volatile to enable double-checked lazy construction.
+ * Per-thread state
*/
- private volatile Slot[] arena = new Slot[CAPACITY];
+ private final Participant participant;
+
+ /**
+ * Elimination array; null until enabled (within slotExchange).
+ * Element accesses use emulation of volatile gets and CAS.
+ */
+ private volatile Node[] arena;
/**
- * The maximum slot index being used. The value sometimes
- * increases when a thread experiences too many CAS contentions,
- * and sometimes decreases when a spin-wait elapses. Changes
- * are performed only via compareAndSet, to avoid stale values
- * when a thread happens to stall right before setting.
+ * Slot used until contention detected.
*/
- private final AtomicInteger max = new AtomicInteger();
+ private volatile Node slot;
/**
- * Main exchange function, handling the different policy variants.
- * Uses Object, not "V" as argument and return value to simplify
- * handling of sentinel values. Callers from public methods decode
- * and cast accordingly.
+ * The index of the largest valid arena position, OR'ed with SEQ
+ * number in high bits, incremented on each update. The initial
+ * update from 0 to SEQ is used to ensure that the arena array is
+ * constructed only once.
+ */
+ private volatile int bound;
+
+ /**
+ * Exchange function when arenas enabled. See above for explanation.
*
* @param item the (non-null) item to exchange
* @param timed true if the wait is timed
- * @param nanos if timed, the maximum wait time
- * @return the other thread's item, or CANCEL if interrupted or timed out
+ * @param ns if timed, the maximum wait time, else 0L
+ * @return the other thread's item; or null if interrupted; or
+ * TIMED_OUT if timed and timed out
*/
- private Object doExchange(Object item, boolean timed, long nanos) {
- Node me = new Node(item); // Create in case occupying
- int index = hashIndex(); // Index of current slot
- int fails = 0; // Number of CAS failures
-
- for (;;) {
- Object y; // Contents of current slot
- Slot slot = arena[index];
- if (slot == null) // Lazily initialize slots
- createSlot(index); // Continue loop to reread
- else if ((y = slot.get()) != null && // Try to fulfill
- slot.compareAndSet(y, null)) {
- Node you = (Node)y; // Transfer item
- if (you.compareAndSet(null, item)) {
- LockSupport.unpark(you.waiter);
- return you.item;
- } // Else cancelled; continue
+ private final Object arenaExchange(Object item, boolean timed, long ns) {
+ Node[] a = arena;
+ Node p = participant.get();
+ for (int i = p.index;;) { // access slot at i
+ int b, m, c; long j; // j is raw array offset
+ Node q = (Node)U.getObjectVolatile(a, j = (i << ASHIFT) + ABASE);
+ if (q != null && U.compareAndSwapObject(a, j, q, null)) {
+ Object v = q.item; // release
+ q.match = item;
+ Thread w = q.parked;
+ if (w != null)
+ U.unpark(w);
+ return v;
}
- else if (y == null && // Try to occupy
- slot.compareAndSet(null, me)) {
- if (index == 0) // Blocking wait for slot 0
- return timed ?
- awaitNanos(me, slot, nanos) :
- await(me, slot);
- Object v = spinWait(me, slot); // Spin wait for non-0
- if (v != CANCEL)
- return v;
- me = new Node(item); // Throw away cancelled node
- int m = max.get();
- if (m > (index >>>= 1)) // Decrease index
- max.compareAndSet(m, m - 1); // Maybe shrink table
+ else if (i <= (m = (b = bound) & MMASK) && q == null) {
+ p.item = item; // offer
+ if (U.compareAndSwapObject(a, j, null, p)) {
+ long end = (timed && m == 0) ? System.nanoTime() + ns : 0L;
+ Thread t = Thread.currentThread(); // wait
+ for (int h = p.hash, spins = SPINS;;) {
+ Object v = p.match;
+ if (v != null) {
+ U.putOrderedObject(p, MATCH, null);
+ p.item = null; // clear for next use
+ p.hash = h;
+ return v;
+ }
+ else if (spins > 0) {
+ h ^= h << 1; h ^= h >>> 3; h ^= h << 10; // xorshift
+ if (h == 0) // initialize hash
+ h = SPINS | (int)t.getId();
+ else if (h < 0 && // approx 50% true
+ (--spins & ((SPINS >>> 1) - 1)) == 0)
+ Thread.yield(); // two yields per wait
+ }
+ else if (U.getObjectVolatile(a, j) != p)
+ spins = SPINS; // releaser hasn't set match yet
+ else if (!t.isInterrupted() && m == 0 &&
+ (!timed ||
+ (ns = end - System.nanoTime()) > 0L)) {
+ U.putObject(t, BLOCKER, this); // emulate LockSupport
+ p.parked = t; // minimize window
+ if (U.getObjectVolatile(a, j) == p)
+ U.park(false, ns);
+ p.parked = null;
+ U.putObject(t, BLOCKER, null);
+ }
+ else if (U.getObjectVolatile(a, j) == p &&
+ U.compareAndSwapObject(a, j, p, null)) {
+ if (m != 0) // try to shrink
+ U.compareAndSwapInt(this, BOUND, b, b + SEQ - 1);
+ p.item = null;
+ p.hash = h;
+ i = p.index >>>= 1; // descend
+ if (Thread.interrupted())
+ return null;
+ if (timed && m == 0 && ns <= 0L)
+ return TIMED_OUT;
+ break; // expired; restart
+ }
+ }
+ }
+ else
+ p.item = null; // clear offer
}
- else if (++fails > 1) { // Allow 2 fails on 1st slot
- int m = max.get();
- if (fails > 3 && m < FULL && max.compareAndSet(m, m + 1))
- index = m + 1; // Grow on 3rd failed slot
- else if (--index < 0)
- index = m; // Circularly traverse
+ else {
+ if (p.bound != b) { // stale; reset
+ p.bound = b;
+ p.collides = 0;
+ i = (i != m || m == 0) ? m : m - 1;
+ }
+ else if ((c = p.collides) < m || m == FULL ||
+ !U.compareAndSwapInt(this, BOUND, b, b + SEQ + 1)) {
+ p.collides = c + 1;
+ i = (i == 0) ? m : i - 1; // cyclically traverse
+ }
+ else
+ i = m + 1; // grow
+ p.index = i;
}
}
}
/**
- * Returns a hash index for the current thread. Uses a one-step
- * FNV-1a hash code (http://www.isthe.com/chongo/tech/comp/fnv/)
- * based on the current thread's Thread.getId(). These hash codes
- * have more uniform distribution properties with respect to small
- * moduli (here 1-31) than do other simple hashing functions.
- *
- * <p>To return an index between 0 and max, we use a cheap
- * approximation to a mod operation, that also corrects for bias
- * due to non-power-of-2 remaindering (see {@link
- * java.util.Random#nextInt}). Bits of the hashcode are masked
- * with "nbits", the ceiling power of two of table size (looked up
- * in a table packed into three ints). If too large, this is
- * retried after rotating the hash by nbits bits, while forcing new
- * top bit to 0, which guarantees eventual termination (although
- * with a non-random-bias). This requires an average of less than
- * 2 tries for all table sizes, and has a maximum 2% difference
- * from perfectly uniform slot probabilities when applied to all
- * possible hash codes for sizes less than 32.
+ * Exchange function used until arenas enabled. See above for explanation.
*
- * @return a per-thread-random index, 0 <= index < max
- */
- private final int hashIndex() {
- long id = Thread.currentThread().getId();
- int hash = (((int)(id ^ (id >>> 32))) ^ 0x811c9dc5) * 0x01000193;
-
- int m = max.get();
- int nbits = (((0xfffffc00 >> m) & 4) | // Compute ceil(log2(m+1))
- ((0x000001f8 >>> m) & 2) | // The constants hold
- ((0xffff00f2 >>> m) & 1)); // a lookup table
- int index;
- while ((index = hash & ((1 << nbits) - 1)) > m) // May retry on
- hash = (hash >>> nbits) | (hash << (33 - nbits)); // non-power-2 m
- return index;
- }
-
- /**
- * Creates a new slot at given index. Called only when the slot
- * appears to be null. Relies on double-check using builtin
- * locks, since they rarely contend. This in turn relies on the
- * arena array being declared volatile.
- *
- * @param index the index to add slot at
+ * @param item the item to exchange
+ * @param timed true if the wait is timed
+ * @param ns if timed, the maximum wait time, else 0L
+ * @return the other thread's item; or null if either the arena
+ * was enabled or the thread was interrupted before completion; or
+ * TIMED_OUT if timed and timed out
*/
- private void createSlot(int index) {
- // Create slot outside of lock to narrow sync region
- Slot newSlot = new Slot();
- Slot[] a = arena;
- synchronized (a) {
- if (a[index] == null)
- a[index] = newSlot;
- }
- }
-
- /**
- * Tries to cancel a wait for the given node waiting in the given
- * slot, if so, helping clear the node from its slot to avoid
- * garbage retention.
- *
- * @param node the waiting node
- * @param the slot it is waiting in
- * @return true if successfully cancelled
- */
- private static boolean tryCancel(Node node, Slot slot) {
- if (!node.compareAndSet(null, CANCEL))
- return false;
- if (slot.get() == node) // pre-check to minimize contention
- slot.compareAndSet(node, null);
- return true;
- }
-
- // Three forms of waiting. Each just different enough not to merge
- // code with others.
-
- /**
- * Spin-waits for hole for a non-0 slot. Fails if spin elapses
- * before hole filled. Does not check interrupt, relying on check
- * in public exchange method to abort if interrupted on entry.
- *
- * @param node the waiting node
- * @return on success, the hole; on failure, CANCEL
- */
- private static Object spinWait(Node node, Slot slot) {
- int spins = SPINS;
- for (;;) {
- Object v = node.get();
- if (v != null)
- return v;
- else if (spins > 0)
- --spins;
- else
- tryCancel(node, slot);
- }
- }
+ private final Object slotExchange(Object item, boolean timed, long ns) {
+ Node p = participant.get();
+ Thread t = Thread.currentThread();
+ if (t.isInterrupted()) // preserve interrupt status so caller can recheck
+ return null;
- /**
- * Waits for (by spinning and/or blocking) and gets the hole
- * filled in by another thread. Fails if interrupted before
- * hole filled.
- *
- * When a node/thread is about to block, it sets its waiter field
- * and then rechecks state at least one more time before actually
- * parking, thus covering race vs fulfiller noticing that waiter
- * is non-null so should be woken.
- *
- * Thread interruption status is checked only surrounding calls to
- * park. The caller is assumed to have checked interrupt status
- * on entry.
- *
- * @param node the waiting node
- * @return on success, the hole; on failure, CANCEL
- */
- private static Object await(Node node, Slot slot) {
- Thread w = Thread.currentThread();
- int spins = SPINS;
- for (;;) {
- Object v = node.get();
- if (v != null)
- return v;
- else if (spins > 0) // Spin-wait phase
- --spins;
- else if (node.waiter == null) // Set up to block next
- node.waiter = w;
- else if (w.isInterrupted()) // Abort on interrupt
- tryCancel(node, slot);
- else // Block
- LockSupport.park(node);
- }
- }
-
- /**
- * Waits for (at index 0) and gets the hole filled in by another
- * thread. Fails if timed out or interrupted before hole filled.
- * Same basic logic as untimed version, but a bit messier.
- *
- * @param node the waiting node
- * @param nanos the wait time
- * @return on success, the hole; on failure, CANCEL
- */
- private Object awaitNanos(Node node, Slot slot, long nanos) {
- int spins = TIMED_SPINS;
- long lastTime = 0;
- Thread w = null;
- for (;;) {
- Object v = node.get();
- if (v != null)
- return v;
- long now = System.nanoTime();
- if (w == null)
- w = Thread.currentThread();
- else
- nanos -= now - lastTime;
- lastTime = now;
- if (nanos > 0) {
- if (spins > 0)
- --spins;
- else if (node.waiter == null)
- node.waiter = w;
- else if (w.isInterrupted())
- tryCancel(node, slot);
- else
- LockSupport.parkNanos(node, nanos);
+ for (Node q;;) {
+ if ((q = slot) != null) {
+ if (U.compareAndSwapObject(this, SLOT, q, null)) {
+ Object v = q.item;
+ q.match = item;
+ Thread w = q.parked;
+ if (w != null)
+ U.unpark(w);
+ return v;
+ }
+ // create arena on contention, but continue until slot null
+ if (NCPU > 1 && bound == 0 &&
+ U.compareAndSwapInt(this, BOUND, 0, SEQ))
+ arena = new Node[(FULL + 2) << ASHIFT];
}
- else if (tryCancel(node, slot) && !w.isInterrupted())
- return scanOnTimeout(node);
- }
- }
-
- /**
- * Sweeps through arena checking for any waiting threads. Called
- * only upon return from timeout while waiting in slot 0. When a
- * thread gives up on a timed wait, it is possible that a
- * previously-entered thread is still waiting in some other
- * slot. So we scan to check for any. This is almost always
- * overkill, but decreases the likelihood of timeouts when there
- * are other threads present to far less than that in lock-based
- * exchangers in which earlier-arriving threads may still be
- * waiting on entry locks.
- *
- * @param node the waiting node
- * @return another thread's item, or CANCEL
- */
- private Object scanOnTimeout(Node node) {
- Object y;
- for (int j = arena.length - 1; j >= 0; --j) {
- Slot slot = arena[j];
- if (slot != null) {
- while ((y = slot.get()) != null) {
- if (slot.compareAndSet(y, null)) {
- Node you = (Node)y;
- if (you.compareAndSet(null, node.item)) {
- LockSupport.unpark(you.waiter);
- return you.item;
- }
- }
- }
+ else if (arena != null)
+ return null; // caller must reroute to arenaExchange
+ else {
+ p.item = item;
+ if (U.compareAndSwapObject(this, SLOT, null, p))
+ break;
+ p.item = null;
}
}
- return CANCEL;
+
+ // await release
+ int h = p.hash;
+ long end = timed ? System.nanoTime() + ns : 0L;
+ int spins = (NCPU > 1) ? SPINS : 1;
+ Object v;
+ while ((v = p.match) == null) {
+ if (spins > 0) {
+ h ^= h << 1; h ^= h >>> 3; h ^= h << 10;
+ if (h == 0)
+ h = SPINS | (int)t.getId();
+ else if (h < 0 && (--spins & ((SPINS >>> 1) - 1)) == 0)
+ Thread.yield();
+ }
+ else if (slot != p)
+ spins = SPINS;
+ else if (!t.isInterrupted() && arena == null &&
+ (!timed || (ns = end - System.nanoTime()) > 0L)) {
+ U.putObject(t, BLOCKER, this);
+ p.parked = t;
+ if (slot == p)
+ U.park(false, ns);
+ p.parked = null;
+ U.putObject(t, BLOCKER, null);
+ }
+ else if (U.compareAndSwapObject(this, SLOT, p, null)) {
+ v = timed && ns <= 0L && !t.isInterrupted() ? TIMED_OUT : null;
+ break;
+ }
+ }
+ U.putOrderedObject(p, MATCH, null);
+ p.item = null;
+ p.hash = h;
+ return v;
}
/**
* Creates a new Exchanger.
*/
public Exchanger() {
+ participant = new Participant();
}
/**
@@ -620,15 +559,14 @@
*/
@SuppressWarnings("unchecked")
public V exchange(V x) throws InterruptedException {
- if (!Thread.interrupted()) {
- Object o = doExchange((x == null) ? NULL_ITEM : x, false, 0);
- if (o == NULL_ITEM)
- return null;
- if (o != CANCEL)
- return (V)o;
- Thread.interrupted(); // Clear interrupt status on IE throw
- }
- throw new InterruptedException();
+ Object v;
+ Object item = (x == null) ? NULL_ITEM : x; // translate null args
+ if ((arena != null ||
+ (v = slotExchange(item, false, 0L)) == null) &&
+ ((Thread.interrupted() || // disambiguates null return
+ (v = arenaExchange(item, false, 0L)) == null)))
+ throw new InterruptedException();
+ return (v == NULL_ITEM) ? null : (V)v;
}
/**
@@ -666,7 +604,7 @@
*
* @param x the object to exchange
* @param timeout the maximum time to wait
- * @param unit the time unit of the <tt>timeout</tt> argument
+ * @param unit the time unit of the {@code timeout} argument
* @return the object provided by the other thread
* @throws InterruptedException if the current thread was
* interrupted while waiting
@@ -676,16 +614,51 @@
@SuppressWarnings("unchecked")
public V exchange(V x, long timeout, TimeUnit unit)
throws InterruptedException, TimeoutException {
- if (!Thread.interrupted()) {
- Object o = doExchange((x == null) ? NULL_ITEM : x,
- true, unit.toNanos(timeout));
- if (o == NULL_ITEM)
- return null;
- if (o != CANCEL)
- return (V)o;
- if (!Thread.interrupted())
- throw new TimeoutException();
+ Object v;
+ Object item = (x == null) ? NULL_ITEM : x;
+ long ns = unit.toNanos(timeout);
+ if ((arena != null ||
+ (v = slotExchange(item, true, ns)) == null) &&
+ ((Thread.interrupted() ||
+ (v = arenaExchange(item, true, ns)) == null)))
+ throw new InterruptedException();
+ if (v == TIMED_OUT)
+ throw new TimeoutException();
+ return (v == NULL_ITEM) ? null : (V)v;
+ }
+
+ // Unsafe mechanics
+ private static final sun.misc.Unsafe U;
+ 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"));
+ SLOT = U.objectFieldOffset
+ (ek.getDeclaredField("slot"));
+ MATCH = U.objectFieldOffset
+ (nk.getDeclaredField("match"));
+ BLOCKER = U.objectFieldOffset
+ (tk.getDeclaredField("parkBlocker"));
+ s = U.arrayIndexScale(ak);
+ // ABASE absorbs padding in front of element 0
+ ABASE = U.arrayBaseOffset(ak) + (1 << ASHIFT);
+
+ } catch (Exception e) {
+ throw new Error(e);
}
- throw new InterruptedException();
+ if ((s & (s-1)) != 0 || s > (1 << ASHIFT))
+ throw new Error("Unsupported array scale");
}
+
}
--- a/jdk/src/share/classes/java/util/concurrent/LinkedBlockingDeque.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/java/util/concurrent/LinkedBlockingDeque.java Wed Jul 03 14:35:10 2013 -0700
@@ -41,12 +41,15 @@
import java.util.NoSuchElementException;
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;
/**
* An optionally-bounded {@linkplain BlockingDeque blocking deque} based on
* linked nodes.
*
- * <p> The optional capacity bound constructor argument serves as a
+ * <p>The optional capacity bound constructor argument serves as a
* way to prevent excessive expansion. The capacity, if unspecified,
* is equal to {@link Integer#MAX_VALUE}. Linked nodes are
* dynamically created upon each insertion unless this would bring the
@@ -315,8 +318,8 @@
// BlockingDeque methods
/**
- * @throws IllegalStateException {@inheritDoc}
- * @throws NullPointerException {@inheritDoc}
+ * @throws IllegalStateException if this deque is full
+ * @throws NullPointerException {@inheritDoc}
*/
public void addFirst(E e) {
if (!offerFirst(e))
@@ -324,7 +327,7 @@
}
/**
- * @throws IllegalStateException {@inheritDoc}
+ * @throws IllegalStateException if this deque is full
* @throws NullPointerException {@inheritDoc}
*/
public void addLast(E e) {
@@ -623,8 +626,7 @@
*
* <p>This method is equivalent to {@link #addLast}.
*
- * @throws IllegalStateException if the element cannot be added at this
- * time due to capacity restrictions
+ * @throws IllegalStateException if this deque is full
* @throws NullPointerException if the specified element is null
*/
public boolean add(E e) {
@@ -761,8 +763,8 @@
// Stack methods
/**
- * @throws IllegalStateException {@inheritDoc}
- * @throws NullPointerException {@inheritDoc}
+ * @throws IllegalStateException if this deque is full
+ * @throws NullPointerException {@inheritDoc}
*/
public void push(E e) {
addFirst(e);
@@ -852,7 +854,7 @@
// * @throws ClassCastException {@inheritDoc}
// * @throws NullPointerException {@inheritDoc}
// * @throws IllegalArgumentException {@inheritDoc}
-// * @throws IllegalStateException {@inheritDoc}
+// * @throws IllegalStateException if this deque is full
// * @see #add(Object)
// */
// public boolean addAll(Collection<? extends E> c) {
@@ -1151,6 +1153,127 @@
Node<E> nextNode(Node<E> n) { return n.prev; }
}
+ /** A customized variant of Spliterators.IteratorSpliterator */
+ static final class LBDSpliterator<E> implements Spliterator<E> {
+ static final int MAX_BATCH = 1 << 25; // max batch array size;
+ final LinkedBlockingDeque<E> queue;
+ Node<E> current; // current node; null until initialized
+ int batch; // batch size for splits
+ boolean exhausted; // true when no more nodes
+ long est; // size estimate
+ LBDSpliterator(LinkedBlockingDeque<E> queue) {
+ this.queue = queue;
+ this.est = queue.size();
+ }
+
+ public long estimateSize() { return est; }
+
+ public Spliterator<E> trySplit() {
+ Node<E> h;
+ final LinkedBlockingDeque<E> q = this.queue;
+ int b = batch;
+ int n = (b <= 0) ? 1 : (b >= MAX_BATCH) ? MAX_BATCH : b + 1;
+ if (!exhausted &&
+ ((h = current) != null || (h = q.first) != null) &&
+ h.next != null) {
+ Object[] a = new Object[n];
+ final ReentrantLock lock = q.lock;
+ int i = 0;
+ Node<E> p = current;
+ lock.lock();
+ try {
+ if (p != null || (p = q.first) != null) {
+ do {
+ if ((a[i] = p.item) != null)
+ ++i;
+ } while ((p = p.next) != null && i < n);
+ }
+ } finally {
+ lock.unlock();
+ }
+ if ((current = p) == null) {
+ est = 0L;
+ exhausted = true;
+ }
+ else if ((est -= i) < 0L)
+ est = 0L;
+ if (i > 0) {
+ batch = i;
+ return Spliterators.spliterator
+ (a, 0, i, Spliterator.ORDERED | Spliterator.NONNULL |
+ Spliterator.CONCURRENT);
+ }
+ }
+ return null;
+ }
+
+ public void forEachRemaining(Consumer<? super E> action) {
+ if (action == null) throw new NullPointerException();
+ final LinkedBlockingDeque<E> q = this.queue;
+ final ReentrantLock lock = q.lock;
+ if (!exhausted) {
+ exhausted = true;
+ Node<E> p = current;
+ do {
+ E e = null;
+ lock.lock();
+ try {
+ if (p == null)
+ p = q.first;
+ while (p != null) {
+ e = p.item;
+ p = p.next;
+ if (e != null)
+ break;
+ }
+ } finally {
+ lock.unlock();
+ }
+ if (e != null)
+ action.accept(e);
+ } while (p != null);
+ }
+ }
+
+ public boolean tryAdvance(Consumer<? super E> action) {
+ if (action == null) throw new NullPointerException();
+ final LinkedBlockingDeque<E> q = this.queue;
+ final ReentrantLock lock = q.lock;
+ if (!exhausted) {
+ E e = null;
+ lock.lock();
+ try {
+ if (current == null)
+ current = q.first;
+ while (current != null) {
+ e = current.item;
+ current = current.next;
+ if (e != null)
+ break;
+ }
+ } finally {
+ lock.unlock();
+ }
+ if (current == null)
+ exhausted = true;
+ if (e != null) {
+ action.accept(e);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public int characteristics() {
+ return Spliterator.ORDERED | Spliterator.NONNULL |
+ Spliterator.CONCURRENT;
+ }
+ }
+
+ public Spliterator<E> spliterator() {
+ return new LBDSpliterator<E>(this);
+ }
+
/**
* Saves this deque to a stream (that is, serializes it).
*
--- a/jdk/src/share/classes/java/util/concurrent/LinkedBlockingQueue.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/java/util/concurrent/LinkedBlockingQueue.java Wed Jul 03 14:35:10 2013 -0700
@@ -42,6 +42,9 @@
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import java.util.function.Consumer;
/**
* An optionally-bounded {@linkplain BlockingQueue blocking queue} based on
@@ -56,7 +59,7 @@
* Linked queues typically have higher throughput than array-based queues but
* less predictable performance in most concurrent applications.
*
- * <p> The optional capacity bound constructor argument serves as a
+ * <p>The optional capacity bound constructor argument serves as a
* way to prevent excessive queue expansion. The capacity, if unspecified,
* is equal to {@link Integer#MAX_VALUE}. Linked nodes are
* dynamically created upon each insertion unless this would bring the
@@ -216,7 +219,7 @@
}
/**
- * Lock to prevent both puts and takes.
+ * Locks to prevent both puts and takes.
*/
void fullyLock() {
putLock.lock();
@@ -224,7 +227,7 @@
}
/**
- * Unlock to allow both puts and takes.
+ * Unlocks to allow both puts and takes.
*/
void fullyUnlock() {
takeLock.unlock();
@@ -362,7 +365,7 @@
* necessary up to the specified wait time for space to become available.
*
* @return {@code true} if successful, or {@code false} if
- * the specified waiting time elapses before space is available.
+ * the specified waiting time elapses before space is available
* @throws InterruptedException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
@@ -782,6 +785,7 @@
* item to hand out so that if hasNext() reports true, we will
* still have it to return even if lost race with a take etc.
*/
+
private Node<E> current;
private Node<E> lastRet;
private E currentElement;
@@ -855,6 +859,124 @@
}
}
+ /** A customized variant of Spliterators.IteratorSpliterator */
+ static final class LBQSpliterator<E> implements Spliterator<E> {
+ static final int MAX_BATCH = 1 << 25; // max batch array size;
+ final LinkedBlockingQueue<E> queue;
+ Node<E> current; // current node; null until initialized
+ int batch; // batch size for splits
+ boolean exhausted; // true when no more nodes
+ long est; // size estimate
+ LBQSpliterator(LinkedBlockingQueue<E> queue) {
+ this.queue = queue;
+ this.est = queue.size();
+ }
+
+ public long estimateSize() { return est; }
+
+ public Spliterator<E> trySplit() {
+ Node<E> h;
+ final LinkedBlockingQueue<E> q = this.queue;
+ int b = batch;
+ int n = (b <= 0) ? 1 : (b >= MAX_BATCH) ? MAX_BATCH : b + 1;
+ if (!exhausted &&
+ ((h = current) != null || (h = q.head.next) != null) &&
+ h.next != null) {
+ Object[] a = new Object[n];
+ int i = 0;
+ Node<E> p = current;
+ q.fullyLock();
+ try {
+ if (p != null || (p = q.head.next) != null) {
+ do {
+ if ((a[i] = p.item) != null)
+ ++i;
+ } while ((p = p.next) != null && i < n);
+ }
+ } finally {
+ q.fullyUnlock();
+ }
+ if ((current = p) == null) {
+ est = 0L;
+ exhausted = true;
+ }
+ else if ((est -= i) < 0L)
+ est = 0L;
+ if (i > 0) {
+ batch = i;
+ return Spliterators.spliterator
+ (a, 0, i, Spliterator.ORDERED | Spliterator.NONNULL |
+ Spliterator.CONCURRENT);
+ }
+ }
+ return null;
+ }
+
+ public void forEachRemaining(Consumer<? super E> action) {
+ if (action == null) throw new NullPointerException();
+ final LinkedBlockingQueue<E> q = this.queue;
+ if (!exhausted) {
+ exhausted = true;
+ Node<E> p = current;
+ do {
+ E e = null;
+ q.fullyLock();
+ try {
+ if (p == null)
+ p = q.head.next;
+ while (p != null) {
+ e = p.item;
+ p = p.next;
+ if (e != null)
+ break;
+ }
+ } finally {
+ q.fullyUnlock();
+ }
+ if (e != null)
+ action.accept(e);
+ } while (p != null);
+ }
+ }
+
+ public boolean tryAdvance(Consumer<? super E> action) {
+ if (action == null) throw new NullPointerException();
+ final LinkedBlockingQueue<E> q = this.queue;
+ if (!exhausted) {
+ E e = null;
+ q.fullyLock();
+ try {
+ if (current == null)
+ current = q.head.next;
+ while (current != null) {
+ e = current.item;
+ current = current.next;
+ if (e != null)
+ break;
+ }
+ } finally {
+ q.fullyUnlock();
+ }
+ if (current == null)
+ exhausted = true;
+ if (e != null) {
+ action.accept(e);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public int characteristics() {
+ return Spliterator.ORDERED | Spliterator.NONNULL |
+ Spliterator.CONCURRENT;
+ }
+ }
+
+ public Spliterator<E> spliterator() {
+ return new LBQSpliterator<E>(this);
+ }
+
/**
* Saves this queue to a stream (that is, serializes it).
*
--- a/jdk/src/share/classes/java/util/concurrent/LinkedTransferQueue.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/java/util/concurrent/LinkedTransferQueue.java Wed Jul 03 14:35:10 2013 -0700
@@ -40,8 +40,10 @@
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.function.Consumer;
/**
* An unbounded {@link TransferQueue} based on linked nodes.
@@ -777,6 +779,24 @@
}
/**
+ * Version of firstOfMode used by Spliterator
+ */
+ final Node firstDataNode() {
+ 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))
+ p = head;
+ }
+ return null;
+ }
+
+ /**
* Returns the item in the first unmatched node with isData; or
* null if none. Used by peek.
*/
@@ -910,6 +930,98 @@
}
}
+ /** A customized variant of Spliterators.IteratorSpliterator */
+ static final class LTQSpliterator<E> implements Spliterator<E> {
+ static final int MAX_BATCH = 1 << 25; // max batch array size;
+ final LinkedTransferQueue<E> queue;
+ Node current; // current node; null until initialized
+ int batch; // batch size for splits
+ boolean exhausted; // true when no more nodes
+ LTQSpliterator(LinkedTransferQueue<E> queue) {
+ this.queue = queue;
+ }
+
+ public Spliterator<E> trySplit() {
+ Node p;
+ final LinkedTransferQueue<E> 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.next != null) {
+ Object[] a = new Object[n];
+ int i = 0;
+ do {
+ if ((a[i] = p.item) != null)
+ ++i;
+ if (p == (p = p.next))
+ p = q.firstDataNode();
+ } while (p != null && i < n);
+ if ((current = p) == null)
+ exhausted = true;
+ if (i > 0) {
+ batch = i;
+ return Spliterators.spliterator
+ (a, 0, i, Spliterator.ORDERED | Spliterator.NONNULL |
+ Spliterator.CONCURRENT);
+ }
+ }
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ public void forEachRemaining(Consumer<? super E> action) {
+ Node p;
+ if (action == null) throw new NullPointerException();
+ final LinkedTransferQueue<E> q = this.queue;
+ if (!exhausted &&
+ ((p = current) != null || (p = q.firstDataNode()) != null)) {
+ exhausted = true;
+ do {
+ Object e = p.item;
+ if (p == (p = p.next))
+ p = q.firstDataNode();
+ if (e != null)
+ action.accept((E)e);
+ } while (p != null);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public boolean tryAdvance(Consumer<? super E> action) {
+ Node p;
+ if (action == null) throw new NullPointerException();
+ final LinkedTransferQueue<E> q = this.queue;
+ if (!exhausted &&
+ ((p = current) != null || (p = q.firstDataNode()) != null)) {
+ Object e;
+ do {
+ e = p.item;
+ if (p == (p = p.next))
+ p = q.firstDataNode();
+ } while (e == null && p != null);
+ if ((current = p) == null)
+ exhausted = true;
+ if (e != null) {
+ action.accept((E)e);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public long estimateSize() { return Long.MAX_VALUE; }
+
+ public int characteristics() {
+ return Spliterator.ORDERED | Spliterator.NONNULL |
+ Spliterator.CONCURRENT;
+ }
+ }
+
+ public Spliterator<E> spliterator() {
+ return new LTQSpliterator<E>(this);
+ }
+
/* -------------- Removal methods -------------- */
/**
--- a/jdk/src/share/classes/java/util/concurrent/Phaser.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/java/util/concurrent/Phaser.java Wed Jul 03 14:35:10 2013 -0700
@@ -46,7 +46,7 @@
* {@link java.util.concurrent.CountDownLatch CountDownLatch}
* but supporting more flexible usage.
*
- * <p> <b>Registration.</b> Unlike the case for other barriers, the
+ * <p><b>Registration.</b> Unlike the case for other barriers, the
* number of parties <em>registered</em> to synchronize on a phaser
* may vary over time. Tasks may be registered at any time (using
* methods {@link #register}, {@link #bulkRegister}, or forms of
@@ -59,7 +59,7 @@
* (However, you can introduce such bookkeeping by subclassing this
* class.)
*
- * <p> <b>Synchronization.</b> Like a {@code CyclicBarrier}, a {@code
+ * <p><b>Synchronization.</b> Like a {@code CyclicBarrier}, a {@code
* Phaser} may be repeatedly awaited. Method {@link
* #arriveAndAwaitAdvance} has effect analogous to {@link
* java.util.concurrent.CyclicBarrier#await CyclicBarrier.await}. Each
@@ -103,7 +103,7 @@
*
* </ul>
*
- * <p> <b>Termination.</b> A phaser may enter a <em>termination</em>
+ * <p><b>Termination.</b> A phaser may enter a <em>termination</em>
* state, that may be checked using method {@link #isTerminated}. Upon
* termination, all synchronization methods immediately return without
* waiting for advance, as indicated by a negative return value.
@@ -118,7 +118,7 @@
* also available to abruptly release waiting threads and allow them
* to terminate.
*
- * <p> <b>Tiering.</b> Phasers may be <em>tiered</em> (i.e.,
+ * <p><b>Tiering.</b> Phasers may be <em>tiered</em> (i.e.,
* constructed in tree structures) to reduce contention. Phasers with
* large numbers of parties that would otherwise experience heavy
* synchronization contention costs may instead be set up so that
@@ -300,18 +300,20 @@
private static final int PHASE_SHIFT = 32;
private static final int UNARRIVED_MASK = 0xffff; // to mask ints
private static final long PARTIES_MASK = 0xffff0000L; // to mask longs
+ private static final long COUNTS_MASK = 0xffffffffL;
private static final long TERMINATION_BIT = 1L << 63;
// some special values
private static final int ONE_ARRIVAL = 1;
private static final int ONE_PARTY = 1 << PARTIES_SHIFT;
+ private static final int ONE_DEREGISTER = ONE_ARRIVAL|ONE_PARTY;
private static final int EMPTY = 1;
// The following unpacking methods are usually manually inlined
private static int unarrivedOf(long s) {
int counts = (int)s;
- return (counts == EMPTY) ? 0 : counts & UNARRIVED_MASK;
+ return (counts == EMPTY) ? 0 : (counts & UNARRIVED_MASK);
}
private static int partiesOf(long s) {
@@ -372,37 +374,44 @@
* Manually tuned to speed up and minimize race windows for the
* common case of just decrementing unarrived field.
*
- * @param deregister false for arrive, true for arriveAndDeregister
+ * @param adjust value to subtract from state;
+ * ONE_ARRIVAL for arrive,
+ * ONE_DEREGISTER for arriveAndDeregister
*/
- private int doArrive(boolean deregister) {
- int adj = deregister ? ONE_ARRIVAL|ONE_PARTY : ONE_ARRIVAL;
+ private int doArrive(int adjust) {
final Phaser root = this.root;
for (;;) {
long s = (root == this) ? state : reconcileState();
int phase = (int)(s >>> PHASE_SHIFT);
- int counts = (int)s;
- int unarrived = (counts & UNARRIVED_MASK) - 1;
if (phase < 0)
return phase;
- else if (counts == EMPTY || unarrived < 0) {
- if (root == this || reconcileState() == s)
- throw new IllegalStateException(badArrive(s));
- }
- else if (UNSAFE.compareAndSwapLong(this, stateOffset, s, s-=adj)) {
- if (unarrived == 0) {
+ int counts = (int)s;
+ 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 (unarrived == 1) {
long n = s & PARTIES_MASK; // base of next state
int nextUnarrived = (int)n >>> PARTIES_SHIFT;
- if (root != this)
- return parent.doArrive(nextUnarrived == 0);
- if (onAdvance(phase, nextUnarrived))
- n |= TERMINATION_BIT;
- else if (nextUnarrived == 0)
- n |= EMPTY;
+ if (root == this) {
+ if (onAdvance(phase, nextUnarrived))
+ n |= TERMINATION_BIT;
+ else if (nextUnarrived == 0)
+ n |= EMPTY;
+ else
+ n |= nextUnarrived;
+ int nextPhase = (phase + 1) & MAX_PHASE;
+ n |= (long)nextPhase << PHASE_SHIFT;
+ UNSAFE.compareAndSwapLong(this, stateOffset, s, n);
+ releaseWaiters(phase);
+ }
+ else if (nextUnarrived == 0) { // propagate deregistration
+ phase = parent.doArrive(ONE_DEREGISTER);
+ UNSAFE.compareAndSwapLong(this, stateOffset,
+ s, s | EMPTY);
+ }
else
- n |= nextUnarrived;
- n |= (long)((phase + 1) & MAX_PHASE) << PHASE_SHIFT;
- UNSAFE.compareAndSwapLong(this, stateOffset, s, n);
- releaseWaiters(phase);
+ phase = parent.doArrive(ONE_ARRIVAL);
}
return phase;
}
@@ -417,42 +426,49 @@
*/
private int doRegister(int registrations) {
// adjustment to state
- long adj = ((long)registrations << PARTIES_SHIFT) | registrations;
+ long adjust = ((long)registrations << PARTIES_SHIFT) | registrations;
final Phaser parent = this.parent;
int phase;
for (;;) {
- long s = state;
+ long s = (parent == null) ? state : reconcileState();
int counts = (int)s;
int parties = counts >>> PARTIES_SHIFT;
int unarrived = counts & UNARRIVED_MASK;
if (registrations > MAX_PARTIES - parties)
throw new IllegalStateException(badRegister(s));
- else if ((phase = (int)(s >>> PHASE_SHIFT)) < 0)
+ phase = (int)(s >>> PHASE_SHIFT);
+ if (phase < 0)
break;
- else if (counts != EMPTY) { // not 1st registration
+ if (counts != EMPTY) { // not 1st registration
if (parent == null || reconcileState() == s) {
if (unarrived == 0) // wait out advance
root.internalAwaitAdvance(phase, null);
else if (UNSAFE.compareAndSwapLong(this, stateOffset,
- s, s + adj))
+ s, s + adjust))
break;
}
}
else if (parent == null) { // 1st root registration
- long next = ((long)phase << PHASE_SHIFT) | adj;
+ long next = ((long)phase << PHASE_SHIFT) | adjust;
if (UNSAFE.compareAndSwapLong(this, stateOffset, s, next))
break;
}
else {
synchronized (this) { // 1st sub registration
if (state == s) { // recheck under lock
- parent.doRegister(1);
- do { // force current phase
+ phase = parent.doRegister(1);
+ if (phase < 0)
+ break;
+ // 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,
+ ((long)phase << PHASE_SHIFT) | adjust)) {
+ s = state;
phase = (int)(root.state >>> PHASE_SHIFT);
- // assert phase < 0 || (int)state == EMPTY;
- } while (!UNSAFE.compareAndSwapLong
- (this, stateOffset, state,
- ((long)phase << PHASE_SHIFT) | adj));
+ // assert (int)s == EMPTY;
+ }
break;
}
}
@@ -467,10 +483,6 @@
* subphasers have not yet done so, in which case they must finish
* their own advance by setting unarrived to parties (or if
* parties is zero, resetting to unregistered EMPTY state).
- * However, this method may also be called when "floating"
- * subphasers with possibly some unarrived parties are merely
- * catching up to current phase, in which case counts are
- * unaffected.
*
* @return reconciled state
*/
@@ -478,16 +490,16 @@
final Phaser root = this.root;
long s = state;
if (root != this) {
- int phase, u, p;
- // CAS root phase with current parties; possibly trip unarrived
+ int phase, p;
+ // 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,
s = (((long)phase << PHASE_SHIFT) |
- (s & PARTIES_MASK) |
- ((p = (int)s >>> PARTIES_SHIFT) == 0 ? EMPTY :
- (u = (int)s & UNARRIVED_MASK) == 0 ? p : u))))
+ ((phase < 0) ? (s & COUNTS_MASK) :
+ (((p = (int)s >>> PARTIES_SHIFT) == 0) ? EMPTY :
+ ((s & PARTIES_MASK) | p))))))
s = state;
}
return s;
@@ -619,7 +631,7 @@
* of unarrived parties would become negative
*/
public int arrive() {
- return doArrive(false);
+ return doArrive(ONE_ARRIVAL);
}
/**
@@ -639,7 +651,7 @@
* of registered or unarrived parties would become negative
*/
public int arriveAndDeregister() {
- return doArrive(true);
+ return doArrive(ONE_DEREGISTER);
}
/**
@@ -666,17 +678,15 @@
for (;;) {
long s = (root == this) ? state : reconcileState();
int phase = (int)(s >>> PHASE_SHIFT);
- int counts = (int)s;
- int unarrived = (counts & UNARRIVED_MASK) - 1;
if (phase < 0)
return phase;
- else if (counts == EMPTY || unarrived < 0) {
- if (reconcileState() == s)
- throw new IllegalStateException(badArrive(s));
- }
- else if (UNSAFE.compareAndSwapLong(this, stateOffset, s,
- s -= ONE_ARRIVAL)) {
- if (unarrived != 0)
+ int counts = (int)s;
+ 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 (unarrived > 1)
return root.internalAwaitAdvance(phase, null);
if (root != this)
return parent.arriveAndAwaitAdvance();
@@ -809,8 +819,8 @@
if (UNSAFE.compareAndSwapLong(root, stateOffset,
s, s | TERMINATION_BIT)) {
// signal all threads
- releaseWaiters(0);
- releaseWaiters(1);
+ releaseWaiters(0); // Waiters on evenQ
+ releaseWaiters(1); // Waiters on oddQ
return;
}
}
@@ -1016,7 +1026,7 @@
/**
* Possibly blocks and waits for phase to advance unless aborted.
- * Call only from root node.
+ * Call only on root phaser.
*
* @param phase current phase
* @param node if non-null, the wait node to track interrupt and timeout;
@@ -1024,6 +1034,7 @@
* @return current phase
*/
private int internalAwaitAdvance(int phase, QNode node) {
+ // assert root == this;
releaseWaiters(phase-1); // ensure old queue clean
boolean queued = false; // true when node is enqueued
int lastUnarrived = 0; // to increase spins upon change
@@ -1082,7 +1093,7 @@
final boolean timed;
boolean wasInterrupted;
long nanos;
- long lastTime;
+ final long deadline;
volatile Thread thread; // nulled to cancel wait
QNode next;
@@ -1093,7 +1104,7 @@
this.interruptible = interruptible;
this.nanos = nanos;
this.timed = timed;
- this.lastTime = timed ? System.nanoTime() : 0L;
+ this.deadline = timed ? System.nanoTime() + nanos : 0L;
thread = Thread.currentThread();
}
@@ -1112,9 +1123,7 @@
}
if (timed) {
if (nanos > 0L) {
- long now = System.nanoTime();
- nanos -= now - lastTime;
- lastTime = now;
+ nanos = deadline - System.nanoTime();
}
if (nanos <= 0L) {
thread = null;
@@ -1129,7 +1138,7 @@
return true;
else if (!timed)
LockSupport.park(this);
- else if (nanos > 0)
+ else if (nanos > 0L)
LockSupport.parkNanos(this, nanos);
return isReleasable();
}
--- a/jdk/src/share/classes/java/util/concurrent/PriorityBlockingQueue.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/java/util/concurrent/PriorityBlockingQueue.java Wed Jul 03 14:35:10 2013 -0700
@@ -37,7 +37,17 @@
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
-import java.util.*;
+import java.util.AbstractQueue;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.PriorityQueue;
+import java.util.Queue;
+import java.util.SortedSet;
+import java.util.Spliterator;
+import java.util.function.Consumer;
/**
* An unbounded {@linkplain BlockingQueue blocking queue} that uses
@@ -342,7 +352,6 @@
* @param k the position to fill
* @param x the item to insert
* @param array the heap array
- * @param n heap size
*/
private static <T> void siftUpComparable(int k, T x, Object[] array) {
Comparable<? super T> key = (Comparable<? super T>) x;
@@ -936,6 +945,70 @@
}
}
+ // Similar to Collections.ArraySnapshotSpliterator but avoids
+ // commitment to toArray until needed
+ static final class PBQSpliterator<E> implements Spliterator<E> {
+ final PriorityBlockingQueue<E> queue;
+ Object[] array;
+ int index;
+ int fence;
+
+ PBQSpliterator(PriorityBlockingQueue<E> queue, Object[] array,
+ int index, int fence) {
+ this.queue = queue;
+ this.array = array;
+ this.index = index;
+ this.fence = fence;
+ }
+
+ final int getFence() {
+ int hi;
+ if ((hi = fence) < 0)
+ hi = fence = (array = queue.toArray()).length;
+ return hi;
+ }
+
+ public Spliterator<E> trySplit() {
+ int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
+ return (lo >= mid) ? null :
+ new PBQSpliterator<E>(queue, array, lo, index = mid);
+ }
+
+ @SuppressWarnings("unchecked")
+ public void forEachRemaining(Consumer<? super E> action) {
+ Object[] a; int i, hi; // hoist accesses and checks from loop
+ if (action == null)
+ throw new NullPointerException();
+ if ((a = array) == null)
+ fence = (a = queue.toArray()).length;
+ if ((hi = fence) <= a.length &&
+ (i = index) >= 0 && i < (index = hi)) {
+ do { action.accept((E)a[i]); } while (++i < hi);
+ }
+ }
+
+ public boolean tryAdvance(Consumer<? super E> action) {
+ if (action == null)
+ throw new NullPointerException();
+ if (getFence() > index && index >= 0) {
+ @SuppressWarnings("unchecked") E e = (E) array[index++];
+ action.accept(e);
+ return true;
+ }
+ return false;
+ }
+
+ public long estimateSize() { return (long)(getFence() - index); }
+
+ public int characteristics() {
+ return Spliterator.NONNULL | Spliterator.SIZED | Spliterator.SUBSIZED;
+ }
+ }
+
+ public Spliterator<E> spliterator() {
+ return new PBQSpliterator<E>(this, null, 0, -1);
+ }
+
// Unsafe mechanics
private static final sun.misc.Unsafe UNSAFE;
private static final long allocationSpinLockOffset;
--- a/jdk/src/share/classes/java/util/concurrent/SynchronousQueue.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/java/util/concurrent/SynchronousQueue.java Wed Jul 03 14:35:10 2013 -0700
@@ -44,17 +44,17 @@
* operation must wait for a corresponding remove operation by another
* thread, and vice versa. A synchronous queue does not have any
* internal capacity, not even a capacity of one. You cannot
- * <tt>peek</tt> at a synchronous queue because an element is only
+ * {@code peek} at a synchronous queue because an element is only
* present when you try to remove it; you cannot insert an element
* (using any method) unless another thread is trying to remove it;
* you cannot iterate as there is nothing to iterate. The
* <em>head</em> of the queue is the element that the first queued
* inserting thread is trying to add to the queue; if there is no such
* queued thread then no element is available for removal and
- * <tt>poll()</tt> will return <tt>null</tt>. For purposes of other
- * <tt>Collection</tt> methods (for example <tt>contains</tt>), a
- * <tt>SynchronousQueue</tt> acts as an empty collection. This queue
- * does not permit <tt>null</tt> elements.
+ * {@code poll()} will return {@code null}. For purposes of other
+ * {@code Collection} methods (for example {@code contains}), a
+ * {@code SynchronousQueue} acts as an empty collection. This queue
+ * does not permit {@code null} elements.
*
* <p>Synchronous queues are similar to rendezvous channels used in
* CSP and Ada. They are well suited for handoff designs, in which an
@@ -62,10 +62,10 @@
* in another thread in order to hand it some information, event, or
* task.
*
- * <p> This class supports an optional fairness policy for ordering
+ * <p>This class supports an optional fairness policy for ordering
* waiting producer and consumer threads. By default, this ordering
* is not guaranteed. However, a queue constructed with fairness set
- * to <tt>true</tt> grants threads access in FIFO order.
+ * to {@code true} grants threads access in FIFO order.
*
* <p>This class and its iterator implement all of the
* <em>optional</em> methods of the {@link Collection} and {@link
@@ -599,7 +599,7 @@
/**
* Reference to a cancelled node that might not yet have been
* unlinked from queue because it was the last inserted node
- * when it cancelled.
+ * when it was cancelled.
*/
transient volatile QNode cleanMe;
@@ -847,14 +847,14 @@
private transient volatile Transferer<E> transferer;
/**
- * Creates a <tt>SynchronousQueue</tt> with nonfair access policy.
+ * Creates a {@code SynchronousQueue} with nonfair access policy.
*/
public SynchronousQueue() {
this(false);
}
/**
- * Creates a <tt>SynchronousQueue</tt> with the specified fairness policy.
+ * Creates a {@code SynchronousQueue} with the specified fairness policy.
*
* @param fair if true, waiting threads contend in FIFO order for
* access; otherwise the order is unspecified.
@@ -882,8 +882,8 @@
* Inserts the specified element into this queue, waiting if necessary
* up to the specified wait time for another thread to receive it.
*
- * @return <tt>true</tt> if successful, or <tt>false</tt> if the
- * specified waiting time elapses before a consumer appears.
+ * @return {@code true} if successful, or {@code false} if the
+ * specified waiting time elapses before a consumer appears
* @throws InterruptedException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
@@ -902,8 +902,8 @@
* waiting to receive it.
*
* @param e the element to add
- * @return <tt>true</tt> if the element was added to this queue, else
- * <tt>false</tt>
+ * @return {@code true} if the element was added to this queue, else
+ * {@code false}
* @throws NullPointerException if the specified element is null
*/
public boolean offer(E e) {
@@ -931,8 +931,8 @@
* if necessary up to the specified wait time, for another thread
* to insert it.
*
- * @return the head of this queue, or <tt>null</tt> if the
- * specified waiting time elapses before an element is present.
+ * @return the head of this queue, or {@code null} if the
+ * specified waiting time elapses before an element is present
* @throws InterruptedException {@inheritDoc}
*/
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
@@ -946,18 +946,18 @@
* Retrieves and removes the head of this queue, if another thread
* is currently making an element available.
*
- * @return the head of this queue, or <tt>null</tt> if no
- * element is available.
+ * @return the head of this queue, or {@code null} if no
+ * element is available
*/
public E poll() {
return transferer.transfer(null, true, 0);
}
/**
- * Always returns <tt>true</tt>.
- * A <tt>SynchronousQueue</tt> has no internal capacity.
+ * Always returns {@code true}.
+ * A {@code SynchronousQueue} has no internal capacity.
*
- * @return <tt>true</tt>
+ * @return {@code true}
*/
public boolean isEmpty() {
return true;
@@ -965,9 +965,9 @@
/**
* Always returns zero.
- * A <tt>SynchronousQueue</tt> has no internal capacity.
+ * A {@code SynchronousQueue} has no internal capacity.
*
- * @return zero.
+ * @return zero
*/
public int size() {
return 0;
@@ -975,9 +975,9 @@
/**
* Always returns zero.
- * A <tt>SynchronousQueue</tt> has no internal capacity.
+ * A {@code SynchronousQueue} has no internal capacity.
*
- * @return zero.
+ * @return zero
*/
public int remainingCapacity() {
return 0;
@@ -985,80 +985,80 @@
/**
* Does nothing.
- * A <tt>SynchronousQueue</tt> has no internal capacity.
+ * A {@code SynchronousQueue} has no internal capacity.
*/
public void clear() {
}
/**
- * Always returns <tt>false</tt>.
- * A <tt>SynchronousQueue</tt> has no internal capacity.
+ * Always returns {@code false}.
+ * A {@code SynchronousQueue} has no internal capacity.
*
* @param o the element
- * @return <tt>false</tt>
+ * @return {@code false}
*/
public boolean contains(Object o) {
return false;
}
/**
- * Always returns <tt>false</tt>.
- * A <tt>SynchronousQueue</tt> has no internal capacity.
+ * Always returns {@code false}.
+ * A {@code SynchronousQueue} has no internal capacity.
*
* @param o the element to remove
- * @return <tt>false</tt>
+ * @return {@code false}
*/
public boolean remove(Object o) {
return false;
}
/**
- * Returns <tt>false</tt> unless the given collection is empty.
- * A <tt>SynchronousQueue</tt> has no internal capacity.
+ * Returns {@code false} unless the given collection is empty.
+ * A {@code SynchronousQueue} has no internal capacity.
*
* @param c the collection
- * @return <tt>false</tt> unless given collection is empty
+ * @return {@code false} unless given collection is empty
*/
public boolean containsAll(Collection<?> c) {
return c.isEmpty();
}
/**
- * Always returns <tt>false</tt>.
- * A <tt>SynchronousQueue</tt> has no internal capacity.
+ * Always returns {@code false}.
+ * A {@code SynchronousQueue} has no internal capacity.
*
* @param c the collection
- * @return <tt>false</tt>
+ * @return {@code false}
*/
public boolean removeAll(Collection<?> c) {
return false;
}
/**
- * Always returns <tt>false</tt>.
- * A <tt>SynchronousQueue</tt> has no internal capacity.
+ * Always returns {@code false}.
+ * A {@code SynchronousQueue} has no internal capacity.
*
* @param c the collection
- * @return <tt>false</tt>
+ * @return {@code false}
*/
public boolean retainAll(Collection<?> c) {
return false;
}
/**
- * Always returns <tt>null</tt>.
- * A <tt>SynchronousQueue</tt> does not return elements
+ * Always returns {@code null}.
+ * A {@code SynchronousQueue} does not return elements
* unless actively waited on.
*
- * @return <tt>null</tt>
+ * @return {@code null}
*/
public E peek() {
return null;
}
/**
- * Returns an empty iterator in which <tt>hasNext</tt> always returns
- * <tt>false</tt>.
+ * Returns an empty iterator in which {@code hasNext} always returns
+ * {@code false}.
*
* @return an empty iterator
*/
@@ -1077,6 +1077,10 @@
public void remove() { throw new IllegalStateException(); }
}
+ public Spliterator<E> spliterator() {
+ return Spliterators.emptySpliterator();
+ }
+
/**
* Returns a zero-length array.
* @return a zero-length array
@@ -1086,7 +1090,7 @@
}
/**
- * Sets the zeroeth element of the specified array to <tt>null</tt>
+ * Sets the zeroeth element of the specified array to {@code null}
* (if the array has non-zero length) and returns it.
*
* @param a the array
--- a/jdk/src/share/classes/java/util/concurrent/TimeUnit.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/java/util/concurrent/TimeUnit.java Wed Jul 03 14:35:10 2013 -0700
@@ -36,10 +36,10 @@
package java.util.concurrent;
/**
- * A <tt>TimeUnit</tt> represents time durations at a given unit of
+ * A {@code TimeUnit} represents time durations at a given unit of
* granularity and provides utility methods to convert across units,
* and to perform timing and delay operations in these units. A
- * <tt>TimeUnit</tt> does not maintain time information, but only
+ * {@code TimeUnit} does not maintain time information, but only
* helps organize and use time representations that may be maintained
* separately across various contexts. A nanosecond is defined as one
* thousandth of a microsecond, a microsecond as one thousandth of a
@@ -47,7 +47,7 @@
* as sixty seconds, an hour as sixty minutes, and a day as twenty four
* hours.
*
- * <p>A <tt>TimeUnit</tt> is mainly used to inform time-based methods
+ * <p>A {@code TimeUnit} is mainly used to inform time-based methods
* how a given timing parameter should be interpreted. For example,
* the following code will timeout in 50 milliseconds if the {@link
* java.util.concurrent.locks.Lock lock} is not available:
@@ -63,7 +63,7 @@
*
* Note however, that there is no guarantee that a particular timeout
* implementation will be able to notice the passage of time at the
- * same granularity as the given <tt>TimeUnit</tt>.
+ * same granularity as the given {@code TimeUnit}.
*
* @since 1.5
* @author Doug Lea
@@ -174,83 +174,82 @@
// etc. are not declared abstract but otherwise act as abstract methods.
/**
- * Convert the given time duration in the given unit to this
- * unit. Conversions from finer to coarser granularities
- * truncate, so lose precision. For example converting
- * <tt>999</tt> milliseconds to seconds results in
- * <tt>0</tt>. Conversions from coarser to finer granularities
- * with arguments that would numerically overflow saturate to
- * <tt>Long.MIN_VALUE</tt> if negative or <tt>Long.MAX_VALUE</tt>
- * if positive.
+ * Converts the given time duration in the given unit to this unit.
+ * Conversions from finer to coarser granularities truncate, so
+ * lose precision. For example, converting {@code 999} milliseconds
+ * to seconds results in {@code 0}. Conversions from coarser to
+ * finer granularities with arguments that would numerically
+ * overflow saturate to {@code Long.MIN_VALUE} if negative or
+ * {@code Long.MAX_VALUE} if positive.
*
* <p>For example, to convert 10 minutes to milliseconds, use:
- * <tt>TimeUnit.MILLISECONDS.convert(10L, TimeUnit.MINUTES)</tt>
+ * {@code TimeUnit.MILLISECONDS.convert(10L, TimeUnit.MINUTES)}
*
- * @param sourceDuration the time duration in the given <tt>sourceUnit</tt>
- * @param sourceUnit the unit of the <tt>sourceDuration</tt> argument
+ * @param sourceDuration the time duration in the given {@code sourceUnit}
+ * @param sourceUnit the unit of the {@code sourceDuration} argument
* @return the converted duration in this unit,
- * or <tt>Long.MIN_VALUE</tt> if conversion would negatively
- * overflow, or <tt>Long.MAX_VALUE</tt> if it would positively overflow.
+ * or {@code Long.MIN_VALUE} if conversion would negatively
+ * overflow, or {@code Long.MAX_VALUE} if it would positively overflow.
*/
public long convert(long sourceDuration, TimeUnit sourceUnit) {
throw new AbstractMethodError();
}
/**
- * Equivalent to <tt>NANOSECONDS.convert(duration, this)</tt>.
+ * Equivalent to
+ * {@link #convert(long, TimeUnit) NANOSECONDS.convert(duration, this)}.
* @param duration the duration
* @return the converted duration,
- * or <tt>Long.MIN_VALUE</tt> if conversion would negatively
- * overflow, or <tt>Long.MAX_VALUE</tt> if it would positively overflow.
- * @see #convert
+ * or {@code Long.MIN_VALUE} if conversion would negatively
+ * overflow, or {@code Long.MAX_VALUE} if it would positively overflow.
*/
public long toNanos(long duration) {
throw new AbstractMethodError();
}
/**
- * Equivalent to <tt>MICROSECONDS.convert(duration, this)</tt>.
+ * Equivalent to
+ * {@link #convert(long, TimeUnit) MICROSECONDS.convert(duration, this)}.
* @param duration the duration
* @return the converted duration,
- * or <tt>Long.MIN_VALUE</tt> if conversion would negatively
- * overflow, or <tt>Long.MAX_VALUE</tt> if it would positively overflow.
- * @see #convert
+ * or {@code Long.MIN_VALUE} if conversion would negatively
+ * overflow, or {@code Long.MAX_VALUE} if it would positively overflow.
*/
public long toMicros(long duration) {
throw new AbstractMethodError();
}
/**
- * Equivalent to <tt>MILLISECONDS.convert(duration, this)</tt>.
+ * Equivalent to
+ * {@link #convert(long, TimeUnit) MILLISECONDS.convert(duration, this)}.
* @param duration the duration
* @return the converted duration,
- * or <tt>Long.MIN_VALUE</tt> if conversion would negatively
- * overflow, or <tt>Long.MAX_VALUE</tt> if it would positively overflow.
- * @see #convert
+ * or {@code Long.MIN_VALUE} if conversion would negatively
+ * overflow, or {@code Long.MAX_VALUE} if it would positively overflow.
*/
public long toMillis(long duration) {
throw new AbstractMethodError();
}
/**
- * Equivalent to <tt>SECONDS.convert(duration, this)</tt>.
+ * Equivalent to
+ * {@link #convert(long, TimeUnit) SECONDS.convert(duration, this)}.
* @param duration the duration
* @return the converted duration,
- * or <tt>Long.MIN_VALUE</tt> if conversion would negatively
- * overflow, or <tt>Long.MAX_VALUE</tt> if it would positively overflow.
- * @see #convert
+ * or {@code Long.MIN_VALUE} if conversion would negatively
+ * overflow, or {@code Long.MAX_VALUE} if it would positively overflow.
*/
public long toSeconds(long duration) {
throw new AbstractMethodError();
}
/**
- * Equivalent to <tt>MINUTES.convert(duration, this)</tt>.
+ * Equivalent to
+ * {@link #convert(long, TimeUnit) MINUTES.convert(duration, this)}.
* @param duration the duration
* @return the converted duration,
- * or <tt>Long.MIN_VALUE</tt> if conversion would negatively
- * overflow, or <tt>Long.MAX_VALUE</tt> if it would positively overflow.
- * @see #convert
+ * or {@code Long.MIN_VALUE} if conversion would negatively
+ * overflow, or {@code Long.MAX_VALUE} if it would positively overflow.
* @since 1.6
*/
public long toMinutes(long duration) {
@@ -258,12 +257,12 @@
}
/**
- * Equivalent to <tt>HOURS.convert(duration, this)</tt>.
+ * Equivalent to
+ * {@link #convert(long, TimeUnit) HOURS.convert(duration, this)}.
* @param duration the duration
* @return the converted duration,
- * or <tt>Long.MIN_VALUE</tt> if conversion would negatively
- * overflow, or <tt>Long.MAX_VALUE</tt> if it would positively overflow.
- * @see #convert
+ * or {@code Long.MIN_VALUE} if conversion would negatively
+ * overflow, or {@code Long.MAX_VALUE} if it would positively overflow.
* @since 1.6
*/
public long toHours(long duration) {
@@ -271,10 +270,10 @@
}
/**
- * Equivalent to <tt>DAYS.convert(duration, this)</tt>.
+ * Equivalent to
+ * {@link #convert(long, TimeUnit) DAYS.convert(duration, this)}.
* @param duration the duration
* @return the converted duration
- * @see #convert
* @since 1.6
*/
public long toDays(long duration) {
@@ -294,9 +293,9 @@
* Performs a timed {@link Object#wait(long, int) Object.wait}
* using this time unit.
* This is a convenience method that converts timeout arguments
- * into the form required by the <tt>Object.wait</tt> method.
+ * into the form required by the {@code Object.wait} method.
*
- * <p>For example, you could implement a blocking <tt>poll</tt>
+ * <p>For example, you could implement a blocking {@code poll}
* method (see {@link BlockingQueue#poll BlockingQueue.poll})
* using:
*
@@ -327,7 +326,7 @@
* Performs a timed {@link Thread#join(long, int) Thread.join}
* using this time unit.
* This is a convenience method that converts time arguments into the
- * form required by the <tt>Thread.join</tt> method.
+ * form required by the {@code Thread.join} method.
*
* @param thread the thread to wait for
* @param timeout the maximum time to wait. If less than
@@ -347,7 +346,7 @@
* Performs a {@link Thread#sleep(long, int) Thread.sleep} using
* this time unit.
* This is a convenience method that converts time arguments into the
- * form required by the <tt>Thread.sleep</tt> method.
+ * form required by the {@code Thread.sleep} method.
*
* @param timeout the minimum time to sleep. If less than
* or equal to zero, do not sleep at all.
--- a/jdk/src/share/classes/java/util/concurrent/TimeoutException.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/java/util/concurrent/TimeoutException.java Wed Jul 03 14:35:10 2013 -0700
@@ -40,7 +40,7 @@
* operations for which a timeout is specified need a means to
* indicate that the timeout has occurred. For many such operations it
* is possible to return a value that indicates timeout; when that is
- * not possible or desirable then <tt>TimeoutException</tt> should be
+ * not possible or desirable then {@code TimeoutException} should be
* declared and thrown.
*
* @since 1.5
@@ -50,13 +50,13 @@
private static final long serialVersionUID = 1900926677490660714L;
/**
- * Constructs a <tt>TimeoutException</tt> with no specified detail
+ * Constructs a {@code TimeoutException} with no specified detail
* message.
*/
public TimeoutException() {}
/**
- * Constructs a <tt>TimeoutException</tt> with the specified detail
+ * Constructs a {@code TimeoutException} with the specified detail
* message.
*
* @param message the detail message
--- a/jdk/src/share/classes/java/util/concurrent/package-info.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/java/util/concurrent/package-info.java Wed Jul 03 14:35:10 2013 -0700
@@ -48,7 +48,7 @@
*
* {@link java.util.concurrent.Executor} is a simple standardized
* interface for defining custom thread-like subsystems, including
- * thread pools, asynchronous IO, and lightweight task frameworks.
+ * thread pools, asynchronous I/O, and lightweight task frameworks.
* Depending on which concrete Executor class is being used, tasks may
* execute in a newly created thread, an existing task-execution thread,
* or the thread calling {@link java.util.concurrent.Executor#execute
@@ -102,8 +102,10 @@
* <h2>Queues</h2>
*
* The {@link java.util.concurrent.ConcurrentLinkedQueue} class
- * supplies an efficient scalable thread-safe non-blocking FIFO
- * queue.
+ * supplies an efficient scalable thread-safe non-blocking FIFO queue.
+ * The {@link java.util.concurrent.ConcurrentLinkedDeque} class is
+ * similar, but additionally supports the {@link java.util.Deque}
+ * interface.
*
* <p>Five implementations in {@code java.util.concurrent} support
* the extended {@link java.util.concurrent.BlockingQueue}
@@ -117,7 +119,7 @@
* for producer-consumer, messaging, parallel tasking, and
* related concurrent designs.
*
- * <p> Extended interface {@link java.util.concurrent.TransferQueue},
+ * <p>Extended interface {@link java.util.concurrent.TransferQueue},
* and implementation {@link java.util.concurrent.LinkedTransferQueue}
* introduce a synchronous {@code transfer} method (along with related
* features) in which a producer may optionally block awaiting its
@@ -216,9 +218,9 @@
* it may (or may not) reflect any updates since the iterator was
* created.
*
- * <h2><a name="MemoryVisibility">Memory Consistency Properties</a></h2>
+ * <h2 id="MemoryVisibility">Memory Consistency Properties</h2>
*
- * <a href="http://docs.oracle.com/javase/specs/jls/se7/html/index.html">
+ * <a href="http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4.5">
* Chapter 17 of the Java Language Specification</a> defines the
* <i>happens-before</i> relation on memory operations such as reads and
* writes of shared variables. The results of a write by one thread are
--- a/jdk/src/share/classes/java/util/logging/LogManager.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/java/util/logging/LogManager.java Wed Jul 03 14:35:10 2013 -0700
@@ -193,13 +193,27 @@
// Create and retain Logger for the root of the namespace.
manager.rootLogger = manager.new RootLogger();
+ // since by design the global manager's userContext and
+ // systemContext don't have their requiresDefaultLoggers
+ // flag set - we make sure to add the root logger to
+ // the global manager's default contexts here.
manager.addLogger(manager.rootLogger);
- manager.systemContext.addLocalLogger(manager.rootLogger);
+ manager.systemContext.addLocalLogger(manager.rootLogger, false);
+ manager.userContext.addLocalLogger(manager.rootLogger, false);
// Adding the global Logger. Doing so in the Logger.<clinit>
// would deadlock with the LogManager.<clinit>.
- Logger.global.setLogManager(manager);
- manager.addLogger(Logger.global);
+ // Do not call Logger.getGlobal() here as this might trigger
+ // the deadlock too.
+ @SuppressWarnings("deprecation")
+ final Logger global = Logger.global;
+ global.setLogManager(manager);
+
+ // Make sure the global logger will be registered in the
+ // global manager's default contexts.
+ manager.addLogger(global);
+ manager.systemContext.addLocalLogger(global, false);
+ manager.userContext.addLocalLogger(global, false);
// We don't call readConfiguration() here, as we may be running
// very early in the JVM startup sequence. Instead readConfiguration
@@ -401,7 +415,11 @@
if (javaAwtAccess.isMainAppContext()) {
context = userContext;
} else {
- context = new LoggerContext();
+ // Create a new LoggerContext for the applet.
+ // The new logger context has its requiresDefaultLoggers
+ // flag set to true - so that these loggers will be
+ // lazily added when the context is firt accessed.
+ context = new LoggerContext(true);
}
javaAwtAccess.put(ecx, LoggerContext.class, context);
}
@@ -508,9 +526,13 @@
private final Hashtable<String,LoggerWeakRef> namedLoggers = new Hashtable<>();
// Tree of named Loggers
private final LogNode root;
-
+ private final boolean requiresDefaultLoggers;
private LoggerContext() {
+ this(false);
+ }
+ private LoggerContext(boolean requiresDefaultLoggers) {
this.root = new LogNode(null, this);
+ this.requiresDefaultLoggers = requiresDefaultLoggers;
}
Logger demandLogger(String name, String resourceBundleName) {
@@ -519,7 +541,27 @@
return manager.demandLogger(name, resourceBundleName, null);
}
+
+ // Due to subtle deadlock issues getUserContext() no longer
+ // calls addLocalLogger(rootLogger);
+ // Therefore - we need to add the default loggers later on.
+ // Checks that the context is properly initialized
+ // This is necessary before calling e.g. find(name)
+ // or getLoggerNames()
+ //
+ private void ensureInitialized() {
+ if (requiresDefaultLoggers) {
+ // Ensure that the root and global loggers are set.
+ ensureDefaultLogger(manager.rootLogger);
+ ensureDefaultLogger(Logger.global);
+ }
+ }
+
+
synchronized Logger findLogger(String name) {
+ // ensure that this context is properly initialized before
+ // looking for loggers.
+ ensureInitialized();
LoggerWeakRef ref = namedLoggers.get(name);
if (ref == null) {
return null;
@@ -533,21 +575,76 @@
return logger;
}
- synchronized void ensureRootLogger(Logger logger) {
- if (logger.getName().isEmpty())
- return;
+ // This method is called before adding a logger to the
+ // context.
+ // 'logger' is the context that will be added.
+ // This method will ensure that the defaults loggers are added
+ // before adding 'logger'.
+ //
+ private void ensureAllDefaultLoggers(Logger logger) {
+ if (requiresDefaultLoggers) {
+ final String name = logger.getName();
+ if (!name.isEmpty()) {
+ ensureDefaultLogger(manager.rootLogger);
+ }
+ if (!Logger.GLOBAL_LOGGER_NAME.equals(name)) {
+ ensureDefaultLogger(Logger.global);
+ }
+ }
+ }
+
+ private void ensureDefaultLogger(Logger logger) {
+ // Used for lazy addition of root logger and global logger
+ // to a LoggerContext.
- // during initialization, rootLogger is null when
- // instantiating itself RootLogger
- if (findLogger("") == null && manager.rootLogger != null) {
- addLocalLogger(manager.rootLogger);
+ // This check is simple sanity: we do not want that this
+ // method be called for anything else than Logger.global
+ // or owner.rootLogger.
+ if (!requiresDefaultLoggers || logger == null
+ || logger != Logger.global && logger != manager.rootLogger) {
+
+ // the case where we have a non null logger which is neither
+ // Logger.global nor manager.rootLogger indicates a serious
+ // issue - as ensureDefaultLogger should never be called
+ // with any other loggers than one of these two (or null - if
+ // e.g manager.rootLogger is not yet initialized)...
+ assert logger == null;
+
+ return;
}
+
+ // Adds the logger if it's not already there.
+ if (!namedLoggers.containsKey(logger.getName())) {
+ // It is important to prevent addLocalLogger to
+ // call ensureAllDefaultLoggers when we're in the process
+ // off adding one of those default loggers - as this would
+ // immediately cause a stack overflow.
+ // Therefore we must pass addDefaultLoggersIfNeeded=false,
+ // even if requiresDefaultLoggers is true.
+ addLocalLogger(logger, false);
+ }
+ }
+
+ boolean addLocalLogger(Logger logger) {
+ // no need to add default loggers if it's not required
+ return addLocalLogger(logger, requiresDefaultLoggers);
}
// Add a logger to this context. This method will only set its level
// and process parent loggers. It doesn't set its handlers.
- synchronized boolean addLocalLogger(Logger logger) {
- ensureRootLogger(logger);
+ synchronized boolean addLocalLogger(Logger logger, boolean addDefaultLoggersIfNeeded) {
+ // addDefaultLoggersIfNeeded serves to break recursion when adding
+ // default loggers. If we're adding one of the default loggers
+ // (we're being called from ensureDefaultLogger()) then
+ // addDefaultLoggersIfNeeded will be false: we don't want to
+ // call ensureAllDefaultLoggers again.
+ //
+ // Note: addDefaultLoggersIfNeeded can also be false when
+ // requiresDefaultLoggers is false - since calling
+ // ensureAllDefaultLoggers would have no effect in this case.
+ if (addDefaultLoggersIfNeeded) {
+ ensureAllDefaultLoggers(logger);
+ }
final String name = logger.getName();
if (name == null) {
@@ -615,6 +712,9 @@
}
synchronized Enumeration<String> getLoggerNames() {
+ // ensure that this context is properly initialized before
+ // returning logger names.
+ ensureInitialized();
return namedLoggers.keys();
}
--- a/jdk/src/share/classes/java/util/logging/Logger.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/java/util/logging/Logger.java Wed Jul 03 14:35:10 2013 -0700
@@ -232,6 +232,27 @@
* @since 1.7
*/
public static final Logger getGlobal() {
+ // In order to break a cyclic dependence between the LogManager
+ // and Logger static initializers causing deadlocks, the global
+ // logger is created with a special constructor that does not
+ // initialize its log manager.
+ //
+ // If an application calls Logger.getGlobal() before any logger
+ // has been initialized, it is therefore possible that the
+ // LogManager class has not been initialized yet, and therefore
+ // Logger.global.manager will be null.
+ //
+ // In order to finish the initialization of the global logger, we
+ // will therefore call LogManager.getLogManager() here.
+ //
+ // Care must be taken *not* to call Logger.getGlobal() in
+ // LogManager static initializers in order to avoid such
+ // deadlocks.
+ //
+ if (global != null && global.manager == null) {
+ // Complete initialization of the global Logger.
+ global.manager = LogManager.getLogManager();
+ }
return global;
}
--- a/jdk/src/share/classes/java/util/spi/LocaleServiceProvider.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/java/util/spi/LocaleServiceProvider.java Wed Jul 03 14:35:10 2013 -0700
@@ -42,7 +42,7 @@
* interfaces to offer support for locales beyond the set of locales
* supported by the Java runtime environment itself.
* <p>
- * <h4>Packaging of Locale Sensitive Service Provider Implementations</h4>
+ * <h3>Packaging of Locale Sensitive Service Provider Implementations</h3>
* Implementations of these locale sensitive services are packaged using the
* <a href="../../../../technotes/guides/extensions/index.html">Java Extension Mechanism</a>
* as installed extensions. A provider identifies itself with a
@@ -165,7 +165,7 @@
/**
* Returns {@code true} if the given {@code locale} is supported by
* this locale service provider. The given {@code locale} may contain
- * <a href="../Locale.html#def_extensions">extensions<a/> that should be
+ * <a href="../Locale.html#def_extensions">extensions</a> that should be
* taken into account for the support determination.
*
* <p>The default implementation returns {@code true} if the given {@code locale}
--- a/jdk/src/share/classes/java/util/stream/SliceOps.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/java/util/stream/SliceOps.java Wed Jul 03 14:35:10 2013 -0700
@@ -598,9 +598,9 @@
final Node.Builder<P_OUT> nb = op.makeNodeBuilder(sizeIfKnown, generator);
Sink<P_OUT> opSink = op.opWrapSink(helper.getStreamAndOpFlags(), nb);
helper.copyIntoWithCancel(helper.wrapSink(opSink), spliterator);
- // It is necessary to truncate here since the result at the root
- // can only be set once
- return doTruncate(nb.build());
+ // There is no need to truncate since the op performs the
+ // skipping and limiting of elements
+ return nb.build();
}
else {
Node<P_OUT> node = helper.wrapAndCopyInto(helper.makeNodeBuilder(-1, generator),
--- a/jdk/src/share/classes/javax/crypto/Cipher.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/javax/crypto/Cipher.java Wed Jul 03 14:35:10 2013 -0700
@@ -1135,7 +1135,7 @@
*
* <p>If this cipher (including its underlying feedback or padding scheme)
* requires any random bytes (e.g., for parameter generation), it will get
- * them using the {@link SecureRandom <code>SecureRandom</code>}
+ * them using the {@link java.security.SecureRandom}
* implementation of the highest-priority
* installed provider as the source of randomness.
* (If none of the installed providers supply an implementation of
@@ -1263,7 +1263,7 @@
*
* <p>If this cipher (including its underlying feedback or padding scheme)
* requires any random bytes (e.g., for parameter generation), it will get
- * them using the {@link SecureRandom <code>SecureRandom</code>}
+ * them using the {@link java.security.SecureRandom}
* implementation of the highest-priority
* installed provider as the source of randomness.
* (If none of the installed providers supply an implementation of
@@ -1400,7 +1400,7 @@
*
* <p>If this cipher (including its underlying feedback or padding scheme)
* requires any random bytes (e.g., for parameter generation), it will get
- * them using the {@link SecureRandom <code>SecureRandom</code>}
+ * them using the {@link java.security.SecureRandom}
* implementation of the highest-priority
* installed provider as the source of randomness.
* (If none of the installed providers supply an implementation of
--- a/jdk/src/share/classes/javax/crypto/CipherInputStream.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/javax/crypto/CipherInputStream.java Wed Jul 03 14:35:10 2013 -0700
@@ -245,7 +245,7 @@
* <p>Fewer bytes than requested might be skipped.
* The actual number of bytes skipped is equal to <code>n</code> or
* the result of a call to
- * {@link #available() <code>available</code>},
+ * {@link #available() available},
* whichever is smaller.
* If <code>n</code> is less than zero, no bytes are skipped.
*
--- a/jdk/src/share/classes/javax/crypto/ExemptionMechanism.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/javax/crypto/ExemptionMechanism.java Wed Jul 03 14:35:10 2013 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 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
@@ -116,7 +116,7 @@
* mechanism.
* See the ExemptionMechanism section in the
* <a href=
- * "{docRoot}/../technotes/guides/security/StandardNames.html#Exemption">
+ * "{@docRoot}/../technotes/guides/security/StandardNames.html#Exemption">
* Java Cryptography Architecture Standard Algorithm Name Documentation</a>
* for information about standard exemption mechanism names.
*
@@ -155,7 +155,7 @@
* @param algorithm the standard name of the requested exemption mechanism.
* See the ExemptionMechanism section in the
* <a href=
- * "{docRoot}/../technotes/guides/security/StandardNames.html#Exemption">
+ * "{@docRoot}/../technotes/guides/security/StandardNames.html#Exemption">
* Java Cryptography Architecture Standard Algorithm Name Documentation</a>
* for information about standard exemption mechanism names.
*
@@ -199,7 +199,7 @@
* @param algorithm the standard name of the requested exemption mechanism.
* See the ExemptionMechanism section in the
* <a href=
- * "{docRoot}/../technotes/guides/security/StandardNames.html#Exemption">
+ * "{@docRoot}/../technotes/guides/security/StandardNames.html#Exemption">
* Java Cryptography Architecture Standard Algorithm Name Documentation</a>
* for information about standard exemption mechanism names.
*
--- a/jdk/src/share/classes/javax/crypto/KeyAgreement.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/javax/crypto/KeyAgreement.java Wed Jul 03 14:35:10 2013 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 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
@@ -149,7 +149,7 @@
* algorithm.
* See the KeyAgreement section in the <a href=
* "{@docRoot}/../technotes/guides/security/StandardNames.html#KeyAgreement">
- * Java Cryptography Architecture Standard Algorithm Name Documentation
+ * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
* for information about standard algorithm names.
*
* @return the new <code>KeyAgreement</code> object.
@@ -196,7 +196,7 @@
* algorithm.
* See the KeyAgreement section in the <a href=
* "{@docRoot}/../technotes/guides/security/StandardNames.html#KeyAgreement">
- * Java Cryptography Architecture Standard Algorithm Name Documentation
+ * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
* for information about standard algorithm names.
*
* @param provider the name of the provider.
@@ -240,7 +240,7 @@
* algorithm.
* See the KeyAgreement section in the <a href=
* "{@docRoot}/../technotes/guides/security/StandardNames.html#KeyAgreement">
- * Java Cryptography Architecture Standard Algorithm Name Documentation
+ * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
* for information about standard algorithm names.
*
* @param provider the provider.
@@ -418,7 +418,7 @@
*
* <p> If this key agreement requires any random bytes, it will get
* them using the
- * {@link SecureRandom <code>SecureRandom</code>}
+ * {@link java.security.SecureRandom}
* implementation of the highest-priority
* installed provider as the source of randomness.
* (If none of the installed providers supply an implementation of
@@ -476,7 +476,7 @@
*
* <p> If this key agreement requires any random bytes, it will get
* them using the
- * {@link SecureRandom <code>SecureRandom</code>}
+ * {@link java.security.SecureRandom}
* implementation of the highest-priority
* installed provider as the source of randomness.
* (If none of the installed providers supply an implementation of
--- a/jdk/src/share/classes/javax/crypto/KeyGenerator.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/javax/crypto/KeyGenerator.java Wed Jul 03 14:35:10 2013 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 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
@@ -398,7 +398,7 @@
*
* <p> If this key generator requires any random bytes, it will get them
* using the
- * {@link SecureRandom <code>SecureRandom</code>}
+ * {@link java.security.SecureRandom}
* implementation of the highest-priority installed
* provider as the source of randomness.
* (If none of the installed providers supply an implementation of
@@ -463,7 +463,7 @@
*
* <p> If this key generator requires any random bytes, it will get them
* using the
- * {@link SecureRandom <code>SecureRandom</code>}
+ * {@link java.security.SecureRandom}
* implementation of the highest-priority installed
* provider as the source of randomness.
* (If none of the installed providers supply an implementation of
--- a/jdk/src/share/classes/javax/crypto/NullCipher.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/javax/crypto/NullCipher.java Wed Jul 03 14:35:10 2013 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 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
@@ -38,6 +38,9 @@
public class NullCipher extends Cipher {
+ /**
+ * Creates a NullCipher object.
+ */
public NullCipher() {
super(new NullCipherSpi(), null);
}
--- a/jdk/src/share/classes/javax/security/auth/Subject.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/javax/security/auth/Subject.java Wed Jul 03 14:35:10 2013 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 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
@@ -325,6 +325,9 @@
* <code>action</code> will run as. This parameter
* may be <code>null</code>. <p>
*
+ * @param <T> the type of the value returned by the PrivilegedAction's
+ * {@code run} method.
+ *
* @param action the code to be run as the specified
* <code>Subject</code>. <p>
*
@@ -378,6 +381,9 @@
* <code>action</code> will run as. This parameter
* may be <code>null</code>. <p>
*
+ * @param <T> the type of the value returned by the
+ * PrivilegedExceptionAction's {@code run} method.
+ *
* @param action the code to be run as the specified
* <code>Subject</code>. <p>
*
@@ -434,6 +440,9 @@
* <code>action</code> will run as. This parameter
* may be <code>null</code>. <p>
*
+ * @param <T> the type of the value returned by the PrivilegedAction's
+ * {@code run} method.
+ *
* @param action the code to be run as the specified
* <code>Subject</code>. <p>
*
@@ -492,6 +501,9 @@
* <code>action</code> will run as. This parameter
* may be <code>null</code>. <p>
*
+ * @param <T> the type of the value returned by the
+ * PrivilegedExceptionAction's {@code run} method.
+ *
* @param action the code to be run as the specified
* <code>Subject</code>. <p>
*
@@ -590,6 +602,8 @@
*
* <p>
*
+ * @param <T> the type of the class modeled by {@code c}
+ *
* @param c the returned <code>Set</code> of Principals will all be
* instances of this class.
*
@@ -684,6 +698,8 @@
*
* <p>
*
+ * @param <T> the type of the class modeled by {@code c}
+ *
* @param c the returned <code>Set</code> of public credentials will all be
* instances of this class.
*
@@ -721,6 +737,8 @@
*
* <p>
*
+ * @param <T> the type of the class modeled by {@code c}
+ *
* @param c the returned <code>Set</code> of private credentials will all be
* instances of this class.
*
--- a/jdk/src/share/classes/javax/security/cert/X509Certificate.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/javax/security/cert/X509Certificate.java Wed Jul 03 14:35:10 2013 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 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
@@ -159,9 +159,9 @@
* certificate is expected to be in the input stream.
* Also, all X509Certificate
* subclasses must provide a constructor of the form:
- * <code><pre>
- * public <subClass>(InputStream inStream) ...
- * </pre></code>
+ * <pre>{@code
+ * public <subClass>(InputStream inStream) ...
+ * }</pre>
*
* @param inStream an input stream with the data to be read to
* initialize the certificate.
@@ -184,9 +184,9 @@
*
* <p>Note: All X509Certificate
* subclasses must provide a constructor of the form:
- * <code><pre>
- * public <subClass>(InputStream inStream) ...
- * </pre></code>
+ * <pre>{@code
+ * public <subClass>(InputStream inStream) ...
+ * }</pre>
*
* @param certData a byte array containing the DER-encoded
* certificate.
@@ -255,10 +255,12 @@
* is valid. It is defined in
* ASN.1 as:
* <pre>
- * validity Validity<p>
+ * validity Validity
+ *
* Validity ::= SEQUENCE {
* notBefore CertificateValidityDate,
- * notAfter CertificateValidityDate }<p>
+ * notAfter CertificateValidityDate }
+ *
* CertificateValidityDate ::= CHOICE {
* utcTime UTCTime,
* generalTime GeneralizedTime }
@@ -291,7 +293,8 @@
* Gets the <code>version</code> (version number) value from the
* certificate. The ASN.1 definition for this is:
* <pre>
- * version [0] EXPLICIT Version DEFAULT v1<p>
+ * version [0] EXPLICIT Version DEFAULT v1
+ *
* Version ::= INTEGER { v1(0), v2(1), v3(2) }
* </pre>
*
@@ -307,7 +310,7 @@
* serial number identify a unique certificate).
* The ASN.1 definition for this is:
* <pre>
- * serialNumber CertificateSerialNumber<p>
+ * serialNumber CertificateSerialNumber
*
* CertificateSerialNumber ::= INTEGER
* </pre>
@@ -325,7 +328,7 @@
* X.500 distinguished name (DN).
* The ASN.1 definition for this is:
* <pre>
- * issuer Name<p>
+ * issuer Name
*
* Name ::= CHOICE { RDNSequence }
* RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
@@ -371,11 +374,12 @@
* the certificate.
* The relevant ASN.1 definitions are:
* <pre>
- * validity Validity<p>
+ * validity Validity
*
* Validity ::= SEQUENCE {
* notBefore CertificateValidityDate,
- * notAfter CertificateValidityDate }<p>
+ * notAfter CertificateValidityDate }
+ *
* CertificateValidityDate ::= CHOICE {
* utcTime UTCTime,
* generalTime GeneralizedTime }
@@ -401,7 +405,8 @@
* signature algorithm. An example is the string "SHA-1/DSA".
* The ASN.1 definition for this is:
* <pre>
- * signatureAlgorithm AlgorithmIdentifier<p>
+ * signatureAlgorithm AlgorithmIdentifier
+ *
* AlgorithmIdentifier ::= SEQUENCE {
* algorithm OBJECT IDENTIFIER,
* parameters ANY DEFINED BY algorithm OPTIONAL }
--- a/jdk/src/share/classes/sun/security/provider/certpath/RevocationChecker.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/src/share/classes/sun/security/provider/certpath/RevocationChecker.java Wed Jul 03 14:35:10 2013 -0700
@@ -675,8 +675,12 @@
responderURI, respCert, params.date(),
ocspExtensions);
}
- } catch (IOException e) {
- throw new CertPathValidatorException(e);
+ } catch (Exception e) {
+ if (e instanceof CertPathValidatorException) {
+ throw (CertPathValidatorException) e;
+ } else {
+ throw new CertPathValidatorException(e);
+ }
}
RevocationStatus rs =
--- a/jdk/test/java/lang/invoke/InvokeDynamicPrintArgs.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/test/java/lang/invoke/InvokeDynamicPrintArgs.java Wed Jul 03 14:35:10 2013 -0700
@@ -22,6 +22,7 @@
*/
/* @test
+ * @bug 7050328 8007035
* @summary smoke test for invokedynamic instructions
* @build indify.Indify
* @compile InvokeDynamicPrintArgs.java
@@ -42,6 +43,7 @@
import java.io.*;
import java.lang.invoke.*;
+import java.security.*;
import static java.lang.invoke.MethodHandles.*;
import static java.lang.invoke.MethodType.*;
@@ -62,17 +64,10 @@
}
private static void checkConstantRefs() throws Throwable {
- // check some constant references:
+ // check some constant references to its self class
assertEquals(MT_bsm(), MH_bsm().type());
assertEquals(MT_bsm2(), MH_bsm2().type());
- try {
- assertEquals(MT_bsm(), non_MH_bsm().type());
- // if SM is installed, must throw before this point
- assertEquals(false, System.getSecurityManager() != null);
- } catch (SecurityException ex) {
- // if SM is installed, must throw to this point
- assertEquals(true, System.getSecurityManager() != null);
- }
+ assertEquals(MT_bsm(), non_MH_bsm().type());
}
private static void assertEquals(Object exp, Object act) {
if (exp == act || (exp != null && exp.equals(act))) return;
@@ -80,21 +75,8 @@
}
private static void setSM() {
- // Test for severe security manager interactions (7050328).
- class SM extends SecurityManager {
- public void checkPackageAccess(String pkg) {
- if (pkg.startsWith("test."))
- throw new SecurityException("checkPackageAccess "+pkg);
- }
- public void checkMemberAccess(Class<?> clazz, int which) {
- if (clazz == InvokeDynamicPrintArgs.class)
- throw new SecurityException("checkMemberAccess "+clazz.getName()+" #"+which);
- }
- // allow these others:
- public void checkPermission(java.security.Permission perm) {
- }
- }
- System.setSecurityManager(new SM());
+ Policy.setPolicy(new TestPolicy());
+ System.setSecurityManager(new SecurityManager());
}
private static PrintStream oldOut;
@@ -250,4 +232,22 @@
if (System.getProperty("InvokeDynamicPrintArgs.allow-untransformed") != null) return;
throw new AssertionError("this code should be statically transformed away by Indify");
}
+
+ static class TestPolicy extends Policy {
+ final PermissionCollection permissions = new Permissions();
+ TestPolicy() {
+ permissions.add(new java.io.FilePermission("<<ALL FILES>>", "read"));
+ }
+ public PermissionCollection getPermissions(ProtectionDomain domain) {
+ return permissions;
+ }
+
+ public PermissionCollection getPermissions(CodeSource codesource) {
+ return permissions;
+ }
+
+ public boolean implies(ProtectionDomain domain, Permission perm) {
+ return permissions.implies(perm);
+ }
+ }
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/invoke/TestCatchExceptionWithVarargs.java Wed Jul 03 14:35:10 2013 -0700
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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.
+ */
+
+/*
+ * @test
+ * @bug 8019184
+ * @summary MethodHandles.catchException() fails when methods have 8 args + varargs
+ */
+
+import java.util.*;
+import java.lang.invoke.*;
+
+public class TestCatchExceptionWithVarargs {
+
+ private static final Class<?> CLASS = TestCatchExceptionWithVarargs.class;
+ private static final int MAX_MH_ARITY = 254;
+
+ public static MethodHandle target;
+ public static MethodHandle handler;
+
+ private static Object firstArg;
+
+ static class MyException extends Exception {
+ }
+
+ public static Object target(Object... a) throws Exception {
+ if (a[0] != firstArg) {
+ throw new AssertionError("first argument different than expected: " + a[0] + " != " + firstArg);
+ }
+ throw new MyException();
+ }
+
+ public static Object handler(Object... a) {
+ if (a[0] != firstArg) {
+ throw new AssertionError("first argument different than expected: " + a[0] + " != " + firstArg);
+ }
+ return a[0];
+ }
+
+ static {
+ try {
+ MethodType mtype = MethodType.methodType(Object.class, Object[].class);
+ target = MethodHandles.lookup().findStatic(CLASS, "target", mtype);
+ handler = MethodHandles.lookup().findStatic(CLASS, "handler", mtype);
+ } catch (Exception e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ public static void main(String[] args) throws Throwable {
+ List<Class<?>> ptypes = new LinkedList<>();
+ ptypes.add(Object[].class);
+
+ // We use MAX_MH_ARITY - 1 here to account for the Object[] argument.
+ for (int i = 1; i < MAX_MH_ARITY - 1; i++) {
+ ptypes.add(0, Object.class);
+
+ MethodHandle targetWithArgs = target.asType(MethodType.methodType(Object.class, ptypes));
+ MethodHandle handlerWithArgs = handler.asType(MethodType.methodType(Object.class, ptypes));
+ handlerWithArgs = MethodHandles.dropArguments(handlerWithArgs, 0, MyException.class);
+
+ MethodHandle gwc1 = MethodHandles.catchException(targetWithArgs, MyException.class, handlerWithArgs);
+
+ // The next line throws an IllegalArgumentException if there is a bug.
+ MethodHandle gwc2 = MethodHandles.catchException(gwc1, MyException.class, handlerWithArgs);
+
+ // This is only to verify that the method handles can actually be invoked and do the right thing.
+ firstArg = new Object();
+ Object o = gwc2.asSpreader(Object[].class, ptypes.size() - 1).invoke(firstArg, new Object[i]);
+ if (o != firstArg) {
+ throw new AssertionError("return value different than expected: " + o + " != " + firstArg);
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/invoke/TestPrivateMember.java Wed Jul 03 14:35:10 2013 -0700
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+/**
+ * @test
+ * @bug 8007035
+ * @summary Test MethodHandle of a private member
+ *
+ * @run main TestPrivateMember
+ */
+
+public class TestPrivateMember {
+ public static void main(String... args) throws Throwable {
+ System.setSecurityManager(new SecurityManager());
+ TestPrivateMember t = new TestPrivateMember();
+ t.test();
+ }
+
+ public TestPrivateMember() {
+ }
+
+ public void test() throws Throwable {
+ MethodHandles.Lookup lookup = MethodHandles.lookup();
+ MethodType mt = MethodType.methodType(void.class);
+ try {
+ MethodHandle mh = lookup.findStatic(Class.class, "checkInitted", mt);
+ throw new RuntimeException("IllegalAccessException not thrown");
+ } catch (IllegalAccessException e) {
+ // okay
+ System.out.println("Expected exception: " + e.getMessage());
+ }
+ }
+}
--- a/jdk/test/java/security/cert/CertPathValidator/OCSP/FailoverToCRL.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/test/java/security/cert/CertPathValidator/OCSP/FailoverToCRL.java Wed Jul 03 14:35:10 2013 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 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
@@ -23,7 +23,7 @@
/**
* @test
- * @bug 6383095
+ * @bug 6383095 8019259
* @summary CRL revoked certificate failures masked by OCSP failures
*
* Note that the certificate validity is from Mar 16 14:55:35 2009 GMT to
@@ -254,12 +254,32 @@
CertPathValidator validator = CertPathValidator.getInstance("PKIX");
try {
+ System.out.println("Validating cert via OCSP: no responder URL");
validator.validate(path, params);
} catch (CertPathValidatorException cpve) {
if (cpve.getReason() != BasicReason.REVOKED) {
throw new Exception(
- "unexpect exception, should be a REVOKED CPVE", cpve);
+ "unexpected exception, should be a REVOKED CPVE", cpve);
}
+ System.out.println(" successful failover to using CRLs");
+ }
+
+ java.security.cert.PKIXRevocationChecker revocationChecker =
+ (java.security.cert.PKIXRevocationChecker)
+ validator.getRevocationChecker();
+ revocationChecker.setOCSPResponder(
+ new java.net.URI("bad_ocsp_responder_url"));
+ params.addCertPathChecker(revocationChecker);
+
+ try {
+ System.out.println("Validating cert via OCSP: bad responder URL");
+ validator.validate(path, params);
+ } catch (CertPathValidatorException cpve) {
+ if (cpve.getReason() != BasicReason.REVOKED) {
+ throw new Exception(
+ "unexpected exception, should be a REVOKED CPVE", cpve);
+ }
+ System.out.println(" successful failover to using CRLs");
}
}
}
--- a/jdk/test/java/util/logging/LogManagerInstanceTest.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/test/java/util/logging/LogManagerInstanceTest.java Wed Jul 03 14:35:10 2013 -0700
@@ -63,7 +63,7 @@
if (!super.addLogger(root))
throw new RuntimeException("Fail to addLogger " + root);
} else {
- System.out.println("Root logger already exists");
+ throw new RuntimeException("Root logger already exists");
}
this.base = root;
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/logging/Logger/getGlobal/TestGetGlobal.java Wed Jul 03 14:35:10 2013 -0700
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.util.Arrays;
+import java.util.List;
+import java.util.logging.Logger;
+
+/**
+ * @test
+ * @bug 7184195
+ * @summary checks that java.util.logging.Logger.getGlobal().info() logs without configuration
+ * @build TestGetGlobal testgetglobal.HandlerImpl testgetglobal.LogManagerImpl1 testgetglobal.LogManagerImpl2 testgetglobal.LogManagerImpl3 testgetglobal.BadLogManagerImpl testgetglobal.DummyLogManagerImpl
+ * @run main/othervm/timeout=10 TestGetGlobal
+ * @run main/othervm/timeout=10/policy=policy -Djava.security.manager TestGetGlobal
+ * @run main/othervm/timeout=10 -Djava.util.logging.manager=testgetglobal.LogManagerImpl1 TestGetGlobal
+ * @run main/othervm/timeout=10/policy=policy -Djava.security.manager -Djava.util.logging.manager=testgetglobal.LogManagerImpl1 TestGetGlobal
+ * @run main/othervm/timeout=10 -Djava.util.logging.manager=testgetglobal.LogManagerImpl2 TestGetGlobal
+ * @run main/othervm/timeout=10/policy=policy -Djava.security.manager -Djava.util.logging.manager=testgetglobal.LogManagerImpl2 TestGetGlobal
+ * @run main/othervm/timeout=10 -Djava.util.logging.manager=testgetglobal.LogManagerImpl3 TestGetGlobal
+ * @run main/othervm/timeout=10/policy=policy -Djava.security.manager -Djava.util.logging.manager=testgetglobal.LogManagerImpl3 TestGetGlobal
+ * @run main/othervm/timeout=10 -Djava.util.logging.manager=testgetglobal.BadLogManagerImpl TestGetGlobal
+ * @run main/othervm/timeout=10/policy=policy -Djava.security.manager -Djava.util.logging.manager=testgetglobal.BadLogManagerImpl TestGetGlobal
+ * @run main/othervm/timeout=10 -Djava.util.logging.manager=testgetglobal.DummyLogManagerImpl TestGetGlobal
+ * @run main/othervm/timeout=10/policy=policy -Djava.security.manager -Djava.util.logging.manager=testgetglobal.DummyLogManagerImpl TestGetGlobal
+ * @author danielfuchs
+ */
+public class TestGetGlobal {
+
+ final static String[] messages = {
+ "1. This message should not appear on the console.",
+ "2. This message should appear on the console.",
+ "3. This message should now appear on the console too."
+ };
+
+ static {
+ System.setProperty("java.util.logging.config.file",
+ System.getProperty("test.src", ".") + java.io.File.separator + "logging.properties");
+ }
+
+ public static void main(String... args) {
+
+ Logger.global.info(messages[0]); // at this point LogManager is not
+ // initialized yet, so this message should not appear.
+ Logger.getGlobal().info(messages[1]); // calling getGlobal() will
+ // initialize the LogManager - and thus this message should appear.
+ Logger.global.info(messages[2]); // Now that the LogManager is
+ // initialized, this message should appear too.
+
+ final List<String> expected = Arrays.asList(Arrays.copyOfRange(messages, 1, messages.length));
+ if (!testgetglobal.HandlerImpl.received.equals(expected)) {
+ throw new Error("Unexpected message list: "+testgetglobal.HandlerImpl.received+" vs "+ expected);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/logging/Logger/getGlobal/TestGetGlobalByName.java Wed Jul 03 14:35:10 2013 -0700
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.util.Arrays;
+import java.util.List;
+import java.util.logging.Logger;
+
+/**
+ * @test
+ * @bug 7184195
+ * @summary checks that java.util.logging.Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).info() logs without configuration
+ * @build TestGetGlobalByName testgetglobal.HandlerImpl testgetglobal.LogManagerImpl1 testgetglobal.LogManagerImpl2 testgetglobal.LogManagerImpl3 testgetglobal.BadLogManagerImpl testgetglobal.DummyLogManagerImpl
+ * @run main/othervm/timeout=10 TestGetGlobalByName
+ * @run main/othervm/timeout=10/policy=policy -Djava.security.manager TestGetGlobalByName
+ * @run main/othervm/timeout=10 -Djava.util.logging.manager=testgetglobal.LogManagerImpl1 TestGetGlobalByName
+ * @run main/othervm/timeout=10/policy=policy -Djava.security.manager -Djava.util.logging.manager=testgetglobal.LogManagerImpl TestGetGlobalByName
+ * @run main/othervm/timeout=10 -Djava.util.logging.manager=testgetglobal.LogManagerImpl2 TestGetGlobalByName
+ * @run main/othervm/timeout=10/policy=policy -Djava.security.manager -Djava.util.logging.manager=testgetglobal.LogManagerImpl2 TestGetGlobalByName
+ * @run main/othervm/timeout=10 -Djava.util.logging.manager=testgetglobal.LogManagerImpl3 TestGetGlobalByName
+ * @run main/othervm/timeout=10/policy=policy -Djava.security.manager -Djava.util.logging.manager=testgetglobal.LogManagerImpl3 TestGetGlobalByName
+ * @run main/othervm/timeout=10 -Djava.util.logging.manager=testgetglobal.BadLogManagerImpl TestGetGlobalByName
+ * @run main/othervm/timeout=10/policy=policy -Djava.security.manager -Djava.util.logging.manager=testgetglobal.BadLogManagerImpl TestGetGlobalByName
+ * @run main/othervm/timeout=10 -Djava.util.logging.manager=testgetglobal.DummyLogManagerImpl TestGetGlobalByName
+ * @run main/othervm/timeout=10/policy=policy -Djava.security.manager -Djava.util.logging.manager=testgetglobal.DummyLogManagerImpl TestGetGlobalByName
+ * @author danielfuchs
+ */
+public class TestGetGlobalByName {
+
+ final static String[] messages = {
+ "1. This message should not appear on the console.",
+ "2. This message should appear on the console.",
+ "3. This message should now appear on the console too."
+ };
+
+ static {
+ System.setProperty("java.util.logging.config.file",
+ System.getProperty("test.src", ".") + java.io.File.separator + "logging.properties");
+ }
+
+ public static void main(String... args) {
+
+ Logger.global.info(messages[0]); // at this point LogManager is not
+ // initialized yet, so this message should not appear.
+ Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).info(messages[1]); // calling getLogger() will
+ // initialize the LogManager - and thus this message should appear.
+ Logger.global.info(messages[2]); // Now that the LogManager is
+ // initialized, this message should appear too.
+
+ final List<String> expected = Arrays.asList(Arrays.copyOfRange(messages, 1, messages.length));
+ if (!testgetglobal.HandlerImpl.received.equals(expected)) {
+ throw new Error("Unexpected message list: "+testgetglobal.HandlerImpl.received+" vs "+ expected);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/logging/Logger/getGlobal/TestGetGlobalConcurrent.java Wed Jul 03 14:35:10 2013 -0700
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.util.Arrays;
+import java.util.List;
+import java.util.logging.Logger;
+
+/**
+ * @test
+ * @bug 7184195
+ * @summary checks that java.util.logging.Logger.getGlobal().info() logs without configuration
+ * @build TestGetGlobalConcurrent testgetglobal.HandlerImpl testgetglobal.LogManagerImpl1 testgetglobal.LogManagerImpl2 testgetglobal.LogManagerImpl3 testgetglobal.BadLogManagerImpl testgetglobal.DummyLogManagerImpl
+ * @run main/othervm/timeout=10 TestGetGlobalConcurrent
+ * @run main/othervm/timeout=10/policy=policy -Djava.security.manager TestGetGlobalConcurrent
+ * @run main/othervm/timeout=10 -Djava.util.logging.manager=testgetglobal.LogManagerImpl TestGetGlobalConcurrent
+ * @run main/othervm/timeout=10/policy=policy -Djava.security.manager -Djava.util.logging.manager=testgetglobal.LogManagerImpl TestGetGlobalConcurrent
+ * @run main/othervm/timeout=10 -Djava.util.logging.manager=testgetglobal.LogManagerImpl2 TestGetGlobalConcurrent
+ * @run main/othervm/timeout=10/policy=policy -Djava.security.manager -Djava.util.logging.manager=testgetglobal.LogManagerImpl2 TestGetGlobalConcurrent
+ * @run main/othervm/timeout=10 -Djava.util.logging.manager=testgetglobal.LogManagerImpl3 TestGetGlobalConcurrent
+ * @run main/othervm/timeout=10/policy=policy -Djava.security.manager -Djava.util.logging.manager=testgetglobal.LogManagerImpl3 TestGetGlobalConcurrent
+ * @run main/othervm/timeout=10 -Djava.util.logging.manager=testgetglobal.BadLogManagerImpl TestGetGlobalConcurrent
+ * @run main/othervm/timeout=10/policy=policy -Djava.security.manager -Djava.util.logging.manager=testgetglobal.BadLogManagerImpl TestGetGlobalConcurrent
+ * @run main/othervm/timeout=10 -Djava.util.logging.manager=testgetglobal.DummyLogManagerImpl TestGetGlobalConcurrent
+ * @run main/othervm/timeout=10/policy=policy -Djava.security.manager -Djava.util.logging.manager=testgetglobal.DummyLogManagerImpl TestGetGlobalConcurrent
+ * @author danielfuchs
+ */
+public class TestGetGlobalConcurrent {
+
+ final static String[] messages = {
+ "1. This message should not appear on the console.",
+ "2. This message should appear on the console.",
+ "3. This message should now appear on the console too.",
+ "4. This message should appear on the console.",
+ "5. This message should now appear on the console too.",
+ "6. This message should appear on the console.",
+ "7. This message should now appear on the console too.",
+ "8. This message should appear on the console.",
+ "9. This message should now appear on the console too."
+ };
+
+ static {
+ System.setProperty("java.util.logging.config.file",
+ System.getProperty("test.src", ".") + java.io.File.separator + "logging.properties");
+ }
+
+ public static void test1() {
+ final int nb = 1;
+ final int i = 2*nb + 1;
+ Logger.getGlobal().info(messages[i]); // calling getGlobal() will
+ // initialize the LogManager - and thus this message should appear.
+ Logger.global.info(messages[i+1]); // Now that the LogManager is
+ // initialized, this message should appear too.
+
+ final List<String> expected = Arrays.asList(Arrays.copyOfRange(messages, i, i+2));
+ if (!testgetglobal.HandlerImpl.received.containsAll(expected)) {
+ fail(new Error("Unexpected message list: "+testgetglobal.HandlerImpl.received+" vs "+ expected));
+ }
+ }
+ public static void test2() {
+ final int nb = 2;
+ final int i = 2*nb + 1;
+ Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).info(messages[i]); // calling getGlobal() will
+ // initialize the LogManager - and thus this message should appear.
+ Logger.global.info(messages[i+1]); // Now that the LogManager is
+ // initialized, this message should appear too.
+
+ final List<String> expected = Arrays.asList(Arrays.copyOfRange(messages, i, i+2));
+ if (!testgetglobal.HandlerImpl.received.containsAll(expected)) {
+ fail(new Error("Unexpected message list: "+testgetglobal.HandlerImpl.received+" vs "+ expected));
+ }
+ }
+ public static void test3() {
+ final int nb = 3;
+ final int i = 2*nb + 1;
+ java.util.logging.LogManager.getLogManager();
+ Logger.getGlobal().info(messages[i]); // calling getGlobal() will
+ // initialize the LogManager - and thus this message should appear.
+ Logger.global.info(messages[i+1]); // Now that the LogManager is
+ // initialized, this message should appear too.
+
+ final List<String> expected = Arrays.asList(Arrays.copyOfRange(messages, i, i+2));
+ if (!testgetglobal.HandlerImpl.received.containsAll(expected)) {
+ fail(new Error("Unexpected message list: "+testgetglobal.HandlerImpl.received+" vs "+ expected));
+ }
+ }
+ public static void test4() {
+ log = new MyLogger("foo.bar");
+ java.util.logging.LogManager.getLogManager().addLogger(log);
+ }
+
+
+ private static volatile Throwable failed = null;
+ private static volatile Logger log = null;
+
+ public static class MyLogger extends Logger {
+ public MyLogger(String name) {
+ super(name, null);
+ }
+ }
+
+ public static void fail(Throwable failure) {
+ failure.printStackTrace();
+ if (failed == null) failed = failure;
+ }
+
+ public static class WaitAndRun implements Runnable {
+ private final Runnable run;
+ public WaitAndRun(Runnable run) {
+ this.run = run;
+ }
+ public void run() {
+ try {
+ Thread.sleep(10);
+ run.run();
+ } catch (Exception | Error x) {
+ fail(x);
+ }
+ }
+ }
+
+ final static class Run1 implements Runnable {
+ public void run() { test1(); }
+ }
+ final static class Run2 implements Runnable {
+ public void run() { test2(); }
+ }
+ final static class Run3 implements Runnable {
+ public void run() { test3(); }
+ }
+ final static class Run4 implements Runnable {
+ public void run() { test4(); }
+ }
+
+ public static void main(String... args) throws Exception {
+
+ final Thread t1 = new Thread(new WaitAndRun(new Run1()), "test1");
+ final Thread t2 = new Thread(new WaitAndRun(new Run2()), "test2");
+ final Thread t3 = new Thread(new WaitAndRun(new Run3()), "test3");
+ final Thread t4 = new Thread(new WaitAndRun(new Run4()), "test4");
+
+ t1.setDaemon(true); t2.setDaemon(true); t3.setDaemon(true); t4.setDaemon(true);
+ t1.start(); t2.start(); t3.start(); t4.start();
+
+ Thread.sleep(10);
+
+ Logger.getGlobal().info(messages[1]); // calling getGlobal() will
+ // initialize the LogManager - and thus this message should appear.
+ Logger.global.info(messages[2]); // Now that the LogManager is
+ // initialized, this message should appear too.
+
+ final List<String> expected = Arrays.asList(Arrays.copyOfRange(messages, 1, 3));
+ if (!testgetglobal.HandlerImpl.received.containsAll(expected)) {
+ throw new Error("Unexpected message list: "+testgetglobal.HandlerImpl.received+" vs "+ expected);
+ }
+
+
+ t1.join(); t2.join(); t3.join(); t4.join();
+
+ if (failed != null) {
+ throw new Error("Test failed.", failed);
+ }
+
+ System.out.println("Test passed");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/logging/Logger/getGlobal/logging.properties Wed Jul 03 14:35:10 2013 -0700
@@ -0,0 +1,2 @@
+.level=INFO
+handlers=testgetglobal.HandlerImpl
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/logging/Logger/getGlobal/policy Wed Jul 03 14:35:10 2013 -0700
@@ -0,0 +1,7 @@
+grant {
+ permission java.util.PropertyPermission "java.util.logging.config.file", "write";
+ permission java.util.PropertyPermission "test.src", "read";
+ permission java.lang.RuntimePermission "setContextClassLoader";
+ permission java.lang.RuntimePermission "shutdownHooks";
+ permission java.util.logging.LoggingPermission "control";
+};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/logging/Logger/getGlobal/testgetglobal/BadLogManagerImpl.java Wed Jul 03 14:35:10 2013 -0700
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package testgetglobal;
+
+import java.util.logging.LogManager;
+import java.util.logging.Logger;
+
+/**
+ * This class is used to verify that calling Logger.getLogger(Logger.GLOBAL_LOGGER_NAME)
+ * in the constructor of a LogManager subclass installed as default
+ * LogManager does not cause issues beyond throwing the expected NPE.
+ * In that case the default LogManager class will simply be used.
+ * @author danielfuchs
+ */
+public class BadLogManagerImpl extends LogManager {
+
+ final Logger globalLogger;
+ public BadLogManagerImpl() {
+ // The call below should generate an NPE, which will be
+ // catched in LogManager initializer.
+ globalLogger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
+ System.err.println("Global is: " + globalLogger);
+ throw new Error("Should not have reached here");
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/logging/Logger/getGlobal/testgetglobal/DummyLogManagerImpl.java Wed Jul 03 14:35:10 2013 -0700
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package testgetglobal;
+
+import java.util.logging.LogManager;
+import java.util.logging.Logger;
+
+/**
+ * A dummy LogManager subclass that does nothing beyond extending LogManager.
+ * @author danielfuchs
+ */
+public class DummyLogManagerImpl extends LogManager {
+
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/logging/Logger/getGlobal/testgetglobal/HandlerImpl.java Wed Jul 03 14:35:10 2013 -0700
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package testgetglobal;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.List;
+import java.util.logging.ConsoleHandler;
+import java.util.logging.LogRecord;
+
+/**
+ *
+ * @author danielfuchs
+ */
+public class HandlerImpl extends ConsoleHandler {
+
+ public final static List<String> received = new CopyOnWriteArrayList<>();
+
+ public HandlerImpl() {
+ }
+
+ @Override
+ public void publish(LogRecord record) {
+ received.add(record.getMessage());
+ super.publish(record);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/logging/Logger/getGlobal/testgetglobal/LogManagerImpl1.java Wed Jul 03 14:35:10 2013 -0700
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package testgetglobal;
+
+import java.util.logging.LogManager;
+import java.util.logging.Logger;
+
+/**
+ * This class is used to verify that calling Logger.getGlobal() in the static
+ * initializer of a LogManager subclass installed as default LogManager
+ * does not cause issues.
+ * @author danielfuchs
+ */
+public class LogManagerImpl1 extends LogManager {
+
+ static final Logger global;
+ static {
+ global = Logger.getGlobal();
+ System.err.println("Global is: " + global);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/logging/Logger/getGlobal/testgetglobal/LogManagerImpl2.java Wed Jul 03 14:35:10 2013 -0700
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package testgetglobal;
+
+import java.util.logging.LogManager;
+import java.util.logging.Logger;
+
+/**
+ * This class is used to verify that calling Logger.getGlobal() in the constructor
+ * initializer of a LogManager subclass installed as default LogManager
+ * does not cause issues.
+ * @author danielfuchs
+ */
+public class LogManagerImpl2 extends LogManager {
+
+ final Logger globalLogger;
+ public LogManagerImpl2() {
+ globalLogger = Logger.getGlobal();
+ System.err.println("Global is: " + globalLogger);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/logging/Logger/getGlobal/testgetglobal/LogManagerImpl3.java Wed Jul 03 14:35:10 2013 -0700
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package testgetglobal;
+
+import java.util.logging.LogManager;
+import java.util.logging.Logger;
+
+/**
+ * This class is used to verify that calling Logger.getLogger(Logger.GLOBAL_LOGGER_NAME)
+ * in the static initializer of a LogManager subclass installed as default
+ * LogManager does not cause issues beyond throwing the expected NPE.
+ * @author danielfuchs
+ */
+public class LogManagerImpl3 extends LogManager {
+
+ static final Logger global;
+ static {
+ Logger g = null;
+ try {
+ g = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
+ throw new Error("Should not have reached here");
+ } catch (Exception x) {
+ // This is to be expected: Logger.getLogger(Logger.GLOBAL_LOGGER_NAME)
+ // will call LogManager.getLogManager() which will return null, since
+ // we haven't manage to do new LogManagerImpl3() yet.
+ //
+ System.err.println("Got expected exception - you cannot call"
+ + " Logger.getLogger(Logger.GLOBAL_LOGGER_NAME)"
+ + " in LogManager subclass static initializer: " + x);
+ x.printStackTrace();
+ }
+ if (g == null) {
+ g = Logger.getGlobal();
+ }
+ global = g;
+ System.err.println("Global is: " + global);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/logging/TestAppletLoggerContext.java Wed Jul 03 14:35:10 2013 -0700
@@ -0,0 +1,610 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+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.EnumSet;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.LogManager;
+import java.util.logging.Logger;
+import java.util.logging.LoggingPermission;
+import sun.misc.JavaAWTAccess;
+import sun.misc.SharedSecrets;
+
+/*
+ * @test
+ * @bug 8017174 8010727
+ * @summary NPE when using Logger.getAnonymousLogger or
+ * LogManager.getLogManager().getLogger
+ *
+ * @run main/othervm -Dtest.security=off TestAppletLoggerContext LoadingApplet
+ * @run main/othervm -Dtest.security=on TestAppletLoggerContext LoadingApplet
+ * @run main/othervm -Dtest.security=off TestAppletLoggerContext LoadingMain
+ * @run main/othervm -Dtest.security=on TestAppletLoggerContext LoadingMain
+ * @run main/othervm -Dtest.security=off TestAppletLoggerContext One
+ * @run main/othervm -Dtest.security=on TestAppletLoggerContext One
+ * @run main/othervm -Dtest.security=off TestAppletLoggerContext Two
+ * @run main/othervm -Dtest.security=on TestAppletLoggerContext Two
+ * @run main/othervm -Dtest.security=off TestAppletLoggerContext Three
+ * @run main/othervm -Dtest.security=on TestAppletLoggerContext Three
+ * @run main/othervm -Dtest.security=off TestAppletLoggerContext Four
+ * @run main/othervm -Dtest.security=on TestAppletLoggerContext Four
+ * @run main/othervm -Dtest.security=off TestAppletLoggerContext Five
+ * @run main/othervm -Dtest.security=on TestAppletLoggerContext Five
+ * @run main/othervm -Dtest.security=off TestAppletLoggerContext Six
+ * @run main/othervm -Dtest.security=on TestAppletLoggerContext Six
+ * @run main/othervm -Dtest.security=off TestAppletLoggerContext Seven
+ * @run main/othervm -Dtest.security=on TestAppletLoggerContext Seven
+ * @run main/othervm -Dtest.security=off TestAppletLoggerContext
+ * @run main/othervm -Dtest.security=on TestAppletLoggerContext
+ */
+
+// NOTE: We run in other VM in order to 1. switch security manager and 2. cause
+// LogManager class to be loaded anew.
+public class TestAppletLoggerContext {
+
+ // Avoids the hassle of dealing with files and system props...
+ static class SimplePolicy extends Policy {
+ private final Permissions perms;
+ public SimplePolicy(Permission... permissions) {
+ perms = new Permissions();
+ for (Permission permission : permissions) {
+ perms.add(permission);
+ }
+ }
+ @Override
+ public PermissionCollection getPermissions(CodeSource cs) {
+ return perms;
+ }
+ @Override
+ public PermissionCollection getPermissions(ProtectionDomain pd) {
+ return perms;
+ }
+ @Override
+ public boolean implies(ProtectionDomain pd, Permission p) {
+ return perms.implies(p);
+ }
+ }
+
+ // The bridge class initializes the logging system.
+ // It stubs the applet context in order to simulate context changes.
+ //
+ public static class Bridge {
+
+ private static class JavaAWTAccessStub implements JavaAWTAccess {
+ boolean active = true;
+
+ private static class TestExc {
+ private final Map<Object, Object> map = new HashMap<>();
+ void put(Object key, Object v) { map.put(key, v); }
+ Object get(Object key) { return map.get(key); }
+ void remove(Object o) { map.remove(o); }
+ public static TestExc exc(Object o) {
+ return TestExc.class.cast(o);
+ }
+ }
+
+ TestExc exc;
+ TestExc global = new TestExc();
+
+ @Override
+ public Object getContext() { return active ? global : null; }
+ @Override
+ public Object getExecutionContext() { return active ? exc : null; }
+ @Override
+ public Object get(Object o, Object o1) { return TestExc.exc(o).get(o1); }
+ @Override
+ public void put(Object o, Object o1, Object o2) { TestExc.exc(o).put(o1, o2); }
+ @Override
+ public void remove(Object o, Object o1) { TestExc.exc(o).remove(o1); }
+ @Override
+ public Object get(Object o) { return global.get(o); }
+ @Override
+ public void put(Object o, Object o1) { global.put(o, o1); }
+ @Override
+ public void remove(Object o) { global.remove(o); }
+ @Override
+ public boolean isDisposed() { return false; }
+ @Override
+ public boolean isMainAppContext() { return exc == null; }
+ }
+
+ final static JavaAWTAccessStub javaAwtAccess = new JavaAWTAccessStub();
+ public static void init() {
+ SharedSecrets.setJavaAWTAccess(javaAwtAccess);
+ if (System.getProperty("test.security", "on").equals("on")) {
+ Policy p = new SimplePolicy(new LoggingPermission("control", null),
+ new RuntimePermission("setContextClassLoader"),
+ new RuntimePermission("shutdownHooks"));
+ Policy.setPolicy(p);
+ System.setSecurityManager(new SecurityManager());
+ }
+ }
+
+ public static void changeContext() {
+ System.out.println("... Switching to a new applet context ...");
+ javaAwtAccess.active = true;
+ javaAwtAccess.exc = new JavaAWTAccessStub.TestExc();
+ }
+
+ public static void desactivate() {
+ System.out.println("... Running with no applet context ...");
+ javaAwtAccess.exc = null;
+ javaAwtAccess.active = false;
+ }
+
+ public static class CustomAnonymousLogger extends Logger {
+ public CustomAnonymousLogger() {
+ this("");
+ }
+ public CustomAnonymousLogger(String name) {
+ super(null, null);
+ System.out.println( " LogManager: " +LogManager.getLogManager());
+ System.out.println( " getLogger: " +LogManager.getLogManager().getLogger(name));
+ setParent(LogManager.getLogManager().getLogger(name));
+ }
+ }
+
+ public static class CustomLogger extends Logger {
+ CustomLogger(String name) {
+ super(name, null);
+ }
+ }
+ }
+
+ public static enum TestCase {
+ LoadingApplet, LoadingMain, One, Two, Three, Four, Five, Six, Seven;
+ public void test() {
+ switch(this) {
+ // When run - each of these two tests must be
+ // run before any other tests and before each other.
+ case LoadingApplet: testLoadingApplet(); break;
+ case LoadingMain: testLoadingMain(); break;
+ case One: testOne(); break;
+ case Two: testTwo(); break;
+ case Three: testThree(); break;
+ case Four: testFour(); break;
+ case Five: testFive(); break;
+ case Six: testSix(); break;
+ case Seven: testSeven(); break;
+ }
+ }
+ public String describe() {
+ switch(this) {
+ case LoadingApplet:
+ return "Test that when the LogManager class is"
+ + " loaded in an applet thread first,"
+ + "\n all LoggerContexts are correctly initialized";
+ case LoadingMain:
+ return "Test that when the LogManager class is"
+ + " loaded in the main thread first,"
+ + "\n all LoggerContexts are correctly initialized";
+ case One:
+ return "Test that Logger.getAnonymousLogger()"
+ + " and new CustomAnonymousLogger() don't throw NPE";
+ case Two:
+ return "Test that Logger.getLogger(\"\")"
+ + " does not return null nor throws NPE";
+ case Three:
+ return "Test that LogManager.getLogManager().getLogger(\"\")"
+ + " does not return null nor throws NPE";
+ case Four:
+ return "Test that Logger.getLogger(Logger.GLOBAL_LOGGER_NAME)"
+ + " does not return null,\n and that"
+ + " new CustomAnonymousLogger(Logger.GLOBAL_LOGGER_NAME)"
+ + " does not throw NPE";
+ case Five:
+ return "Test that LogManager.getLogManager().getLogger(Logger.GLOBAL_LOGGER_NAME)"
+ + "\n does not return null nor throws NPE";
+ case Six:
+ return "Test that manager.getLogger(Logger.GLOBAL_LOGGER_NAME)"
+ + " returns null\n when manager is not the default"
+ + " LogManager instance.\n"
+ + "Test adding a new logger named \"global\" in that"
+ + " non default instance.";
+ case Seven: return "Test that manager.getLogger(\"\")"
+ + " returns null\n when manager is not the default"
+ + " LogManager instance.\n"
+ + "Test adding a new logger named \"\" in that"
+ + " non default instance.";
+ default: return "Undefined";
+ }
+ }
+ };
+
+ /**
+ * @param args the command line arguments
+ */
+ public static void main(String[] args) {
+ Bridge.init();
+ EnumSet<TestCase> tests = EnumSet.noneOf(TestCase.class);
+ for (String arg : args) {
+ tests.add(TestCase.valueOf(arg));
+ }
+ if (args.length == 0) {
+ tests = EnumSet.complementOf(EnumSet.of(TestCase.LoadingMain));
+ }
+ final EnumSet<TestCase> loadingTests =
+ EnumSet.of(TestCase.LoadingApplet, TestCase.LoadingMain);
+ int testrun = 0;
+ for (TestCase test : tests) {
+ if (loadingTests.contains(test)) {
+ if (testrun > 0) {
+ throw new UnsupportedOperationException("Test case "
+ + test + " must be executed first!");
+ }
+ }
+ System.out.println("Testing "+ test+": ");
+ System.out.println(test.describe());
+ try {
+ test.test();
+ } catch (Exception x) {
+ throw new Error(String.valueOf(test)
+ + (System.getSecurityManager() == null ? " without " : " with ")
+ + "security failed: "+x+"\n "+"FAILED: "+test.describe()+"\n", x);
+ } finally {
+ testrun++;
+ }
+ Bridge.changeContext();
+ System.out.println("PASSED: "+ test);
+ }
+ }
+
+ public static void testLoadingApplet() {
+ Bridge.changeContext();
+
+ Logger bar = new Bridge.CustomLogger("com.foo.Bar");
+ LogManager.getLogManager().addLogger(bar);
+ assertNotNull(bar.getParent());
+ testParent(bar);
+ testParent(LogManager.getLogManager().getLogger("global"));
+ testParent(LogManager.getLogManager().getLogger(bar.getName()));
+
+ Bridge.desactivate();
+
+ Logger foo = new Bridge.CustomLogger("com.foo.Foo");
+ boolean b = LogManager.getLogManager().addLogger(foo);
+ assertEquals(Boolean.TRUE, Boolean.valueOf(b));
+ assertNotNull(foo.getParent());
+ testParent(foo);
+ testParent(LogManager.getLogManager().getLogger("global"));
+ testParent(LogManager.getLogManager().getLogger(foo.getName()));
+ }
+
+ public static void testLoadingMain() {
+ Bridge.desactivate();
+
+ Logger bar = new Bridge.CustomLogger("com.foo.Bar");
+ LogManager.getLogManager().addLogger(bar);
+ assertNotNull(bar.getParent());
+ testParent(bar);
+ testParent(LogManager.getLogManager().getLogger("global"));
+ testParent(LogManager.getLogManager().getLogger(bar.getName()));
+
+ Bridge.changeContext();
+
+ Logger foo = new Bridge.CustomLogger("com.foo.Foo");
+ boolean b = LogManager.getLogManager().addLogger(foo);
+ assertEquals(Boolean.TRUE, Boolean.valueOf(b));
+ assertNotNull(foo.getParent());
+ testParent(foo);
+ testParent(LogManager.getLogManager().getLogger("global"));
+ testParent(LogManager.getLogManager().getLogger(foo.getName()));
+
+ }
+
+ public static void testOne() {
+ for (int i=0; i<3 ; i++) {
+ Logger logger1 = Logger.getAnonymousLogger();
+ Logger logger1b = Logger.getAnonymousLogger();
+ Bridge.changeContext();
+ Logger logger2 = Logger.getAnonymousLogger();
+ Logger logger2b = Logger.getAnonymousLogger();
+ Bridge.changeContext();
+ Logger logger3 = new Bridge.CustomAnonymousLogger();
+ Logger logger3b = new Bridge.CustomAnonymousLogger();
+ Bridge.changeContext();
+ Logger logger4 = new Bridge.CustomAnonymousLogger();
+ Logger logger4b = new Bridge.CustomAnonymousLogger();
+ }
+ }
+
+
+ public static void testTwo() {
+ for (int i=0; i<3 ; i++) {
+ Logger logger1 = Logger.getLogger("");
+ Logger logger1b = Logger.getLogger("");
+ assertNotNull(logger1);
+ assertNotNull(logger1b);
+ assertEquals(logger1, logger1b);
+ Bridge.changeContext();
+ Logger logger2 = Logger.getLogger("");
+ Logger logger2b = Logger.getLogger("");
+ assertNotNull(logger2);
+ assertNotNull(logger2b);
+ assertEquals(logger2, logger2b);
+ assertEquals(logger1, logger2);
+ }
+ }
+
+ public static void testThree() {
+ for (int i=0; i<3 ; i++) {
+ Logger logger1 = LogManager.getLogManager().getLogger("");
+ Logger logger1b = LogManager.getLogManager().getLogger("");
+ assertNotNull(logger1);
+ assertNotNull(logger1b);
+ assertEquals(logger1, logger1b);
+ Bridge.changeContext();
+ Logger logger2 = LogManager.getLogManager().getLogger("");
+ Logger logger2b = LogManager.getLogManager().getLogger("");
+ assertNotNull(logger2);
+ assertNotNull(logger2b);
+ assertEquals(logger2, logger2b);
+ assertEquals(logger1, logger2);
+ }
+ }
+
+ public static void testFour() {
+ for (int i=0; i<3 ; i++) {
+ Logger logger1 = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
+ Logger logger1b = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
+ assertNotNull(logger1);
+ assertNotNull(logger1b);
+ assertEquals(logger1, logger1b);
+ Bridge.changeContext();
+
+ Logger logger2 = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
+ Logger logger2b = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
+ assertNotNull(logger2);
+ assertNotNull(logger2b);
+ assertEquals(logger2, logger2b);
+
+ assertEquals(logger1, logger2);
+
+ Bridge.changeContext();
+ Logger logger3 = new Bridge.CustomAnonymousLogger(Logger.GLOBAL_LOGGER_NAME);
+ Logger logger3b = new Bridge.CustomAnonymousLogger(Logger.GLOBAL_LOGGER_NAME);
+ Bridge.changeContext();
+ Logger logger4 = new Bridge.CustomAnonymousLogger(Logger.GLOBAL_LOGGER_NAME);
+ Logger logger4b = new Bridge.CustomAnonymousLogger(Logger.GLOBAL_LOGGER_NAME);
+ }
+ }
+
+ public static void testFive() {
+ for (int i=0; i<3 ; i++) {
+ Logger logger1 = LogManager.getLogManager().getLogger(Logger.GLOBAL_LOGGER_NAME);
+ Logger logger1b = LogManager.getLogManager().getLogger(Logger.GLOBAL_LOGGER_NAME);
+ assertNotNull(logger1);
+ assertNotNull(logger1b);
+ assertEquals(logger1, logger1b);
+
+ Bridge.changeContext();
+
+ Logger logger2 = LogManager.getLogManager().getLogger(Logger.GLOBAL_LOGGER_NAME);
+ Logger logger2b = LogManager.getLogManager().getLogger(Logger.GLOBAL_LOGGER_NAME);
+ assertNotNull(logger2);
+ assertNotNull(logger2b);
+ assertEquals(logger2, logger2b);
+
+ assertEquals(logger1, logger2);
+ }
+ }
+
+ /**
+ * This test is designed to test the behavior of additional LogManager instances.
+ * It must be noted that if the security manager is off, then calling
+ * Bridge.changeContext() has actually no effect - which explains why we have
+ * some differences between the cases security manager on & security manager
+ * off.
+ **/
+ public static void testSix() {
+ for (int i=0; i<3 ; i++) {
+ Bridge.desactivate();
+ LogManager manager = new LogManager() {};
+ Logger logger1 = manager.getLogger(Logger.GLOBAL_LOGGER_NAME);
+ Logger logger1b = manager.getLogger(Logger.GLOBAL_LOGGER_NAME);
+ assertNull(logger1);
+ assertNull(logger1b);
+ Logger global = new Bridge.CustomLogger(Logger.GLOBAL_LOGGER_NAME);
+ manager.addLogger(global);
+ Logger logger2 = manager.getLogger(Logger.GLOBAL_LOGGER_NAME);
+ Logger logger2b = manager.getLogger(Logger.GLOBAL_LOGGER_NAME);
+ assertNotNull(logger2);
+ assertNotNull(logger2b);
+ assertEquals(logger2, global);
+ assertEquals(logger2b, global);
+ assertNull(manager.getLogger(""));
+ assertNull(manager.getLogger(""));
+
+ Bridge.changeContext();
+
+ // this is not a supported configuration:
+ // We are in an applet context with several log managers.
+ // We however need to check our assumptions...
+
+ // Applet context => root logger and global logger are not null.
+ // root == LogManager.getLogManager().rootLogger
+ // global == Logger.global
+
+ Logger logger3 = manager.getLogger(Logger.GLOBAL_LOGGER_NAME);
+ Logger logger3b = manager.getLogger(Logger.GLOBAL_LOGGER_NAME);
+ assertNotNull(logger3);
+ assertNotNull(logger3b);
+ Logger expected = (System.getSecurityManager() != null
+ ? Logger.getGlobal()
+ : global);
+ assertEquals(logger3, expected); // in applet context, we will not see
+ // the LogManager's custom global logger added above...
+ assertEquals(logger3b, expected); // in applet context, we will not see
+ // the LogManager's custom global logger added above...
+ Logger global2 = new Bridge.CustomLogger(Logger.GLOBAL_LOGGER_NAME);
+ manager.addLogger(global2); // adding a global logger will not work in applet context
+ // we will always get back the global logger.
+ // this could be considered as a bug...
+ Logger logger4 = manager.getLogger(Logger.GLOBAL_LOGGER_NAME);
+ Logger logger4b = manager.getLogger(Logger.GLOBAL_LOGGER_NAME);
+ assertNotNull(logger4);
+ assertNotNull(logger4b);
+ assertEquals(logger4, expected); // adding a global logger will not work in applet context
+ assertEquals(logger4b, expected); // adding a global logger will not work in applet context
+
+ Logger logger5 = manager.getLogger("");
+ Logger logger5b = manager.getLogger("");
+ Logger expectedRoot = (System.getSecurityManager() != null
+ ? LogManager.getLogManager().getLogger("")
+ : null);
+ assertEquals(logger5, expectedRoot);
+ assertEquals(logger5b, expectedRoot);
+
+ }
+ }
+
+ /**
+ * This test is designed to test the behavior of additional LogManager instances.
+ * It must be noted that if the security manager is off, then calling
+ * Bridge.changeContext() has actually no effect - which explains why we have
+ * some differences between the cases security manager on & security manager
+ * off.
+ **/
+ public static void testSeven() {
+ for (int i=0; i<3 ; i++) {
+ Bridge.desactivate();
+ LogManager manager = new LogManager() {};
+ Logger logger1 = manager.getLogger("");
+ Logger logger1b = manager.getLogger("");
+ assertNull(logger1);
+ assertNull(logger1b);
+ Logger global = new Bridge.CustomLogger(Logger.GLOBAL_LOGGER_NAME);
+ manager.addLogger(global);
+ Logger logger2 = manager.getLogger(Logger.GLOBAL_LOGGER_NAME);
+ Logger logger2b = manager.getLogger(Logger.GLOBAL_LOGGER_NAME);
+ assertNotNull(logger2);
+ assertNotNull(logger2b);
+ assertEquals(logger2, global);
+ assertEquals(logger2b, global);
+ Logger logger3 = manager.getLogger("");
+ Logger logger3b = manager.getLogger("");
+ assertNull(logger3);
+ assertNull(logger3b);
+ Logger root = new Bridge.CustomLogger("");
+ manager.addLogger(root);
+ Logger logger4 = manager.getLogger("");
+ Logger logger4b = manager.getLogger("");
+ assertNotNull(logger4);
+ assertNotNull(logger4b);
+ assertEquals(logger4, root);
+ assertEquals(logger4b, root);
+
+ Bridge.changeContext();
+
+ // this is not a supported configuration:
+ // We are in an applet context with several log managers.
+ // We haowever need to check our assumptions...
+
+ // Applet context => root logger and global logger are not null.
+ // root == LogManager.getLogManager().rootLogger
+ // global == Logger.global
+
+ Logger logger5 = manager.getLogger("");
+ Logger logger5b = manager.getLogger("");
+ Logger expectedRoot = (System.getSecurityManager() != null
+ ? LogManager.getLogManager().getLogger("")
+ : root);
+
+ assertNotNull(logger5);
+ assertNotNull(logger5b);
+ assertEquals(logger5, expectedRoot);
+ assertEquals(logger5b, expectedRoot);
+ if (System.getSecurityManager() != null) {
+ assertNotEquals(logger5, root);
+ assertNotEquals(logger5b, root);
+ }
+
+ Logger global2 = new Bridge.CustomLogger(Logger.GLOBAL_LOGGER_NAME);
+ manager.addLogger(global2); // adding a global logger will not work in applet context
+ // we will always get back the global logger.
+ // this could be considered as a bug...
+ Logger logger6 = manager.getLogger(Logger.GLOBAL_LOGGER_NAME);
+ Logger logger6b = manager.getLogger(Logger.GLOBAL_LOGGER_NAME);
+ Logger expectedGlobal = (System.getSecurityManager() != null
+ ? Logger.getGlobal()
+ : global);
+ assertNotNull(logger6);
+ assertNotNull(logger6b);
+ assertEquals(logger6, expectedGlobal); // adding a global logger will not work in applet context
+ assertEquals(logger6b, expectedGlobal); // adding a global logger will not work in applet context
+
+ Logger root2 = new Bridge.CustomLogger("");
+ manager.addLogger(root2); // adding a root logger will not work in applet context
+ // we will always get back the default manager's root logger.
+ // this could be considered as a bug...
+ Logger logger7 = manager.getLogger("");
+ Logger logger7b = manager.getLogger("");
+ assertNotNull(logger7);
+ assertNotNull(logger7b);
+ assertEquals(logger7, expectedRoot); // adding a global logger will not work in applet context
+ assertEquals(logger7b, expectedRoot); // adding a global logger will not work in applet context
+ assertNotEquals(logger7, root2);
+ assertNotEquals(logger7b, root2);
+ }
+ }
+
+ public static void testParent(Logger logger) {
+ Logger l = logger;
+ while (l.getParent() != null) {
+ l = l.getParent();
+ }
+ assertEquals("", l.getName());
+ }
+
+ public static class TestError extends RuntimeException {
+ public TestError(String msg) {
+ super(msg);
+ }
+ }
+
+ public static void assertNotNull(Object obj) {
+ if (obj == null) throw new NullPointerException();
+ }
+
+ public static void assertNull(Object obj) {
+ if (obj != null) throw new TestError("Null expected, got "+obj);
+ }
+
+ public static void assertEquals(Object o1, Object o2) {
+ if (o1 != o2) {
+ throw new TestError(o1 + " != " + o2);
+ }
+ }
+
+ public static void assertNotEquals(Object o1, Object o2) {
+ if (o1 == o2) {
+ throw new TestError(o1 + " == " + o2);
+ }
+ }
+}
--- a/jdk/test/java/util/stream/test/org/openjdk/tests/java/util/stream/SliceOpTest.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/test/java/util/stream/test/org/openjdk/tests/java/util/stream/SliceOpTest.java Wed Jul 03 14:35:10 2013 -0700
@@ -26,13 +26,16 @@
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
import java.util.function.Function;
+import java.util.stream.Collectors;
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
import java.util.stream.LambdaTestHelpers;
import java.util.stream.LongStream;
import java.util.stream.OpTestCase;
import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
import java.util.stream.StreamTestDataProvider;
import java.util.stream.TestData;
@@ -192,6 +195,53 @@
}
}
+ public void testSkipLimitOpsWithNonSplittingSpliterator() {
+ class NonSplittingNotSubsizedOrderedSpliterator<T> implements Spliterator<T> {
+ Spliterator<T> s;
+
+ NonSplittingNotSubsizedOrderedSpliterator(Spliterator<T> s) {
+ assert s.hasCharacteristics(Spliterator.ORDERED);
+ this.s = s;
+ }
+
+ @Override
+ public boolean tryAdvance(Consumer<? super T> action) {
+ return s.tryAdvance(action);
+ }
+
+ @Override
+ public void forEachRemaining(Consumer<? super T> action) {
+ s.forEachRemaining(action);
+ }
+
+ @Override
+ public Spliterator<T> trySplit() {
+ return null;
+ }
+
+ @Override
+ public long estimateSize() {
+ return s.estimateSize();
+ }
+
+ @Override
+ public int characteristics() {
+ return s.characteristics() & ~(Spliterator.SUBSIZED);
+ }
+
+ @Override
+ public Comparator<? super T> getComparator() {
+ return s.getComparator();
+ }
+ }
+ List<Integer> list = IntStream.range(0, 100).boxed().collect(Collectors.toList());
+ TestData.OfRef<Integer> data = TestData.Factory.ofSupplier(
+ "Non splitting, not SUBSIZED, ORDERED, stream",
+ () -> StreamSupport.stream(new NonSplittingNotSubsizedOrderedSpliterator<>(list.spliterator())));
+
+ testSkipLimitOps("testSkipLimitOpsWithNonSplittingSpliterator", data);
+ }
+
@Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class)
public void testLimitOps(String name, TestData.OfRef<Integer> data) {
List<Integer> limits = sizes(data.size());
--- a/jdk/test/tools/pack200/AttributeTests.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/test/tools/pack200/AttributeTests.java Wed Jul 03 14:35:10 2013 -0700
@@ -37,6 +37,7 @@
public static void main(String... args) throws Exception {
test6746111();
testMethodParameters();
+ Utils.cleanup();
}
/*
--- a/jdk/test/tools/pack200/BandIntegrity.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/test/tools/pack200/BandIntegrity.java Wed Jul 03 14:35:10 2013 -0700
@@ -40,7 +40,7 @@
* the java packer and unpacker must be called in the same java instance.
*/
public class BandIntegrity {
- public static void main(String... args) throws IOException {
+ public static void main(String... args) throws IOException {
File testFile = new File("test.jar");
Utils.jar("cvf", testFile.getName(),
"-C", Utils.TEST_CLS_DIR.getAbsolutePath(),
@@ -56,6 +56,7 @@
Utils.createFile(configFile, scratch);
File outFile = new File("out.jar");
Utils.repack(testFile, outFile, true,
- "-v", "--config-file=" + configFile.getName());
+ "-v", "--config-file=" + configFile.getName());
+ Utils.cleanup();
}
}
--- a/jdk/test/tools/pack200/CommandLineTests.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/test/tools/pack200/CommandLineTests.java Wed Jul 03 14:35:10 2013 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 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
@@ -83,6 +83,11 @@
Utils.recursiveCopy(Utils.JavaSDK, EXP_SDK);
creatConfigFile();
}
+ // cleanup the test area
+ static void cleanup() throws IOException {
+ Utils.recursiveDelete(EXP_SDK);
+ Utils.cleanup();
+ }
// Hopefully, this should be kept in sync with what the installer does.
static void creatConfigFile() throws IOException {
@@ -172,6 +177,7 @@
init();
testJRE();
testJDK();
+ cleanup(); // cleanup only if we pass successfully
} catch (IOException ioe) {
throw new RuntimeException(ioe);
}
--- a/jdk/test/tools/pack200/InstructionTests.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/test/tools/pack200/InstructionTests.java Wed Jul 03 14:35:10 2013 -0700
@@ -35,6 +35,7 @@
public class InstructionTests {
public static void main(String... args) throws Exception {
testInvokeOpCodes();
+ Utils.cleanup();
}
/*
* the following should produce invokestatic and invokespecial
--- a/jdk/test/tools/pack200/Pack200Props.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/test/tools/pack200/Pack200Props.java Wed Jul 03 14:35:10 2013 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 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
@@ -31,6 +31,7 @@
*/
import java.io.File;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -45,11 +46,12 @@
public class Pack200Props {
- public static void main(String... args) {
+ public static void main(String... args) throws IOException {
verifyDefaults();
File out = new File("test" + Utils.PACK_FILE_EXT);
out.delete();
verifySegmentLimit(out);
+ Utils.cleanup();
}
static void verifySegmentLimit(File outFile) {
--- a/jdk/test/tools/pack200/Pack200Test.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/test/tools/pack200/Pack200Test.java Wed Jul 03 14:35:10 2013 -0700
@@ -66,7 +66,7 @@
}
}
- private static void doPackUnpack() {
+ private static void doPackUnpack() throws IOException {
for (File in : jarList) {
JarOutputStream javaUnpackerStream = null;
JarOutputStream nativeUnpackerStream = null;
@@ -117,12 +117,13 @@
Utils.close((Closeable) jarFile);
}
}
+ Utils.cleanup(); // cleanup artifacts, if successful run
}
/**
* @param args the command line arguments
*/
- public static void main(String[] args) {
+ public static void main(String[] args) throws IOException {
// select the jars carefully, adding more jars will increase the
// testing time, especially for jprt.
jarList.add(Utils.locateJar("tools.jar"));
--- a/jdk/test/tools/pack200/PackageVersionTest.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/test/tools/pack200/PackageVersionTest.java Wed Jul 03 14:35:10 2013 -0700
@@ -1,6 +1,5 @@
-
/*
- * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 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
@@ -54,7 +53,7 @@
public final static int JAVA7_PACKAGE_MAJOR_VERSION = 170;
public final static int JAVA7_PACKAGE_MINOR_VERSION = 1;
- public static void main(String... args) {
+ public static void main(String... args) throws IOException {
if (!javaHome.getName().endsWith("jre")) {
throw new RuntimeException("Error: requires an SDK to run");
}
@@ -78,6 +77,7 @@
// test for resource file, ie. no class files
verifyPack("Test6.java", JAVA5_PACKAGE_MAJOR_VERSION,
JAVA5_PACKAGE_MINOR_VERSION);
+ Utils.cleanup();
}
static void verify6991164() {
--- a/jdk/test/tools/pack200/RepackTest.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/test/tools/pack200/RepackTest.java Wed Jul 03 14:35:10 2013 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -36,6 +36,7 @@
public static void main(String... args) throws Exception {
testRepack();
+ Utils.cleanup();
}
/*
--- a/jdk/test/tools/pack200/T7007157.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/test/tools/pack200/T7007157.java Wed Jul 03 14:35:10 2013 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 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
@@ -63,5 +63,6 @@
Utils.close(fos);
Utils.close(jarFile);
}
+ Utils.cleanup();
}
}
--- a/jdk/test/tools/pack200/TestExceptions.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/test/tools/pack200/TestExceptions.java Wed Jul 03 14:35:10 2013 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 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
@@ -219,12 +219,13 @@
}
}
- public static void main(String... args) {
+ public static void main(String... args) throws IOException {
init();
pack200Test1();
pack200Test2();
pack200Test3();
unpack200Test1();
+ Utils.cleanup();
}
// containers for test inputs and management
--- a/jdk/test/tools/pack200/TimeStamp.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/test/tools/pack200/TimeStamp.java Wed Jul 03 14:35:10 2013 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 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
@@ -149,6 +149,7 @@
Utils.close(jf1);
Utils.close(jf2);
}
+ Utils.cleanup();
if (errors > 0) {
throw new RuntimeException("FAIL:" + errors + " error(s) encounted");
}
--- a/jdk/test/tools/pack200/UnpackerMemoryTest.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/test/tools/pack200/UnpackerMemoryTest.java Wed Jul 03 14:35:10 2013 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 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
@@ -81,6 +81,7 @@
Utils.close(fos);
}
}
+ Utils.cleanup();
}
}
--- a/jdk/test/tools/pack200/Utils.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/test/tools/pack200/Utils.java Wed Jul 03 14:35:10 2013 -0700
@@ -75,6 +75,7 @@
static final File TEST_CLS_DIR = new File(System.getProperty("test.classes"));
static final String VERIFIER_DIR_NAME = "pack200-verifier";
static final File VerifierJar = new File(VERIFIER_DIR_NAME + JAR_FILE_EXT);
+ static final File XCLASSES = new File("xclasses");
private Utils() {} // all static
@@ -95,8 +96,7 @@
}
List<File> javaFileList = findFiles(srcDir, createFilter(JAVA_FILE_EXT));
File tmpFile = File.createTempFile("javac", ".tmp");
- File classesDir = new File("xclasses");
- classesDir.mkdirs();
+ XCLASSES.mkdirs();
FileOutputStream fos = null;
PrintStream ps = null;
try {
@@ -111,14 +111,14 @@
}
compiler("-d",
- "xclasses",
+ XCLASSES.getName(),
"@" + tmpFile.getAbsolutePath());
jar("cvfe",
VerifierJar.getName(),
"sun.tools.pack.verify.Main",
"-C",
- "xclasses",
+ XCLASSES.getName(),
".");
}
@@ -175,6 +175,33 @@
};
}
+ /*
+ * clean up all the usual suspects
+ */
+ static void cleanup() throws IOException {
+ recursiveDelete(XCLASSES);
+ List<File> toDelete = new ArrayList<>();
+ toDelete.addAll(Utils.findFiles(new File("."),
+ Utils.createFilter(".out")));
+ toDelete.addAll(Utils.findFiles(new File("."),
+ Utils.createFilter(".bak")));
+ toDelete.addAll(Utils.findFiles(new File("."),
+ Utils.createFilter(".jar")));
+ toDelete.addAll(Utils.findFiles(new File("."),
+ Utils.createFilter(".pack")));
+ toDelete.addAll(Utils.findFiles(new File("."),
+ Utils.createFilter(".bnd")));
+ toDelete.addAll(Utils.findFiles(new File("."),
+ Utils.createFilter(".txt")));
+ toDelete.addAll(Utils.findFiles(new File("."),
+ Utils.createFilter(".idx")));
+ toDelete.addAll(Utils.findFiles(new File("."),
+ Utils.createFilter(".gidx")));
+ for (File f : toDelete) {
+ f.delete();
+ }
+ }
+
static final FileFilter DIR_FILTER = new FileFilter() {
public boolean accept(File pathname) {
if (pathname.isDirectory()) {
@@ -199,6 +226,9 @@
Files.createDirectories(parent);
}
Files.copy(src.toPath(), dst.toPath(), COPY_ATTRIBUTES, REPLACE_EXISTING);
+ if (dst.isDirectory() && !dst.canWrite()) {
+ dst.setWritable(true);
+ }
}
static String baseName(File file, String extension) {
--- a/jdk/test/tools/pack200/typeannos/TestTypeAnnotations.java Tue Jul 02 16:38:09 2013 -0700
+++ b/jdk/test/tools/pack200/typeannos/TestTypeAnnotations.java Wed Jul 03 14:35:10 2013 -0700
@@ -41,5 +41,6 @@
"-C", Utils.TEST_CLS_DIR.getAbsolutePath(),
".");
Utils.testWithRepack(testFile, "--unknown-attribute=error");
+ Utils.cleanup();
}
}