changeset 16846:7bcfcb00a069

Merge
author prr
date Mon, 06 Mar 2017 10:35:14 -0800
parents 743970d86b39 1e2e880353a6
children 103f15c4564d
files test/java/util/concurrent/ArrayBlockingQueue/IteratorConsistency.java
diffstat 27 files changed, 1202 insertions(+), 940 deletions(-) [+]
line wrap: on
line diff
--- a/.hgtags	Mon Mar 06 17:03:26 2017 +0300
+++ b/.hgtags	Mon Mar 06 10:35:14 2017 -0800
@@ -401,3 +401,4 @@
 7d64e541a6c04c714bcad4c8b553db912f827cd5 jdk-9+156
 fdfa7b2fe9a7db06792eae20f97748f3e85bb83a jdk-9+157
 c476ca73750698fa5654e101af699ee45db38e2a jdk-9+158
+49b54a4d9e84b7ba956b8c27fced5035465146ae jdk-9+159
--- a/src/java.base/share/classes/java/util/Properties.java	Mon Mar 06 17:03:26 2017 +0300
+++ b/src/java.base/share/classes/java/util/Properties.java	Mon Mar 06 10:35:14 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1995, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 2017, 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
@@ -471,7 +471,7 @@
                 if (inStream != null) {
                     //The line below is equivalent to calling a
                     //ISO8859-1 decoder.
-                    c = (char) (0xff & inByteBuf[inOff++]);
+                    c = (char)(inByteBuf[inOff++] & 0xFF);
                 } else {
                     c = inCharBuf[inOff++];
                 }
@@ -494,8 +494,25 @@
                 if (isNewLine) {
                     isNewLine = false;
                     if (c == '#' || c == '!') {
+                        // Comment, quickly consume the rest of the line,
+                        // resume on line-break and backslash.
+                        if (inStream != null) {
+                            while (inOff < inLimit) {
+                                byte b = inByteBuf[inOff++];
+                                if (b == '\n' || b == '\r' || b == '\\') {
+                                    c = (char)(b & 0xFF);
+                                    break;
+                                }
+                            }
+                        } else {
+                            while (inOff < inLimit) {
+                                c = inCharBuf[inOff++];
+                                if (c == '\n' || c == '\r' || c == '\\') {
+                                    break;
+                                }
+                            }
+                        }
                         isCommentLine = true;
-                        continue;
                     }
                 }
 
--- a/src/java.base/share/classes/java/util/SplittableRandom.java	Mon Mar 06 17:03:26 2017 +0300
+++ b/src/java.base/share/classes/java/util/SplittableRandom.java	Mon Mar 06 10:35:14 2017 -0800
@@ -779,8 +779,7 @@
      * @return a stream of pseudorandom {@code double} values,
      *         each with the given origin (inclusive) and bound (exclusive)
      * @throws IllegalArgumentException if {@code streamSize} is
-     *         less than zero
-     * @throws IllegalArgumentException if {@code randomNumberOrigin}
+     *         less than zero, or {@code randomNumberOrigin}
      *         is greater than or equal to {@code randomNumberBound}
      */
     public DoubleStream doubles(long streamSize, double randomNumberOrigin,
--- a/src/java.base/share/classes/java/util/concurrent/ArrayBlockingQueue.java	Mon Mar 06 17:03:26 2017 +0300
+++ b/src/java.base/share/classes/java/util/concurrent/ArrayBlockingQueue.java	Mon Mar 06 10:35:14 2017 -0800
@@ -1226,6 +1226,7 @@
                 } else {
                     nextIndex = NONE;
                     nextItem = null;
+                    if (lastRet == REMOVED) detach();
                 }
             } finally {
                 lock.unlock();
--- a/src/java.base/share/classes/java/util/concurrent/ThreadLocalRandom.java	Mon Mar 06 17:03:26 2017 +0300
+++ b/src/java.base/share/classes/java/util/concurrent/ThreadLocalRandom.java	Mon Mar 06 10:35:14 2017 -0800
@@ -699,8 +699,7 @@
      * @return a stream of pseudorandom {@code double} values,
      *         each with the given origin (inclusive) and bound (exclusive)
      * @throws IllegalArgumentException if {@code streamSize} is
-     *         less than zero
-     * @throws IllegalArgumentException if {@code randomNumberOrigin}
+     *         less than zero, or {@code randomNumberOrigin}
      *         is greater than or equal to {@code randomNumberBound}
      * @since 1.8
      */
--- a/src/java.base/share/classes/java/util/concurrent/locks/ReentrantReadWriteLock.java	Mon Mar 06 17:03:26 2017 +0300
+++ b/src/java.base/share/classes/java/util/concurrent/locks/ReentrantReadWriteLock.java	Mon Mar 06 10:35:14 2017 -0800
@@ -53,16 +53,14 @@
  *
  * <dl>
  * <dt><b><i>Non-fair mode (default)</i></b>
- * <dd style="font-family:'DejaVu Sans', Arial, Helvetica, sans-serif">
- * When constructed as non-fair (the default), the order of entry
+ * <dd>When constructed as non-fair (the default), the order of entry
  * to the read and write lock is unspecified, subject to reentrancy
  * constraints.  A nonfair lock that is continuously contended may
  * indefinitely postpone one or more reader or writer threads, but
  * will normally have higher throughput than a fair lock.
  *
  * <dt><b><i>Fair mode</i></b>
- * <dd style="font-family:'DejaVu Sans', Arial, Helvetica, sans-serif">
- * When constructed as fair, threads contend for entry using an
+ * <dd>When constructed as fair, threads contend for entry using an
  * approximately arrival-order policy. When the currently held lock
  * is released, either the longest-waiting single writer thread will
  * be assigned the write lock, or if there is a group of reader threads
--- a/src/java.base/share/classes/jdk/internal/jimage/BasicImageReader.java	Mon Mar 06 17:03:26 2017 +0300
+++ b/src/java.base/share/classes/jdk/internal/jimage/BasicImageReader.java	Mon Mar 06 10:35:14 2017 -0800
@@ -249,27 +249,20 @@
         return stringsReader;
     }
 
-    public ImageLocation findLocation(String mn, String rn) {
-        Objects.requireNonNull(mn);
-        Objects.requireNonNull(rn);
-
-        return findLocation("/" + mn + "/" + rn);
-    }
-
-    public synchronized ImageLocation findLocation(String name) {
+    public synchronized ImageLocation findLocation(String module, String name) {
+        Objects.requireNonNull(module);
         Objects.requireNonNull(name);
         // Details of the algorithm used here can be found in
         // jdk.tools.jlink.internal.PerfectHashBuilder.
-        byte[] bytes = ImageStringsReader.mutf8FromString(name);
         int count = header.getTableLength();
-        int index = redirect.get(ImageStringsReader.hashCode(bytes) % count);
+        int index = redirect.get(ImageStringsReader.hashCode(module, name) % count);
 
         if (index < 0) {
             // index is twos complement of location attributes index.
             index = -index - 1;
         } else if (index > 0) {
             // index is hash seed needed to compute location attributes index.
-            index = ImageStringsReader.hashCode(bytes, index) % count;
+            index = ImageStringsReader.hashCode(module, name, index) % count;
         } else {
             // No entry.
             return null;
@@ -277,13 +270,36 @@
 
         long[] attributes = getAttributes(offsets.get(index));
 
-        ImageLocation imageLocation = new ImageLocation(attributes, stringsReader);
+        if (!ImageLocation.verify(module, name, attributes, stringsReader)) {
+            return null;
+        }
+        return new ImageLocation(attributes, stringsReader);
+    }
 
-        if (!imageLocation.verify(name)) {
+    public synchronized ImageLocation findLocation(String name) {
+        Objects.requireNonNull(name);
+        // Details of the algorithm used here can be found in
+        // jdk.tools.jlink.internal.PerfectHashBuilder.
+        int count = header.getTableLength();
+        int index = redirect.get(ImageStringsReader.hashCode(name) % count);
+
+        if (index < 0) {
+            // index is twos complement of location attributes index.
+            index = -index - 1;
+        } else if (index > 0) {
+            // index is hash seed needed to compute location attributes index.
+            index = ImageStringsReader.hashCode(name, index) % count;
+        } else {
+            // No entry.
             return null;
         }
 
-        return imageLocation;
+        long[] attributes = getAttributes(offsets.get(index));
+
+        if (!ImageLocation.verify(name, attributes, stringsReader)) {
+            return null;
+        }
+        return new ImageLocation(attributes, stringsReader);
     }
 
     public String[] getEntryNames() {
--- a/src/java.base/share/classes/jdk/internal/jimage/ImageLocation.java	Mon Mar 06 17:03:26 2017 +0300
+++ b/src/java.base/share/classes/jdk/internal/jimage/ImageLocation.java	Mon Mar 06 10:35:14 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2017, 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
@@ -59,14 +59,6 @@
         return strings;
     }
 
-    private static int attributeLength(int data) {
-        return (data & 0x7) + 1;
-    }
-
-    private static int attributeKind(int data) {
-        return data >>> 3;
-    }
-
     static long[] decompress(ByteBuffer bytes) {
         Objects.requireNonNull(bytes);
         long[] attributes = new long[ATTRIBUTE_COUNT];
@@ -74,7 +66,7 @@
         if (bytes != null) {
             while (bytes.hasRemaining()) {
                 int data = bytes.get() & 0xFF;
-                int kind = attributeKind(data);
+                int kind = data >>> 3;
 
                 if (kind == ATTRIBUTE_END) {
                     break;
@@ -85,7 +77,7 @@
                         "Invalid jimage attribute kind: " + kind);
                 }
 
-                int length = attributeLength(data);
+                int length = (data & 0x7) + 1;
                 long value = 0;
 
                 for (int j = 0; j < length; j++) {
@@ -128,9 +120,82 @@
      }
 
     public boolean verify(String name) {
+        return verify(name, attributes, strings);
+    }
+
+    /**
+     * A simpler verification would be {@code name.equals(getFullName())}, but
+     * by not creating the full name and enabling early returns we allocate
+     * fewer objects. Could possibly be made allocation free by extending
+     * ImageStrings to test if strings at an offset match the name region.
+     */
+    static boolean verify(String name, long[] attributes, ImageStrings strings) {
         Objects.requireNonNull(name);
+        final int length = name.length();
+        int index = 0;
+        int moduleOffset = (int)attributes[ATTRIBUTE_MODULE];
+        if (moduleOffset != 0) {
+            String module = strings.get(moduleOffset);
+            final int moduleLen = module.length();
+            index = moduleLen + 1;
+            if (length <= index
+                    || name.charAt(0) != '/'
+                    || !name.regionMatches(1, module, 0, moduleLen)
+                    || name.charAt(index++) != '/') {
+                return false;
+            }
+        }
 
-        return name.equals(getFullName());
+        return verifyName(name, index, length, attributes, strings);
+    }
+
+    static boolean verify(String module, String name, long[] attributes,
+            ImageStrings strings) {
+        Objects.requireNonNull(module);
+        Objects.requireNonNull(name);
+        int moduleOffset = (int)attributes[ATTRIBUTE_MODULE];
+        if (moduleOffset != 0) {
+            if (!module.equals(strings.get(moduleOffset))) {
+                return false;
+            }
+        }
+
+        return verifyName(name, 0, name.length(), attributes, strings);
+    }
+
+    private static boolean verifyName(String name, int index, int length,
+            long[] attributes, ImageStrings strings) {
+
+        int parentOffset = (int) attributes[ATTRIBUTE_PARENT];
+        if (parentOffset != 0) {
+            String parent = strings.get(parentOffset);
+            final int parentLen = parent.length();
+            if (!name.regionMatches(index, parent, 0, parentLen)) {
+                return false;
+            }
+            index += parentLen;
+            if (length <= index || name.charAt(index++) != '/') {
+                return false;
+            }
+        }
+        String base = strings.get((int) attributes[ATTRIBUTE_BASE]);
+        final int baseLen = base.length();
+        if (!name.regionMatches(index, base, 0, baseLen)) {
+            return false;
+        }
+        index += baseLen;
+        int extOffset = (int) attributes[ATTRIBUTE_EXTENSION];
+        if (extOffset != 0) {
+            String extension = strings.get(extOffset);
+            int extLen = extension.length();
+            if (length <= index
+                    || name.charAt(index++) != '.'
+                    || !name.regionMatches(index, extension, 0, extLen)) {
+                return false;
+            }
+            index += extLen;
+        }
+        return length == index;
     }
 
     long getAttribute(int kind) {
--- a/src/java.base/share/classes/jdk/internal/jimage/ImageStringsReader.java	Mon Mar 06 17:03:26 2017 +0300
+++ b/src/java.base/share/classes/jdk/internal/jimage/ImageStringsReader.java	Mon Mar 06 10:35:14 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2017, 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,8 @@
  */
 public class ImageStringsReader implements ImageStrings {
     public static final int HASH_MULTIPLIER = 0x01000193;
+    public static final int POSITIVE_MASK = 0x7FFFFFFF;
+
     private final BasicImageReader reader;
 
     ImageStringsReader(BasicImageReader reader) {
@@ -54,40 +56,60 @@
         throw new InternalError("Can not add strings at runtime");
     }
 
-    private static int hashCode(byte[] bytes, int offset, int count, int seed) {
-        Objects.requireNonNull(bytes);
-
-        if (offset < 0 || count < 0 || offset > bytes.length - count) {
-            throw new IndexOutOfBoundsException("offset=" + offset + ", count=" + count);
-        }
-
-        int limit = offset + count;
-
-        if (limit < 0 || limit > bytes.length) {
-            throw new IndexOutOfBoundsException("limit=" + limit);
-        }
-
-        for (int i = offset; i < limit; i++) {
-            seed = (seed * HASH_MULTIPLIER) ^ (bytes[i] & 0xFF);
-        }
-
-        return seed & 0x7FFFFFFF;
+    public static int hashCode(String s) {
+        return hashCode(s, HASH_MULTIPLIER);
     }
 
-    public static int hashCode(byte[] bytes, int seed) {
-        return hashCode(bytes, 0, bytes.length, seed);
+    public static int hashCode(String s, int seed) {
+        return unmaskedHashCode(s, seed) & POSITIVE_MASK;
     }
 
-    public static int hashCode(byte[] bytes) {
-        return hashCode(bytes, 0, bytes.length, HASH_MULTIPLIER);
+    public static int hashCode(String module, String name) {
+        return hashCode(module, name, HASH_MULTIPLIER);
     }
 
-    public static int hashCode(String string, int seed) {
-        return hashCode(mutf8FromString(string), seed);
+    public static int hashCode(String module, String name, int seed) {
+        seed = unmaskedHashCode("/", seed);
+        seed = unmaskedHashCode(module, seed);
+        seed = unmaskedHashCode("/", seed);
+        seed = unmaskedHashCode(name, seed);
+        return seed & POSITIVE_MASK;
     }
 
-    public static int hashCode(String string) {
-        return hashCode(mutf8FromString(string), HASH_MULTIPLIER);
+    public static int unmaskedHashCode(String s, int seed) {
+        int slen = s.length();
+        byte[] buffer = null;
+
+        for (int i = 0; i < slen; i++) {
+            char ch = s.charAt(i);
+            int uch = ch & 0xFFFF;
+
+            if ((uch & ~0x7F) != 0) {
+                if (buffer == null) {
+                    buffer = new byte[8];
+                }
+                int mask = ~0x3F;
+                int n = 0;
+
+                do {
+                    buffer[n++] = (byte)(0x80 | (uch & 0x3F));
+                    uch >>= 6;
+                    mask >>= 1;
+                } while ((uch & mask) != 0);
+
+                buffer[n] = (byte)((mask << 1) | uch);
+
+                do {
+                    seed = (seed * HASH_MULTIPLIER) ^ (buffer[n--] & 0xFF);
+                } while (0 <= n);
+            } else if (uch == 0) {
+                seed = (seed * HASH_MULTIPLIER) ^ (0xC0);
+                seed = (seed * HASH_MULTIPLIER) ^ (0x80);
+            } else {
+                seed = (seed * HASH_MULTIPLIER) ^ (uch);
+            }
+        }
+        return seed;
     }
 
     static int charsFromMUTF8Length(byte[] bytes, int offset, int count) {
@@ -179,7 +201,7 @@
         throw new InternalError("No terminating zero byte for modified UTF-8 byte sequence");
     }
 
-    static void charsFromByteBuffer(char chars[], ByteBuffer buffer) {
+    static void charsFromByteBuffer(char[] chars, ByteBuffer buffer) {
         int j = 0;
 
         while(buffer.hasRemaining()) {
@@ -228,10 +250,12 @@
         return new String(chars);
     }
 
-    static int mutf8FromCharsLength(char chars[]) {
+    static int mutf8FromStringLength(String s) {
         int length = 0;
+        int slen = s.length();
 
-        for (char ch : chars) {
+        for (int i = 0; i < slen; i++) {
+            char ch = s.charAt(i);
             int uch = ch & 0xFFFF;
 
             if ((uch & ~0x7F) != 0) {
@@ -255,14 +279,19 @@
         return length;
     }
 
-    static void mutf8FromChars(byte[] bytes, int offset, char chars[]) {
+    static void mutf8FromString(byte[] bytes, int offset, String s) {
         int j = offset;
-        byte[] buffer = new byte[8];
+        byte[] buffer = null;
+        int slen = s.length();
 
-        for (char ch : chars) {
+        for (int i = 0; i < slen; i++) {
+            char ch = s.charAt(i);
             int uch = ch & 0xFFFF;
 
             if ((uch & ~0x7F) != 0) {
+                if (buffer == null) {
+                    buffer = new byte[8];
+                }
                 int mask = ~0x3F;
                 int n = 0;
 
@@ -287,10 +316,9 @@
     }
 
     public static byte[] mutf8FromString(String string) {
-        char[] chars = string.toCharArray();
-        int length = mutf8FromCharsLength(chars);
+        int length = mutf8FromStringLength(string);
         byte[] bytes = new byte[length];
-        mutf8FromChars(bytes, 0, chars);
+        mutf8FromString(bytes, 0, string);
 
         return bytes;
     }
--- a/src/java.base/unix/native/launcher/jexec.c	Mon Mar 06 17:03:26 2017 +0300
+++ b/src/java.base/unix/native/launcher/jexec.c	Mon Mar 06 10:35:14 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2017, 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
@@ -332,7 +332,7 @@
 
                 if (end <= count) {
                     end -= 4; // make sure there are 4 bytes to read at start
-                    while (start < end) {
+                    while (start <= end) {
                         off_t xhid  = SH(buf, start);
                         off_t xdlen = SH(buf, start + 2);
 
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImageLocationWriter.java	Mon Mar 06 17:03:26 2017 +0300
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImageLocationWriter.java	Mon Mar 06 10:35:14 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2017, 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
@@ -108,24 +108,24 @@
         int hash = seed;
 
         if (getModuleOffset() != 0) {
-            hash = ImageStringsReader.hashCode("/", hash);
-            hash = ImageStringsReader.hashCode(getModule(), hash);
-            hash = ImageStringsReader.hashCode("/", hash);
+            hash = ImageStringsReader.unmaskedHashCode("/", hash);
+            hash = ImageStringsReader.unmaskedHashCode(getModule(), hash);
+            hash = ImageStringsReader.unmaskedHashCode("/", hash);
         }
 
         if (getParentOffset() != 0) {
-            hash = ImageStringsReader.hashCode(getParent(), hash);
-            hash = ImageStringsReader.hashCode("/", hash);
+            hash = ImageStringsReader.unmaskedHashCode(getParent(), hash);
+            hash = ImageStringsReader.unmaskedHashCode("/", hash);
         }
 
-        hash = ImageStringsReader.hashCode(getBase(), hash);
+        hash = ImageStringsReader.unmaskedHashCode(getBase(), hash);
 
         if (getExtensionOffset() != 0) {
-            hash = ImageStringsReader.hashCode(".", hash);
-            hash = ImageStringsReader.hashCode(getExtension(), hash);
+            hash = ImageStringsReader.unmaskedHashCode(".", hash);
+            hash = ImageStringsReader.unmaskedHashCode(getExtension(), hash);
         }
 
-        return hash;
+        return hash & ImageStringsReader.POSITIVE_MASK;
     }
 
     @Override
--- a/test/ProblemList.txt	Mon Mar 06 17:03:26 2017 +0300
+++ b/test/ProblemList.txt	Mon Mar 06 10:35:14 2017 -0800
@@ -125,6 +125,8 @@
 
 java/lang/StringCoding/CheckEncodings.sh                        7008363 generic-all
 
+jdk/internal/misc/JavaLangAccess/NewUnsafeString.java           8176188 generic-all
+
 ############################################################################
 
 # jdk_instrument
@@ -215,6 +217,8 @@
 javax/net/ssl/DTLS/PacketLossRetransmission.java                8169086 macosx-x64
 javax/net/ssl/DTLS/RespondToRetransmit.java                     8169086 macosx-x64
 
+sun/security/mscapi/SignedObjectChain.java                      8176183 windows-all
+
 ############################################################################
 
 # jdk_sound
--- a/test/com/sun/jndi/dns/Parser.java	Mon Mar 06 17:03:26 2017 +0300
+++ b/test/com/sun/jndi/dns/Parser.java	Mon Mar 06 10:35:14 2017 -0800
@@ -26,7 +26,9 @@
  * @bug 8035105
  * @summary DNS resource record parsing
  * @modules jdk.naming.dns/com.sun.jndi.dns:+open
+ *
  * @compile --add-modules jdk.naming.dns Parser.java
+ * @run main Parser
  */
 
 import com.sun.jndi.dns.ResourceRecord;
--- a/test/java/util/TimeZone/UTCAliasTest.java	Mon Mar 06 17:03:26 2017 +0300
+++ b/test/java/util/TimeZone/UTCAliasTest.java	Mon Mar 06 10:35:14 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2017, 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
@@ -27,6 +27,7 @@
  * @summary Make sure that "UTC" is an alias of "Etc/UTC" as defined in the tzdata backward.
  * @modules java.base/sun.util.calendar
  * @compile -XDignore.symbol.file UTCAliasTest.java
+ * @run main UTCAliasTest
  */
 
 import java.util.*;
--- a/test/java/util/concurrent/ArrayBlockingQueue/IteratorConsistency.java	Mon Mar 06 17:03:26 2017 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,755 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-/*
- * This file is available under and governed by the GNU General Public
- * License version 2 only, as published by the Free Software Foundation.
- * However, the following notice accompanied the original version of this
- * file:
- *
- * Written by Martin Buchholz with assistance from members of JCP
- * JSR-166 Expert Group and released to the public domain, as
- * explained at http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-import java.lang.ref.WeakReference;
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.ArrayDeque;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.NoSuchElementException;
-import java.util.Queue;
-import java.util.concurrent.ArrayBlockingQueue;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ThreadLocalRandom;
-
-/*
- * @test
- * @bug 7014263
- * @modules java.base/java.util.concurrent:open
- * @summary White box testing of ArrayBlockingQueue iterators.
- */
-
-/**
- * Tightly coupled to the implementation of ArrayBlockingQueue.
- * Uses reflection to inspect queue and iterator state.
- */
-@SuppressWarnings({"unchecked", "rawtypes"})
-public class IteratorConsistency {
-    final ThreadLocalRandom rnd = ThreadLocalRandom.current();
-    final int CAPACITY = 20;
-    Field itrsField;
-    Field itemsField;
-    Field takeIndexField;
-    Field headField;
-    Field nextField;
-    Field prevTakeIndexField;
-
-    void test(String[] args) throws Throwable {
-        itrsField = ArrayBlockingQueue.class.getDeclaredField("itrs");
-        itemsField = ArrayBlockingQueue.class.getDeclaredField("items");
-        takeIndexField = ArrayBlockingQueue.class.getDeclaredField("takeIndex");
-        headField = Class.forName("java.util.concurrent.ArrayBlockingQueue$Itrs").getDeclaredField("head");
-        nextField = Class.forName("java.util.concurrent.ArrayBlockingQueue$Itrs$Node").getDeclaredField("next");
-        prevTakeIndexField = Class.forName("java.util.concurrent.ArrayBlockingQueue$Itr").getDeclaredField("prevTakeIndex");
-        itrsField.setAccessible(true);
-        itemsField.setAccessible(true);
-        takeIndexField.setAccessible(true);
-        headField.setAccessible(true);
-        nextField.setAccessible(true);
-        prevTakeIndexField.setAccessible(true);
-        test(CAPACITY, true);
-        test(CAPACITY, false);
-    }
-
-    Object itrs(ArrayBlockingQueue q) {
-        try { return itrsField.get(q); }
-        catch (Throwable ex) { throw new AssertionError(ex); }
-    }
-
-    int takeIndex(ArrayBlockingQueue q) {
-        try { return takeIndexField.getInt(q); }
-        catch (Throwable ex) { throw new AssertionError(ex); }
-    }
-
-    List<Iterator> trackedIterators(Object itrs) {
-        try {
-            List<Iterator> its = new ArrayList<>();
-            if (itrs != null)
-                for (Object p = headField.get(itrs); p != null; p = nextField.get(p))
-                    its.add(((WeakReference<Iterator>)(p)).get());
-            Collections.reverse(its);
-            return its;
-        } catch (Throwable ex) { throw new AssertionError(ex); }
-    }
-
-    List<Iterator> trackedIterators(ArrayBlockingQueue q) {
-        return trackedIterators(itrs(q));
-    }
-
-    List<Iterator> attachedIterators(Object itrs) {
-        try {
-            List<Iterator> its = new ArrayList<>();
-            if (itrs != null)
-                for (Object p = headField.get(itrs); p != null; p = nextField.get(p)) {
-                    Iterator it = ((WeakReference<Iterator>)(p)).get();
-                    if (it != null && !isDetached(it))
-                        its.add(it);
-                }
-            Collections.reverse(its);
-            return its;
-        } catch (Throwable ex) { unexpected(ex); return null; }
-    }
-
-    List<Iterator> attachedIterators(ArrayBlockingQueue q) {
-        return attachedIterators(itrs(q));
-    }
-
-    Object[] internalArray(ArrayBlockingQueue q) {
-        try { return (Object[]) itemsField.get(q); }
-        catch (Throwable ex) { throw new AssertionError(ex); }
-    }
-
-    void printInternalArray(ArrayBlockingQueue q) {
-        System.err.println(Arrays.toString(internalArray(q)));
-    }
-
-    void checkExhausted(Iterator it) {
-        if (rnd.nextBoolean()) {
-            check(!it.hasNext());
-            check(isDetached(it));
-        }
-        if (rnd.nextBoolean()) {
-            it.forEachRemaining(e -> { throw new AssertionError(); });
-            checkDetached(it);
-        }
-        if (rnd.nextBoolean())
-            try { it.next(); fail("should throw"); }
-            catch (NoSuchElementException success) {}
-    }
-
-    boolean isDetached(Iterator it) {
-        try {
-            return prevTakeIndexField.getInt(it) < 0;
-        } catch (IllegalAccessException t) { unexpected(t); return false; }
-    }
-
-    void checkDetached(Iterator it) {
-        check(isDetached(it));
-    }
-
-    void removeUsingIterator(ArrayBlockingQueue q, Object element) {
-        Iterator it = q.iterator();
-        while (it.hasNext()) {
-            Object x = it.next();
-            if (element.equals(x))
-                it.remove();
-            checkRemoveThrowsISE(it);
-        }
-    }
-
-    void checkRemoveThrowsISE(Iterator it) {
-        if (rnd.nextBoolean())
-            return;
-        try { it.remove(); fail("should throw"); }
-        catch (IllegalStateException success) {}
-    }
-
-    void checkRemoveHasNoEffect(Iterator it, Collection c) {
-        if (rnd.nextBoolean())
-            return;
-        int size = c.size();
-        it.remove(); // no effect
-        equal(c.size(), size);
-        checkRemoveThrowsISE(it);
-    }
-
-    void checkIterationSanity(Queue q) {
-        if (rnd.nextBoolean())
-            return;
-        int size = q.size();
-        Object[] a = q.toArray();
-        Object[] b = new Object[size+2];
-        Arrays.fill(b, Boolean.TRUE);
-        Object[] c = q.toArray(b);
-        equal(a.length, size);
-        check(b == c);
-        check(b[size] == null);
-        check(b[size+1] == Boolean.TRUE);
-        equal(q.toString(), Arrays.toString(a));
-        Integer[] xx = null, yy = null;
-        if (size > 0) {
-            xx = new Integer[size - 1];
-            Arrays.fill(xx, 42);
-            yy = ((Queue<Integer>)q).toArray(xx);
-            for (Integer zz : xx)
-                equal(42, zz);
-        }
-        Iterator it = q.iterator();
-        for (int i = 0; i < size; i++) {
-            check(it.hasNext());
-            Object x = it.next();
-            check(x == a[i]);
-            check(x == b[i]);
-            if (xx != null) check(x == yy[i]);
-        }
-        check(!it.hasNext());
-    }
-
-    private static void waitForFinalizersToRun() {
-        for (int i = 0; i < 2; i++)
-            tryWaitForFinalizersToRun();
-    }
-
-    private static void tryWaitForFinalizersToRun() {
-        System.gc();
-        final CountDownLatch fin = new CountDownLatch(1);
-        new Object() { protected void finalize() { fin.countDown(); }};
-        System.gc();
-        try { fin.await(); }
-        catch (InterruptedException ie) { throw new Error(ie); }
-    }
-
-    void test(int capacity, boolean fair) {
-        //----------------------------------------------------------------
-        // q.clear will clear out itrs.
-        //----------------------------------------------------------------
-        try {
-            ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
-            List<Iterator> its = new ArrayList<>();
-            for (int i = 0; i < capacity; i++)
-                check(q.add(i));
-            check(itrs(q) == null);
-            for (int i = 0; i < capacity; i++) {
-                its.add(q.iterator());
-                equal(trackedIterators(q), its);
-                q.poll();
-                q.add(capacity+i);
-            }
-            q.clear();
-            check(itrs(q) == null);
-            int j = 0;
-            for (Iterator it : its) {
-                if (rnd.nextBoolean())
-                    check(it.hasNext());
-                equal(it.next(), j++);
-                checkExhausted(it);
-            }
-        } catch (Throwable t) { unexpected(t); }
-
-        //----------------------------------------------------------------
-        // q emptying will clear out itrs.
-        //----------------------------------------------------------------
-        try {
-            ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
-            List<Iterator> its = new ArrayList<>();
-            for (int i = 0; i < capacity; i++)
-                q.add(i);
-            check(itrs(q) == null);
-            for (int i = 0; i < capacity; i++) {
-                its.add(q.iterator());
-                equal(trackedIterators(q), its);
-                q.poll();
-                q.add(capacity+i);
-            }
-            for (int i = 0; i < capacity; i++)
-                q.poll();
-            check(itrs(q) == null);
-            int j = 0;
-            for (Iterator it : its) {
-                if (rnd.nextBoolean())
-                    check(it.hasNext());
-                equal(it.next(), j++);
-                checkExhausted(it);
-            }
-        } catch (Throwable t) { unexpected(t); }
-
-        //----------------------------------------------------------------
-        // Advancing 2 cycles will remove iterators.
-        //----------------------------------------------------------------
-        try {
-            ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
-            List<Iterator> its = new ArrayList<>();
-            for (int i = 0; i < capacity; i++)
-                q.add(i);
-            check(itrs(q) == null);
-            for (int i = capacity; i < 3 * capacity; i++) {
-                its.add(q.iterator());
-                equal(trackedIterators(q), its);
-                q.poll();
-                q.add(i);
-            }
-            for (int i = 3 * capacity; i < 4 * capacity; i++) {
-                equal(trackedIterators(q), its.subList(capacity,2*capacity));
-                q.poll();
-                q.add(i);
-            }
-            check(itrs(q) == null);
-            int j = 0;
-            for (Iterator it : its) {
-                if (rnd.nextBoolean())
-                    check(it.hasNext());
-                equal(it.next(), j++);
-                checkExhausted(it);
-            }
-        } catch (Throwable t) { unexpected(t); }
-
-        //----------------------------------------------------------------
-        // Interior removal of elements used by an iterator will cause
-        // it to be untracked.
-        //----------------------------------------------------------------
-        try {
-            ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
-            q.add(0);
-            for (int i = 1; i < 2 * capacity; i++) {
-                q.add(i);
-                Integer[] elts = { -1, -2, -3 };
-                for (Integer elt : elts) q.add(elt);
-                equal(q.remove(), i - 1);
-                Iterator it = q.iterator();
-                equal(it.next(), i);
-                equal(it.next(), elts[0]);
-                Collections.shuffle(Arrays.asList(elts));
-                check(q.remove(elts[0]));
-                check(q.remove(elts[1]));
-                equal(trackedIterators(q), Collections.singletonList(it));
-                check(q.remove(elts[2]));
-                check(itrs(q) == null);
-                equal(it.next(), -2);
-                if (rnd.nextBoolean()) checkExhausted(it);
-                if (rnd.nextBoolean()) checkDetached(it);
-            }
-        } catch (Throwable t) { unexpected(t); }
-
-        //----------------------------------------------------------------
-        // Check iterators on an empty q
-        //----------------------------------------------------------------
-        try {
-            ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
-            for (int i = 0; i < 4; i++) {
-                Iterator it = q.iterator();
-                check(itrs(q) == null);
-                if (rnd.nextBoolean()) checkExhausted(it);
-                if (rnd.nextBoolean()) checkDetached(it);
-                checkRemoveThrowsISE(it);
-            }
-        } catch (Throwable t) { unexpected(t); }
-
-        //----------------------------------------------------------------
-        // Check "interior" removal of iterator's last element
-        //----------------------------------------------------------------
-        try {
-            ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
-            List<Iterator> its = new ArrayList<>();
-            for (int i = 0; i < capacity; i++)
-                q.add(i);
-            for (int i = 0; i < capacity; i++) {
-                Iterator it = q.iterator();
-                its.add(it);
-                for (int j = 0; j < i; j++)
-                    equal(j, it.next());
-                equal(attachedIterators(q), its);
-            }
-            q.remove(capacity - 1);
-            equal(attachedIterators(q), its);
-            for (int i = 1; i < capacity - 1; i++) {
-                q.remove(capacity - i - 1);
-                Iterator it = its.get(capacity - i);
-                checkDetached(it);
-                equal(attachedIterators(q), its.subList(0, capacity - i));
-                if (rnd.nextBoolean()) check(it.hasNext());
-                equal(it.next(), capacity - i);
-                checkExhausted(it);
-            }
-            equal(attachedIterators(q), its.subList(0, 2));
-            q.remove(0);
-            check(q.isEmpty());
-            check(itrs(q) == null);
-            Iterator it = its.get(0);
-            equal(it.next(), 0);
-            checkRemoveHasNoEffect(it, q);
-            checkExhausted(it);
-            checkDetached(it);
-            checkRemoveHasNoEffect(its.get(1), q);
-        } catch (Throwable t) { unexpected(t); }
-
-        //----------------------------------------------------------------
-        // Check "interior" removal of alternating elements, straddling 2 cycles
-        //----------------------------------------------------------------
-        try {
-            ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
-            List<Iterator> its = new ArrayList<>();
-            // Move takeIndex to middle
-            for (int i = 0; i < capacity/2; i++) {
-                check(q.add(i));
-                equal(q.poll(), i);
-            }
-            check(takeIndex(q) == capacity/2);
-            for (int i = 0; i < capacity; i++)
-                q.add(i);
-            for (int i = 0; i < capacity; i++) {
-                Iterator it = q.iterator();
-                its.add(it);
-                for (int j = 0; j < i; j++)
-                    equal(j, it.next());
-                equal(attachedIterators(q), its);
-            }
-            // Remove all even elements, in either direction using
-            // q.remove(), or iterator.remove()
-            switch (rnd.nextInt(3)) {
-            case 0:
-                for (int i = 0; i < capacity; i+=2) {
-                    check(q.remove(i));
-                    equal(attachedIterators(q), its);
-                }
-                break;
-            case 1:
-                for (int i = capacity - 2; i >= 0; i-=2) {
-                    check(q.remove(i));
-                    equal(attachedIterators(q), its);
-                }
-                break;
-            case 2:
-                Iterator it = q.iterator();
-                while (it.hasNext()) {
-                    int i = (Integer) it.next();
-                    if ((i & 1) == 0)
-                        it.remove();
-                }
-                equal(attachedIterators(q), its);
-                break;
-            default: throw new AssertionError();
-            }
-
-            for (int i = 0; i < capacity; i++) {
-                Iterator it = its.get(i);
-                boolean even = ((i & 1) == 0);
-                if (even) {
-                    if (rnd.nextBoolean()) check(it.hasNext());
-                    equal(i, it.next());
-                    for (int j = i+1; j < capacity; j += 2)
-                        equal(j, it.next());
-                    check(!isDetached(it));
-                    check(!it.hasNext());
-                    check(isDetached(it));
-                } else { /* odd */
-                    if (rnd.nextBoolean()) check(it.hasNext());
-                    checkRemoveHasNoEffect(it, q);
-                    equal(i, it.next());
-                    for (int j = i+2; j < capacity; j += 2)
-                        equal(j, it.next());
-                    check(!isDetached(it));
-                    check(!it.hasNext());
-                    check(isDetached(it));
-                }
-            }
-            equal(trackedIterators(q), Collections.emptyList());
-            check(itrs(q) == null);
-        } catch (Throwable t) { unexpected(t); }
-
-        //----------------------------------------------------------------
-        // Check garbage collection of discarded iterators
-        //----------------------------------------------------------------
-        try {
-            ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
-            List<Iterator> its = new ArrayList<>();
-            for (int i = 0; i < capacity; i++)
-                q.add(i);
-            for (int i = 0; i < capacity; i++) {
-                its.add(q.iterator());
-                equal(attachedIterators(q), its);
-            }
-            its = null;
-            waitForFinalizersToRun();
-            List<Iterator> trackedIterators = trackedIterators(q);
-            equal(trackedIterators.size(), capacity);
-            for (Iterator x : trackedIterators)
-                check(x == null);
-            Iterator it = q.iterator();
-            equal(trackedIterators(q), Collections.singletonList(it));
-        } catch (Throwable t) { unexpected(t); }
-
-        //----------------------------------------------------------------
-        // Check garbage collection of discarded iterators,
-        // with a randomly retained subset.
-        //----------------------------------------------------------------
-        try {
-            ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
-            List<Iterator> its = new ArrayList<>();
-            List<Iterator> retained = new ArrayList<>();
-            final int size = 1 + rnd.nextInt(capacity);
-            for (int i = 0; i < size; i++)
-                q.add(i);
-            for (int i = 0; i < size; i++) {
-                Iterator it = q.iterator();
-                its.add(it);
-                equal(attachedIterators(q), its);
-            }
-            // Leave sufficient gaps in retained
-            for (int i = 0; i < size; i+= 2+rnd.nextInt(3))
-                retained.add(its.get(i));
-            its = null;
-            waitForFinalizersToRun();
-            List<Iterator> trackedIterators = trackedIterators(q);
-            equal(trackedIterators.size(), size);
-            for (Iterator it : trackedIterators)
-                check((it == null) ^ retained.contains(it));
-            Iterator it = q.iterator(); // trigger another sweep
-            retained.add(it);
-            equal(trackedIterators(q), retained);
-        } catch (Throwable t) { unexpected(t); }
-
-        //----------------------------------------------------------------
-        // Check incremental sweeping of discarded iterators.
-        // Excessively white box?!
-        //----------------------------------------------------------------
-        try {
-            final int SHORT_SWEEP_PROBES = 4;
-            final int LONG_SWEEP_PROBES = 16;
-            final int PROBE_HOP = LONG_SWEEP_PROBES + 6 * SHORT_SWEEP_PROBES;
-            final int PROBE_HOP_COUNT = 10;
-            // Expect around 8 sweeps per PROBE_HOP
-            final int SWEEPS_PER_PROBE_HOP = 8;
-            ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
-            List<Iterator> its = new ArrayList<>();
-            for (int i = 0; i < capacity; i++)
-                q.add(i);
-            for (int i = 0; i < PROBE_HOP_COUNT * PROBE_HOP; i++) {
-                its.add(q.iterator());
-                equal(attachedIterators(q), its);
-            }
-            // make some garbage, separated by PROBE_HOP
-            for (int i = 0; i < its.size(); i += PROBE_HOP)
-                its.set(i, null);
-            waitForFinalizersToRun();
-            int retries;
-            for (retries = 0;
-                 trackedIterators(q).contains(null) && retries < 1000;
-                 retries++)
-                // one round of sweeping
-                its.add(q.iterator());
-            check(retries >= PROBE_HOP_COUNT * (SWEEPS_PER_PROBE_HOP - 2));
-            check(retries <= PROBE_HOP_COUNT * (SWEEPS_PER_PROBE_HOP + 2));
-            Iterator itsit = its.iterator();
-            while (itsit.hasNext())
-                if (itsit.next() == null)
-                    itsit.remove();
-            equal(trackedIterators(q), its);
-        } catch (Throwable t) { unexpected(t); }
-
-        //----------------------------------------------------------------
-        // Check safety of iterator.remove while in detached mode.
-        //----------------------------------------------------------------
-        try {
-            ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
-            List<Iterator> its = new ArrayList<>();
-            for (int i = 0; i < capacity/2; i++) {
-                q.add(i);
-                q.remove();
-            }
-            check(takeIndex(q) == capacity/2);
-            for (int i = 0; i < capacity; i++)
-                q.add(i);
-            for (int i = 0; i < capacity; i++) {
-                Iterator it = q.iterator();
-                its.add(it);
-                for (int j = 0; j < i; j++)
-                    equal(j, it.next());
-                equal(attachedIterators(q), its);
-            }
-            for (int i = capacity - 1; i >= 0; i--) {
-                Iterator it = its.get(i);
-                equal(i, it.next()); // last element
-                check(!isDetached(it));
-                check(!it.hasNext()); // first hasNext failure
-                check(isDetached(it));
-                int size = q.size();
-                check(q.contains(i));
-                switch (rnd.nextInt(3)) {
-                case 0:
-                    it.remove();
-                    check(!q.contains(i));
-                    equal(q.size(), size - 1);
-                    break;
-                case 1:
-                    // replace i with impostor
-                    if (q.remainingCapacity() == 0) {
-                        check(q.remove(i));
-                        check(q.add(-1));
-                    } else {
-                        check(q.add(-1));
-                        check(q.remove(i));
-                    }
-                    it.remove(); // should have no effect
-                    equal(size, q.size());
-                    check(q.contains(-1));
-                    check(q.remove(-1));
-                    break;
-                case 2:
-                    // replace i with true impostor
-                    if (i != 0) {
-                        check(q.remove(i));
-                        check(q.add(i));
-                    }
-                    it.remove();
-                    check(!q.contains(i));
-                    equal(q.size(), size - 1);
-                    break;
-                default: throw new AssertionError();
-                }
-                checkRemoveThrowsISE(it);
-                check(isDetached(it));
-                check(!trackedIterators(q).contains(it));
-            }
-            check(q.isEmpty());
-            check(itrs(q) == null);
-            for (Iterator it : its)
-                checkExhausted(it);
-        } catch (Throwable t) { unexpected(t); }
-
-        //----------------------------------------------------------------
-        // Check dequeues bypassing iterators' current positions.
-        //----------------------------------------------------------------
-        try {
-            ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
-            Queue<Iterator> its0 = new ArrayDeque<>();
-            Queue<Iterator> itsMid = new ArrayDeque<>();
-            List<Iterator> its = new ArrayList<>();
-            for (int i = 0; i < capacity; i++)
-                q.add(i);
-            for (int i = 0; i < 2 * capacity + 1; i++) {
-                Iterator it = q.iterator();
-                its.add(it);
-                its0.add(it);
-            }
-            for (int i = 0; i < 2 * capacity + 1; i++) {
-                Iterator it = q.iterator();
-                for (int j = 0; j < capacity/2; j++)
-                    equal(j, it.next());
-                its.add(it);
-                itsMid.add(it);
-            }
-            for (int i = capacity; i < 3 * capacity; i++) {
-                Iterator it;
-
-                it = its0.remove();
-                checkRemoveThrowsISE(it);
-                if (rnd.nextBoolean()) check(it.hasNext());
-                equal(0, it.next());
-                int victim = i - capacity;
-                for (int j = victim + (victim == 0 ? 1 : 0); j < i; j++) {
-                    if (rnd.nextBoolean()) check(it.hasNext());
-                    equal(j, it.next());
-                }
-                checkExhausted(it);
-
-                it = itsMid.remove();
-                if (victim >= capacity/2)
-                    checkRemoveHasNoEffect(it, q);
-                equal(capacity/2, it.next());
-                if (victim > capacity/2)
-                    checkRemoveHasNoEffect(it, q);
-                for (int j = Math.max(victim, capacity/2 + 1); j < i; j++) {
-                    if (rnd.nextBoolean()) check(it.hasNext());
-                    equal(j, it.next());
-                }
-                checkExhausted(it);
-
-                if (rnd.nextBoolean()) {
-                    equal(victim, q.remove());
-                } else {
-                    ArrayList list = new ArrayList(1);
-                    q.drainTo(list, 1);
-                    equal(list.size(), 1);
-                    equal(victim, list.get(0));
-                }
-                check(q.add(i));
-            }
-            // takeIndex has wrapped twice.
-            Iterator it0 = its0.remove();
-            Iterator itMid = itsMid.remove();
-            check(isDetached(it0));
-            check(isDetached(itMid));
-            if (rnd.nextBoolean()) check(it0.hasNext());
-            if (rnd.nextBoolean()) check(itMid.hasNext());
-            checkRemoveThrowsISE(it0);
-            checkRemoveHasNoEffect(itMid, q);
-            if (rnd.nextBoolean()) equal(0, it0.next());
-            if (rnd.nextBoolean()) equal(capacity/2, itMid.next());
-            check(isDetached(it0));
-            check(isDetached(itMid));
-            equal(capacity, q.size());
-            equal(0, q.remainingCapacity());
-        } catch (Throwable t) { unexpected(t); }
-
-        //----------------------------------------------------------------
-        // Check collective sanity of iteration, toArray() and toString()
-        //----------------------------------------------------------------
-        try {
-            ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
-            for (int i = 0; i < capacity; i++) {
-                checkIterationSanity(q);
-                equal(capacity, q.size() + q.remainingCapacity());
-                q.add(i);
-            }
-            for (int i = 0; i < (capacity + (capacity >> 1)); i++) {
-                checkIterationSanity(q);
-                equal(capacity, q.size() + q.remainingCapacity());
-                equal(i, q.peek());
-                equal(i, q.poll());
-                checkIterationSanity(q);
-                equal(capacity, q.size() + q.remainingCapacity());
-                q.add(capacity + i);
-            }
-            for (int i = 0; i < capacity; i++) {
-                checkIterationSanity(q);
-                equal(capacity, q.size() + q.remainingCapacity());
-                int expected = i + capacity + (capacity >> 1);
-                equal(expected, q.peek());
-                equal(expected, q.poll());
-            }
-            checkIterationSanity(q);
-        } catch (Throwable t) { unexpected(t); }
-
-    }
-
-    //--------------------- Infrastructure ---------------------------
-    volatile int passed = 0, failed = 0;
-    void pass() {passed++;}
-    void fail() {failed++; Thread.dumpStack();}
-    void fail(String msg) {System.err.println(msg); fail();}
-    void unexpected(Throwable t) {failed++; t.printStackTrace();}
-    void check(boolean cond) {if (cond) pass(); else fail();}
-    void equal(Object x, Object y) {
-        if (x == null ? y == null : x.equals(y)) pass();
-        else fail(x + " not equal to " + y);}
-    public static void main(String[] args) throws Throwable {
-        new IteratorConsistency().instanceMain(args);}
-    public void instanceMain(String[] args) throws Throwable {
-        try {test(args);} catch (Throwable t) {unexpected(t);}
-        System.err.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
-        if (failed > 0) throw new AssertionError("Some tests failed");}
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/util/concurrent/ArrayBlockingQueue/WhiteBox.java	Mon Mar 06 10:35:14 2017 -0800
@@ -0,0 +1,759 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Martin Buchholz with assistance from members of JCP
+ * JSR-166 Expert Group and released to the public domain, as
+ * explained at http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/*
+ * @test
+ * @modules java.base/java.util.concurrent:open
+ * @run testng WhiteBox
+ * @summary White box tests of implementation details
+ */
+
+import static org.testng.Assert.*;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import java.lang.ref.WeakReference;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.TimeUnit;
+import java.util.function.BooleanSupplier;
+
+@Test
+public class WhiteBox {
+    final ThreadLocalRandom rnd = ThreadLocalRandom.current();
+    final MethodHandles.Lookup lookup = MethodHandles.lookup();
+    final VarHandle ITRS, ITEMS, TAKEINDEX, PUTINDEX, COUNT, HEAD, NEXT, PREVTAKEINDEX;
+
+    WhiteBox() throws ReflectiveOperationException {
+        Class<?> qClass = ArrayBlockingQueue.class;
+        Class<?> itrClass  = Class.forName(qClass.getName() + "$Itr");
+        Class<?> itrsClass = Class.forName(qClass.getName() + "$Itrs");
+        Class<?> nodeClass = Class.forName(itrsClass.getName() + "$Node");
+        ITRS      = findVarHandle(qClass, "itrs", itrsClass);
+        ITEMS     = findVarHandle(qClass, "items", Object[].class);
+        TAKEINDEX = findVarHandle(qClass, "takeIndex", int.class);
+        PUTINDEX  = findVarHandle(qClass, "putIndex", int.class);
+        COUNT     = findVarHandle(qClass, "count", int.class);
+        HEAD      = findVarHandle(itrsClass, "head", nodeClass);
+        NEXT      = findVarHandle(nodeClass, "next", nodeClass);
+        PREVTAKEINDEX = findVarHandle(itrClass, "prevTakeIndex", int.class);
+    }
+
+    VarHandle findVarHandle(Class<?> recv, String name, Class<?> type)
+        throws ReflectiveOperationException {
+        return MethodHandles.privateLookupIn(recv, lookup)
+            .findVarHandle(recv, name, type);
+    }
+
+    Object itrs(ArrayBlockingQueue q) { return ITRS.get(q); }
+    Object[] items(ArrayBlockingQueue q) { return (Object[]) ITEMS.get(q); }
+    int takeIndex(ArrayBlockingQueue q) { return (int) TAKEINDEX.get(q); }
+    int putIndex(ArrayBlockingQueue q) { return (int) PUTINDEX.get(q); }
+    int count(ArrayBlockingQueue q) { return (int) COUNT.get(q); }
+    Object head(Object itrs) { return HEAD.get(itrs); }
+    Object next(Object node) { return NEXT.get(node); }
+    int prevTakeIndex(Iterator itr) { return (int) PREVTAKEINDEX.get(itr); }
+
+    boolean isDetached(Iterator it) {
+        return prevTakeIndex(it) < 0;
+    }
+
+    void assertIteratorExhausted(Iterator it) {
+        if (rnd.nextBoolean()) {
+            assertTrue(!it.hasNext());
+            assertTrue(isDetached(it));
+        }
+        if (rnd.nextBoolean()) {
+            it.forEachRemaining(e -> { throw new AssertionError(); });
+            assertTrue(isDetached(it));
+        }
+        if (rnd.nextBoolean())
+            try { it.next(); fail("should throw"); }
+            catch (NoSuchElementException success) {}
+    }
+
+    List<Iterator> trackedIterators(ArrayBlockingQueue q) {
+        List<Iterator> its = new ArrayList<>();
+        Object itrs = itrs(q);
+        if (itrs != null) {
+            for (Object p = head(itrs); p != null; p = next(p))
+                its.add(((WeakReference<Iterator>)(p)).get());
+            Collections.reverse(its);
+        }
+        return its;
+    }
+
+    List<Iterator> attachedIterators(ArrayBlockingQueue q) {
+        List<Iterator> its = new ArrayList<>();
+        Object itrs = itrs(q);
+        if (itrs != null) {
+            for (Object p = head(itrs); p != null; p = next(p)) {
+                Iterator it = ((WeakReference<Iterator>)(p)).get();
+                if (it != null && !isDetached(it))
+                    its.add(it);
+            }
+            Collections.reverse(its);
+        }
+        return its;
+    }
+
+    void assertRemoveThrowsISE(Iterator it) {
+        if (rnd.nextBoolean())
+            try { it.remove(); fail("should throw"); }
+            catch (IllegalStateException success) {}
+    }
+
+    void assertRemoveHasNoEffect(Iterator it, Collection c) {
+        if (rnd.nextBoolean()) {
+            int size = c.size();
+            it.remove(); // no effect
+            assertEquals(c.size(), size);
+            assertRemoveThrowsISE(it);
+        }
+    }
+
+    void checkIterationSanity(Queue q) {
+        if (rnd.nextBoolean())
+            return;
+        int size = q.size();
+        Object[] a = q.toArray();
+        Object[] b = new Object[size+2];
+        Arrays.fill(b, Boolean.TRUE);
+        Object[] c = q.toArray(b);
+        assertEquals(a.length, size);
+        assertSame(b, c);
+        assertNull(b[size]);
+        assertSame(b[size+1], Boolean.TRUE);
+        assertEquals(q.toString(), Arrays.toString(a));
+        Integer[] xx = null, yy = null;
+        if (size > 0) {
+            xx = new Integer[size - 1];
+            Arrays.fill(xx, 42);
+            yy = ((Queue<Integer>)q).toArray(xx);
+            for (Integer zz : xx)
+                assertEquals(42, (int) zz);
+        }
+        Iterator it = q.iterator();
+        for (int i = 0; i < size; i++) {
+            if (rnd.nextBoolean()) assertTrue(it.hasNext());
+            Object x = it.next();
+            assertSame(x, a[i]);
+            assertSame(x, b[i]);
+            if (xx != null) assertSame(x, yy[i]);
+        }
+        if (rnd.nextBoolean()) assertTrue(!it.hasNext());
+    }
+
+    /**
+     * Instead of having putIndex (and takeIndex) at the initial
+     * default of 0, move them to a random location.
+     */
+    void randomizePutIndex(ArrayBlockingQueue q) {
+        assertTrue(q.isEmpty());
+        int capacity = q.remainingCapacity();
+        int n = rnd.nextInt(capacity + 1);
+        int putIndex = putIndex(q);
+        for (int i = n; i-->0; ) q.add(Boolean.TRUE);
+        for (int i = n; i-->0; ) q.remove();
+        assertEquals(putIndex(q), (putIndex + n) % items(q).length);
+    }
+
+    /** No guarantees, but effective in practice. */
+    static void forceFullGc() {
+        CountDownLatch finalizeDone = new CountDownLatch(1);
+        WeakReference<?> ref = new WeakReference<Object>(new Object() {
+            protected void finalize() { finalizeDone.countDown(); }});
+        try {
+            for (int i = 0; i < 10; i++) {
+                System.gc();
+                if (finalizeDone.await(1L, TimeUnit.SECONDS) && ref.get() == null) {
+                    System.runFinalization(); // try to pick up stragglers
+                    return;
+                }
+            }
+        } catch (InterruptedException unexpected) {
+            throw new AssertionError("unexpected InterruptedException");
+        }
+        throw new AssertionError("failed to do a \"full\" gc");
+    }
+
+    static void gcAwait(BooleanSupplier s) {
+        for (int i = 0; i < 10; i++) {
+            if (s.getAsBoolean())
+                return;
+            forceFullGc();
+        }
+        throw new AssertionError("failed to satisfy condition");
+    }
+
+    public void clear_willClearItrs() {
+        boolean fair = rnd.nextBoolean();
+        int capacity = rnd.nextInt(2, 10);
+        ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
+        randomizePutIndex(q);
+        List<Iterator> its = new ArrayList<>();
+        for (int i = 0; i < capacity; i++)
+            assertTrue(q.add(i));
+        assertNull(itrs(q));
+        for (int i = 0; i < capacity; i++) {
+            its.add(q.iterator());
+            assertEquals(trackedIterators(q), its);
+            q.poll();
+            q.add(capacity + i);
+        }
+        q.clear();
+        assertNull(itrs(q));
+        int j = 0;
+        for (Iterator it : its) {
+            assertTrue(isDetached(it));
+            if (rnd.nextBoolean()) assertTrue(it.hasNext());
+            if (rnd.nextBoolean()) {
+                assertEquals(it.next(), j);
+                assertIteratorExhausted(it);
+            }
+            j++;
+        }
+    }
+
+    public void queueEmptyingWillClearItrs() {
+        boolean fair = rnd.nextBoolean();
+        int capacity = rnd.nextInt(2, 10);
+        ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
+        randomizePutIndex(q);
+        List<Iterator> its = new ArrayList<>();
+        for (int i = 0; i < capacity; i++)
+            q.add(i);
+        assertNull(itrs(q));
+        for (int i = 0; i < capacity; i++) {
+            its.add(q.iterator());
+            assertEquals(trackedIterators(q), its);
+            q.poll();
+            q.add(capacity+i);
+        }
+        for (int i = 0; i < capacity; i++)
+            q.poll();
+        assertNull(itrs(q));
+        int j = 0;
+        for (Iterator it : its) {
+            assertTrue(isDetached(it));
+            if (rnd.nextBoolean()) assertTrue(it.hasNext());
+            if (rnd.nextBoolean()) {
+                assertEquals(it.next(), j);
+                assertIteratorExhausted(it);
+            }
+            j++;
+        }
+    }
+
+    public void advancing2CyclesWillRemoveIterators() {
+        boolean fair = rnd.nextBoolean();
+        int capacity = rnd.nextInt(2, 10);
+        ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
+        List<Iterator> its = new ArrayList<>();
+        for (int i = 0; i < capacity; i++)
+            q.add(i);
+        assertNull(itrs(q));
+        for (int i = capacity; i < 3 * capacity; i++) {
+            its.add(q.iterator());
+            assertEquals(trackedIterators(q), its);
+            q.poll();
+            q.add(i);
+        }
+        for (int i = 3 * capacity; i < 4 * capacity; i++) {
+            assertEquals(trackedIterators(q), its.subList(capacity,2*capacity));
+            q.poll();
+            q.add(i);
+        }
+        assertNull(itrs(q));
+        int j = 0;
+        for (Iterator it : its) {
+            assertTrue(isDetached(it));
+            if (rnd.nextBoolean()) assertTrue(it.hasNext());
+            if (rnd.nextBoolean()) {
+                assertEquals(it.next(), j);
+                assertIteratorExhausted(it);
+            }
+            j++;
+        }
+    }
+
+    /**
+     * Interior removal of elements used by an iterator will cause it
+     * to be untracked.
+     */
+    public void interiorRemovalOfElementsUsedByIterator() {
+        boolean fair = rnd.nextBoolean();
+        int capacity = rnd.nextInt(10, 20);
+        ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
+        randomizePutIndex(q);
+        q.add(0);
+        for (int i = 1; i < 2 * capacity; i++) {
+            q.add(i);
+            Integer[] elts = { -1, -2, -3 };
+            for (Integer elt : elts) q.add(elt);
+            assertEquals(q.remove(), i - 1);
+            Iterator it = q.iterator();
+            assertEquals(it.next(), i);
+            assertEquals(it.next(), elts[0]);
+            Collections.shuffle(Arrays.asList(elts));
+            assertTrue(q.remove(elts[0]));
+            assertTrue(q.remove(elts[1]));
+            assertEquals(trackedIterators(q), Collections.singletonList(it));
+            assertTrue(q.remove(elts[2]));
+            assertNull(itrs(q));
+            assertEquals(it.next(), -2);
+            assertIteratorExhausted(it);
+            assertTrue(isDetached(it));
+        }
+    }
+
+    public void iteratorsOnEmptyQueue() {
+        boolean fair = rnd.nextBoolean();
+        int capacity = rnd.nextInt(1, 10);
+        ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
+        randomizePutIndex(q);
+        for (int i = 0; i < 4; i++) {
+            Iterator it = q.iterator();
+            assertNull(itrs(q));
+            assertIteratorExhausted(it);
+            assertTrue(isDetached(it));
+            assertRemoveThrowsISE(it);
+        }
+    }
+
+    public void interiorRemovalOfIteratorsLastElement() {
+        boolean fair = rnd.nextBoolean();
+        int capacity = rnd.nextInt(3, 10);
+        ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
+        randomizePutIndex(q);
+        List<Iterator> its = new ArrayList<>();
+        for (int i = 0; i < capacity; i++)
+            q.add(i);
+        for (int i = 0; i < capacity; i++) {
+            Iterator it = q.iterator();
+            its.add(it);
+            for (int j = 0; j < i; j++)
+                assertEquals(j, it.next());
+            assertEquals(attachedIterators(q), its);
+        }
+        q.remove(capacity - 1);
+        assertEquals(attachedIterators(q), its);
+        for (int i = 1; i < capacity - 1; i++) {
+            q.remove(capacity - i - 1);
+            Iterator it = its.get(capacity - i);
+            assertTrue(isDetached(it));
+            assertEquals(attachedIterators(q), its.subList(0, capacity - i));
+            if (rnd.nextBoolean()) assertTrue(it.hasNext());
+            assertEquals(it.next(), capacity - i);
+            assertIteratorExhausted(it);
+        }
+        assertEquals(attachedIterators(q), its.subList(0, 2));
+        q.remove(0);
+        assertTrue(q.isEmpty());
+        assertNull(itrs(q));
+        Iterator it = its.get(0);
+        assertEquals(it.next(), 0);
+        assertRemoveHasNoEffect(it, q);
+        assertIteratorExhausted(it);
+        assertTrue(isDetached(it));
+        assertRemoveHasNoEffect(its.get(1), q);
+    }
+
+    /**
+     * Checks "interior" removal of alternating elements, straddling 2 cycles
+     */
+    public void interiorRemovalOfAlternatingElements() {
+        boolean fair = rnd.nextBoolean();
+        int capacity = 2 * rnd.nextInt(2, 10);
+        ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
+        List<Iterator> its = new ArrayList<>();
+        // Move takeIndex to middle
+        for (int i = 0; i < capacity/2; i++) {
+            assertTrue(q.add(i));
+            assertEquals(q.poll(), i);
+        }
+        assertEquals(takeIndex(q), capacity/2);
+        for (int i = 0; i < capacity; i++)
+            q.add(i);
+        for (int i = 0; i < capacity; i++) {
+            Iterator it = q.iterator();
+            its.add(it);
+            for (int j = 0; j < i; j++)
+                assertEquals(j, it.next());
+            assertEquals(attachedIterators(q), its);
+        }
+
+        // Remove all even elements, in either direction, using
+        // q.remove(), or iterator.remove()
+        switch (rnd.nextInt(3)) {
+        case 0:
+            for (int i = 0; i < capacity; i+=2) assertTrue(q.remove(i));
+            break;
+        case 1:
+            for (int i = capacity - 2; i >= 0; i-=2) assertTrue(q.remove(i));
+            break;
+        case 2:
+            Iterator it = q.iterator();
+            while (it.hasNext()) {
+                int i = (Integer) it.next();
+                if ((i & 1) == 0)
+                    it.remove();
+            }
+            break;
+        default: throw new AssertionError();
+        }
+        assertEquals(attachedIterators(q), its);
+
+        for (int i = 0; i < capacity; i++) {
+            Iterator it = its.get(i);
+            boolean even = ((i & 1) == 0);
+            if (even) {
+                if (rnd.nextBoolean()) assertTrue(it.hasNext());
+                assertEquals(i, it.next());
+                for (int j = i+1; j < capacity; j += 2)
+                    assertEquals(j, it.next());
+                assertTrue(!isDetached(it));
+                assertTrue(!it.hasNext());
+                assertTrue(isDetached(it));
+            } else { /* odd */
+                if (rnd.nextBoolean()) assertTrue(it.hasNext());
+                assertRemoveHasNoEffect(it, q);
+                assertEquals(i, it.next());
+                for (int j = i+2; j < capacity; j += 2)
+                    assertEquals(j, it.next());
+                assertTrue(!isDetached(it));
+                assertTrue(!it.hasNext());
+                assertTrue(isDetached(it));
+            }
+        }
+        assertEquals(trackedIterators(q), Collections.emptyList());
+        assertNull(itrs(q));
+    }
+
+    public void garbageCollectionOfUnreachableIterators() {
+        boolean fair = rnd.nextBoolean();
+        int capacity = rnd.nextInt(1, 10);
+        ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
+        randomizePutIndex(q);
+        List<Iterator> its = new ArrayList<>();
+        for (int i = 0; i < capacity; i++) q.add(i);
+        for (int i = 0; i < capacity; i++) its.add(q.iterator());
+        assertEquals(attachedIterators(q), its);
+        its = null;
+        gcAwait(() -> {
+            List<Iterator> trackedIterators = trackedIterators(q);
+            assertEquals(trackedIterators.size(), capacity);
+            for (Iterator x : trackedIterators)
+                if (x != null) return false;
+            return true;
+        });
+        Iterator it = q.iterator(); //
+        assertEquals(trackedIterators(q), Collections.singletonList(it));
+    }
+
+    public void garbageCollectionOfUnreachableIteratorsWithRandomlyRetainedSubset() {
+        boolean fair = rnd.nextBoolean();
+        int capacity = rnd.nextInt(10, 20);
+        ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
+        randomizePutIndex(q);
+        List<Iterator> its = new ArrayList<>();
+        List<Iterator> retained = new ArrayList<>();
+        final int size = 1 + rnd.nextInt(capacity);
+        for (int i = 0; i < size; i++) q.add(i);
+        for (int i = 0; i < size; i++) its.add(q.iterator());
+        assertEquals(attachedIterators(q), its);
+        // Leave sufficient gaps in retained
+        for (int i = 0; i < size; i+= 2+rnd.nextInt(3))
+            retained.add(its.get(i));
+        its = null;
+        gcAwait(() -> {
+            List<Iterator> trackedIterators = trackedIterators(q);
+            assertEquals(trackedIterators.size(), size);
+            for (Iterator it : trackedIterators)
+                if ((it != null) ^ retained.contains(it)) return false;
+            return true;
+        });
+        Iterator it = q.iterator(); // trigger another sweep
+        retained.add(it);
+        assertEquals(trackedIterators(q), retained);
+    }
+
+    /**
+     * Checks incremental sweeping of unreachable iterators.
+     * Excessively white box?!
+     */
+    public void incrementalSweepingOfUnreachableIterators() {
+        boolean fair = rnd.nextBoolean();
+        int capacity = rnd.nextInt(10, 20);
+        ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
+        randomizePutIndex(q);
+        final int SHORT_SWEEP_PROBES = 4;
+        final int LONG_SWEEP_PROBES = 16;
+        final int PROBE_HOP = LONG_SWEEP_PROBES + 6 * SHORT_SWEEP_PROBES;
+        final int PROBE_HOP_COUNT = 10;
+        // Expect around 8 sweeps per PROBE_HOP
+        final int SWEEPS_PER_PROBE_HOP = 8;
+        List<Iterator> its = new ArrayList<>();
+        for (int i = 0; i < capacity; i++)
+            q.add(i);
+        for (int i = 0; i < PROBE_HOP_COUNT * PROBE_HOP; i++)
+            its.add(q.iterator());
+        assertEquals(attachedIterators(q), its);
+        // make some garbage, separated by PROBE_HOP
+        for (int i = 0; i < its.size(); i += PROBE_HOP)
+            its.set(i, null);
+        its.removeIf(it -> it == null);
+        forceFullGc();
+        int retries;
+        for (retries = 0;
+             trackedIterators(q).contains(null) && retries < 1000;
+             retries++)
+            // one round of sweeping
+            its.add(q.iterator());
+        assertTrue(retries >= PROBE_HOP_COUNT * (SWEEPS_PER_PROBE_HOP - 2));
+        assertTrue(retries <= PROBE_HOP_COUNT * (SWEEPS_PER_PROBE_HOP + 2));
+        assertEquals(trackedIterators(q), its);
+    }
+
+    public void Iterator_remove_safetyWhileInDetachedMode() {
+        boolean fair = rnd.nextBoolean();
+        int capacity = rnd.nextInt(10, 20);
+        ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
+        List<Iterator> its = new ArrayList<>();
+        for (int i = 0; i < capacity/2; i++) {
+            q.add(i);
+            q.remove();
+        }
+        assertEquals(takeIndex(q), capacity/2);
+        for (int i = 0; i < capacity; i++)
+            q.add(i);
+        for (int i = 0; i < capacity; i++) {
+            Iterator it = q.iterator();
+            its.add(it);
+            for (int j = 0; j < i; j++)
+                assertEquals(j, it.next());
+        }
+        assertEquals(attachedIterators(q), its);
+        for (int i = capacity - 1; i >= 0; i--) {
+            Iterator it = its.get(i);
+            assertEquals(i, it.next()); // last element
+            assertTrue(!isDetached(it));
+            assertTrue(!it.hasNext()); // first hasNext failure
+            assertTrue(isDetached(it));
+            int size = q.size();
+            assertTrue(q.contains(i));
+            switch (rnd.nextInt(3)) {
+            case 0:
+                it.remove();
+                assertTrue(!q.contains(i));
+                assertEquals(q.size(), size - 1);
+                break;
+            case 1:
+                // replace i with impostor
+                if (q.remainingCapacity() == 0) {
+                    assertTrue(q.remove(i));
+                    assertTrue(q.add(-1));
+                } else {
+                    assertTrue(q.add(-1));
+                    assertTrue(q.remove(i));
+                }
+                it.remove(); // should have no effect
+                assertEquals(size, q.size());
+                assertTrue(q.contains(-1));
+                assertTrue(q.remove(-1));
+                break;
+            case 2:
+                // replace i with true impostor
+                if (i != 0) {
+                    assertTrue(q.remove(i));
+                    assertTrue(q.add(i));
+                }
+                it.remove();
+                assertTrue(!q.contains(i));
+                assertEquals(q.size(), size - 1);
+                break;
+            default: throw new AssertionError();
+            }
+            assertRemoveThrowsISE(it);
+            assertTrue(isDetached(it));
+            assertTrue(!trackedIterators(q).contains(it));
+        }
+        assertTrue(q.isEmpty());
+        assertNull(itrs(q));
+        for (Iterator it : its)
+            assertIteratorExhausted(it);
+    }
+
+    /**
+     * Checks dequeues bypassing iterators' current positions.
+     */
+    public void dequeuesBypassingIteratorCurrentPositions() {
+        boolean fair = rnd.nextBoolean();
+        int capacity = rnd.nextInt(10, 20);
+        ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
+        Queue<Iterator> its0 = new ArrayDeque<>();
+        Queue<Iterator> itsMid = new ArrayDeque<>();
+        List<Iterator> its = new ArrayList<>();
+        for (int i = 0; i < capacity; i++)
+            q.add(i);
+        for (int i = 0; i < 2 * capacity + 1; i++) {
+            Iterator it = q.iterator();
+            its.add(it);
+            its0.add(it);
+        }
+        for (int i = 0; i < 2 * capacity + 1; i++) {
+            Iterator it = q.iterator();
+            for (int j = 0; j < capacity/2; j++)
+                assertEquals(j, it.next());
+            its.add(it);
+            itsMid.add(it);
+        }
+        for (int i = capacity; i < 3 * capacity; i++) {
+            Iterator it;
+
+            it = its0.remove();
+            assertRemoveThrowsISE(it);
+            if (rnd.nextBoolean()) assertTrue(it.hasNext());
+            assertEquals(0, it.next());
+            int victim = i - capacity;
+            for (int j = victim + (victim == 0 ? 1 : 0); j < i; j++) {
+                if (rnd.nextBoolean()) assertTrue(it.hasNext());
+                assertEquals(j, it.next());
+            }
+            assertIteratorExhausted(it);
+
+            it = itsMid.remove();
+            if (victim >= capacity/2)
+                assertRemoveHasNoEffect(it, q);
+            assertEquals(capacity/2, it.next());
+            if (victim > capacity/2)
+                assertRemoveHasNoEffect(it, q);
+            for (int j = Math.max(victim, capacity/2 + 1); j < i; j++) {
+                if (rnd.nextBoolean()) assertTrue(it.hasNext());
+                assertEquals(j, it.next());
+            }
+            assertIteratorExhausted(it);
+
+            if (rnd.nextBoolean()) {
+                assertEquals(victim, q.remove());
+            } else {
+                ArrayList list = new ArrayList(1);
+                q.drainTo(list, 1);
+                assertEquals(list.size(), 1);
+                assertEquals(victim, list.get(0));
+            }
+            assertTrue(q.add(i));
+        }
+        // takeIndex has wrapped twice.
+        Iterator it0 = its0.remove();
+        Iterator itMid = itsMid.remove();
+        assertTrue(isDetached(it0));
+        assertTrue(isDetached(itMid));
+        if (rnd.nextBoolean()) assertTrue(it0.hasNext());
+        if (rnd.nextBoolean()) assertTrue(itMid.hasNext());
+        assertRemoveThrowsISE(it0);
+        assertRemoveHasNoEffect(itMid, q);
+        if (rnd.nextBoolean()) assertEquals(0, it0.next());
+        if (rnd.nextBoolean()) assertEquals(capacity/2, itMid.next());
+        assertTrue(isDetached(it0));
+        assertTrue(isDetached(itMid));
+        assertEquals(capacity, q.size());
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * Checks collective sanity of iteration, toArray() and toString().
+     */
+    public void collectiveSanity() {
+        boolean fair = rnd.nextBoolean();
+        int capacity = rnd.nextInt(10, 20);
+        ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
+        randomizePutIndex(q);
+        for (int i = 0; i < capacity; i++) {
+            checkIterationSanity(q);
+            assertEquals(capacity, q.size() + q.remainingCapacity());
+            q.add(i);
+        }
+        for (int i = 0; i < (capacity + (capacity >> 1)); i++) {
+            checkIterationSanity(q);
+            assertEquals(capacity, q.size() + q.remainingCapacity());
+            assertEquals(i, q.peek());
+            assertEquals(i, q.poll());
+            checkIterationSanity(q);
+            assertEquals(capacity, q.size() + q.remainingCapacity());
+            q.add(capacity + i);
+        }
+        for (int i = 0; i < capacity; i++) {
+            checkIterationSanity(q);
+            assertEquals(capacity, q.size() + q.remainingCapacity());
+            int expected = i + capacity + (capacity >> 1);
+            assertEquals(expected, q.peek());
+            assertEquals(expected, q.poll());
+        }
+        checkIterationSanity(q);
+    }
+
+    public void iteratorsDetachedWhenExhaustedAndLastRetRemoved() {
+        boolean fair = rnd.nextBoolean();
+        int capacity = rnd.nextInt(2, 10);
+        ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
+        randomizePutIndex(q);
+        int size = rnd.nextInt(1, capacity + 1);
+        for (int i = 0; i < size; i++) q.add(i);
+        Iterator it = q.iterator();
+        for (int i = 0; i < size - 1; i++) assertEquals(i, it.next());
+        assertEquals(trackedIterators(q), Collections.singletonList(it));
+        assertFalse(isDetached(it));
+        switch (rnd.nextInt(2)) {
+        case 0: assertTrue(q.remove(size - 1)); break;
+        case 1: assertTrue(q.removeIf(e -> e.equals(size - 1))); break;
+        default: throw new AssertionError();
+        }
+        assertEquals(size - 1, it.next()); // should trigger detach
+        assertNull(itrs(q));
+        assertTrue(isDetached(it));
+        assertRemoveHasNoEffect(it, q);
+    }
+}
--- a/test/java/util/concurrent/ScheduledThreadPoolExecutor/GCRetention.java	Mon Mar 06 17:03:26 2017 +0300
+++ b/test/java/util/concurrent/ScheduledThreadPoolExecutor/GCRetention.java	Mon Mar 06 10:35:14 2017 -0800
@@ -34,12 +34,12 @@
 /*
  * @test
  * @summary Ensure that waiting pool threads don't retain refs to tasks.
- * @library /lib/testlibrary/
  */
 
-import static java.util.concurrent.TimeUnit.MILLISECONDS;
-
+import java.lang.ref.ReferenceQueue;
 import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.Delayed;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
@@ -47,10 +47,8 @@
 import java.util.concurrent.ScheduledThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
-import jdk.testlibrary.Utils;
 
 public class GCRetention {
-    static final long LONG_DELAY_MS = Utils.adjustTimeout(10_000);
 
     /**
      * A custom thread pool with a custom RunnableScheduledFuture, for the
@@ -80,53 +78,48 @@
         }
     }
 
-    int countRefsCleared(WeakReference<?>[] refs) {
-        int count = 0;
-        for (WeakReference<?> ref : refs)
-            if (ref.get() == null)
-                count++;
-        return count;
+    void removeAll(ReferenceQueue<?> q, int n) throws InterruptedException {
+        for (int j = n; j--> 0; ) {
+            if (q.poll() == null) {
+                for (;;) {
+                    System.gc();
+                    if (q.remove(1000) != null)
+                        break;
+                    System.out.printf(
+                        "%d/%d unqueued references remaining%n", j, n);
+                }
+            }
+        }
+        check(q.poll() == null);
     }
 
     void test(String[] args) throws Throwable {
-        CustomPool pool = new CustomPool(10);
+        final CustomPool pool = new CustomPool(10);
         final int size = 100;
-        WeakReference<?>[] refs = new WeakReference<?>[size];
-        Future<?>[] futures = new Future<?>[size];
-        for (int i = 0; i < size; i++) {
-            final Object x = new Object();
-            refs[i] = new WeakReference<Object>(x);
-            // Create a Runnable with a strong ref to x.
-            Runnable r = new Runnable() {
-                    public void run() { System.out.println(x); }
-                };
-            // Schedule a custom task with a strong reference to r.
-            // Later tasks have earlier expiration, to ensure multiple
-            // residents of queue head.
-            futures[i] = pool.schedule(r, size*2-i, TimeUnit.MINUTES);
+        final ReferenceQueue<Object> q = new ReferenceQueue<>();
+        final List<WeakReference<?>> refs = new ArrayList<>(size);
+        final List<Future<?>> futures = new ArrayList<>(size);
+
+        // Schedule custom tasks with strong references.
+        class Task implements Runnable {
+            final Object x;
+            Task() { refs.add(new WeakReference<>(x = new Object(), q)); }
+            public void run() { System.out.println(x); }
         }
-        Thread.sleep(10);
-        for (int i = 0; i < size; i++) {
-            if (futures[i] != null) {
-                futures[i].cancel(false);
-                futures[i] = null;
-            }
-        }
+        // Give tasks added later earlier expiration, to ensure
+        // multiple residents of queue head.
+        for (int i = size; i--> 0; )
+            futures.add(pool.schedule(new Task(), i + 1, TimeUnit.MINUTES));
+        futures.forEach(future -> future.cancel(false));
+        futures.clear();
+
         pool.purge();
-        int cleared = 0;
-        for (int i = 0;
-             i < 10 && (cleared = countRefsCleared(refs)) < size;
-             i++) {
-            System.gc();
-            System.runFinalization();
-            Thread.sleep(10);
-        }
+        removeAll(q, size);
+        for (WeakReference<?> ref : refs) check(ref.get() == null);
+
         pool.shutdown();
-        pool.awaitTermination(LONG_DELAY_MS, MILLISECONDS);
-        if (cleared < size)
-            throw new Error(String.format
-                            ("references to %d/%d tasks retained (\"leaked\")",
-                             size - cleared, size));
+        // rely on test harness to handle timeout
+        pool.awaitTermination(1L, TimeUnit.DAYS);
     }
 
     //--------------------- Infrastructure ---------------------------
--- a/test/java/util/concurrent/tck/JSR166TestCase.java	Mon Mar 06 17:03:26 2017 +0300
+++ b/test/java/util/concurrent/tck/JSR166TestCase.java	Mon Mar 06 10:35:14 2017 -0800
@@ -1326,21 +1326,61 @@
                 startTime = System.nanoTime();
             else if (millisElapsedSince(startTime) > timeoutMillis) {
                 threadAssertTrue(thread.isAlive());
-                return;
+                fail("timed out waiting for thread to enter wait state");
             }
             Thread.yield();
         }
     }
 
     /**
-     * Waits up to LONG_DELAY_MS for the given thread to enter a wait
-     * state: BLOCKED, WAITING, or TIMED_WAITING.
+     * Spin-waits up to the specified number of milliseconds for the given
+     * thread to enter a wait state: BLOCKED, WAITING, or TIMED_WAITING,
+     * and additionally satisfy the given condition.
+     */
+    void waitForThreadToEnterWaitState(
+        Thread thread, long timeoutMillis, Callable<Boolean> waitingForGodot) {
+        long startTime = 0L;
+        for (;;) {
+            Thread.State s = thread.getState();
+            if (s == Thread.State.BLOCKED ||
+                s == Thread.State.WAITING ||
+                s == Thread.State.TIMED_WAITING) {
+                try {
+                    if (waitingForGodot.call())
+                        return;
+                } catch (Throwable fail) { threadUnexpectedException(fail); }
+            }
+            else if (s == Thread.State.TERMINATED)
+                fail("Unexpected thread termination");
+            else if (startTime == 0L)
+                startTime = System.nanoTime();
+            else if (millisElapsedSince(startTime) > timeoutMillis) {
+                threadAssertTrue(thread.isAlive());
+                fail("timed out waiting for thread to enter wait state");
+            }
+            Thread.yield();
+        }
+    }
+
+    /**
+     * Spin-waits up to LONG_DELAY_MS milliseconds for the given thread to
+     * enter a wait state: BLOCKED, WAITING, or TIMED_WAITING.
      */
     void waitForThreadToEnterWaitState(Thread thread) {
         waitForThreadToEnterWaitState(thread, LONG_DELAY_MS);
     }
 
     /**
+     * Spin-waits up to LONG_DELAY_MS milliseconds for the given thread to
+     * enter a wait state: BLOCKED, WAITING, or TIMED_WAITING,
+     * and additionally satisfy the given condition.
+     */
+    void waitForThreadToEnterWaitState(
+        Thread thread, Callable<Boolean> waitingForGodot) {
+        waitForThreadToEnterWaitState(thread, LONG_DELAY_MS, waitingForGodot);
+    }
+
+    /**
      * Returns the number of milliseconds since time given by
      * startNanoTime, which must have been previously returned from a
      * call to {@link System#nanoTime()}.
--- a/test/java/util/concurrent/tck/LinkedTransferQueueTest.java	Mon Mar 06 17:03:26 2017 +0300
+++ b/test/java/util/concurrent/tck/LinkedTransferQueueTest.java	Mon Mar 06 10:35:14 2017 -0800
@@ -42,6 +42,7 @@
 import java.util.NoSuchElementException;
 import java.util.Queue;
 import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Callable;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ExecutorService;
@@ -766,9 +767,11 @@
             }});
 
         threadStarted.await();
-        waitForThreadToEnterWaitState(t);
-        assertEquals(1, q.getWaitingConsumerCount());
-        assertTrue(q.hasWaitingConsumer());
+        Callable<Boolean> oneConsumer
+            = new Callable<Boolean>() { public Boolean call() {
+                return q.hasWaitingConsumer()
+                && q.getWaitingConsumerCount() == 1; }};
+        waitForThreadToEnterWaitState(t, oneConsumer);
 
         assertTrue(q.offer(one));
         assertEquals(0, q.getWaitingConsumerCount());
@@ -790,7 +793,7 @@
 
     /**
      * transfer waits until a poll occurs. The transfered element
-     * is returned by this associated poll.
+     * is returned by the associated poll.
      */
     public void testTransfer2() throws InterruptedException {
         final LinkedTransferQueue<Integer> q = new LinkedTransferQueue<>();
@@ -804,8 +807,11 @@
             }});
 
         threadStarted.await();
-        waitForThreadToEnterWaitState(t);
-        assertEquals(1, q.size());
+        Callable<Boolean> oneElement
+            = new Callable<Boolean>() { public Boolean call() {
+                return !q.isEmpty() && q.size() == 1; }};
+        waitForThreadToEnterWaitState(t, oneElement);
+
         assertSame(five, q.poll());
         checkEmpty(q);
         awaitTermination(t);
@@ -868,7 +874,7 @@
 
     /**
      * transfer waits until a take occurs. The transfered element
-     * is returned by this associated take.
+     * is returned by the associated take.
      */
     public void testTransfer5() throws InterruptedException {
         final LinkedTransferQueue<Integer> q = new LinkedTransferQueue<>();
--- a/test/java/util/concurrent/tck/PhaserTest.java	Mon Mar 06 17:03:26 2017 +0300
+++ b/test/java/util/concurrent/tck/PhaserTest.java	Mon Mar 06 10:35:14 2017 -0800
@@ -550,7 +550,7 @@
             }});
 
         await(pleaseArrive);
-        waitForThreadToEnterWaitState(t, SHORT_DELAY_MS);
+        waitForThreadToEnterWaitState(t);
         assertEquals(0, phaser.arrive());
         awaitTermination(t);
 
@@ -578,7 +578,7 @@
             }});
 
         await(pleaseArrive);
-        waitForThreadToEnterWaitState(t, SHORT_DELAY_MS);
+        waitForThreadToEnterWaitState(t);
         t.interrupt();
         assertEquals(0, phaser.arrive());
         awaitTermination(t);
@@ -594,20 +594,20 @@
     public void testArriveAndAwaitAdvanceAfterInterrupt() {
         final Phaser phaser = new Phaser();
         assertEquals(0, phaser.register());
-        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        final CountDownLatch pleaseArrive = new CountDownLatch(1);
 
         Thread t = newStartedThread(new CheckedRunnable() {
             public void realRun() {
                 Thread.currentThread().interrupt();
                 assertEquals(0, phaser.register());
-                pleaseInterrupt.countDown();
+                pleaseArrive.countDown();
                 assertTrue(Thread.currentThread().isInterrupted());
                 assertEquals(1, phaser.arriveAndAwaitAdvance());
-                assertTrue(Thread.currentThread().isInterrupted());
+                assertTrue(Thread.interrupted());
             }});
 
-        await(pleaseInterrupt);
-        waitForThreadToEnterWaitState(t, SHORT_DELAY_MS);
+        await(pleaseArrive);
+        waitForThreadToEnterWaitState(t);
         Thread.currentThread().interrupt();
         assertEquals(1, phaser.arriveAndAwaitAdvance());
         assertTrue(Thread.interrupted());
@@ -628,11 +628,11 @@
                 assertFalse(Thread.currentThread().isInterrupted());
                 pleaseInterrupt.countDown();
                 assertEquals(1, phaser.arriveAndAwaitAdvance());
-                assertTrue(Thread.currentThread().isInterrupted());
+                assertTrue(Thread.interrupted());
             }});
 
         await(pleaseInterrupt);
-        waitForThreadToEnterWaitState(t, SHORT_DELAY_MS);
+        waitForThreadToEnterWaitState(t);
         t.interrupt();
         Thread.currentThread().interrupt();
         assertEquals(1, phaser.arriveAndAwaitAdvance());
@@ -807,7 +807,7 @@
         assertEquals(THREADS, phaser.getArrivedParties());
         assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
         for (Thread thread : threads)
-            waitForThreadToEnterWaitState(thread, SHORT_DELAY_MS);
+            waitForThreadToEnterWaitState(thread);
         for (Thread thread : threads)
             assertTrue(thread.isAlive());
         assertState(phaser, 0, THREADS + 1, 1);
--- a/test/java/util/concurrent/tck/StampedLockTest.java	Mon Mar 06 17:03:26 2017 +0300
+++ b/test/java/util/concurrent/tck/StampedLockTest.java	Mon Mar 06 10:35:14 2017 -0800
@@ -299,7 +299,6 @@
      * interruptible operations throw InterruptedException when pre-interrupted
      */
     public void testInterruptibleOperationsThrowInterruptedExceptionWhenPreInterrupted() {
-        final CountDownLatch running = new CountDownLatch(1);
         final StampedLock lock = new StampedLock();
 
         Action[] interruptibleLockActions = {
@@ -364,7 +363,6 @@
      * interruptible operations throw InterruptedException when write locked and interrupted
      */
     public void testInterruptibleOperationsThrowInterruptedExceptionWriteLockedInterrupted() {
-        final CountDownLatch running = new CountDownLatch(1);
         final StampedLock lock = new StampedLock();
         long s = lock.writeLock();
 
@@ -387,7 +385,6 @@
      * interruptible operations throw InterruptedException when read locked and interrupted
      */
     public void testInterruptibleOperationsThrowInterruptedExceptionReadLockedInterrupted() {
-        final CountDownLatch running = new CountDownLatch(1);
         final StampedLock lock = new StampedLock();
         long s = lock.readLock();
 
@@ -506,29 +503,32 @@
     }
 
     /**
-     * A writelock succeeds only after a reading thread unlocks
+     * writeLock() succeeds only after a reading thread unlocks
      */
     public void testWriteAfterReadLock() throws InterruptedException {
-        final CountDownLatch running = new CountDownLatch(1);
+        final CountDownLatch aboutToLock = new CountDownLatch(1);
         final StampedLock lock = new StampedLock();
         long rs = lock.readLock();
         Thread t = newStartedThread(new CheckedRunnable() {
             public void realRun() {
-                running.countDown();
+                aboutToLock.countDown();
                 long s = lock.writeLock();
+                assertTrue(lock.isWriteLocked());
+                assertFalse(lock.isReadLocked());
                 lock.unlockWrite(s);
             }});
 
-        running.await();
-        waitForThreadToEnterWaitState(t, MEDIUM_DELAY_MS);
+        aboutToLock.await();
+        waitForThreadToEnterWaitState(t);
         assertFalse(lock.isWriteLocked());
+        assertTrue(lock.isReadLocked());
         lock.unlockRead(rs);
         awaitTermination(t);
-        assertFalse(lock.isWriteLocked());
+        assertUnlocked(lock);
     }
 
     /**
-     * A writelock succeeds only after reading threads unlock
+     * writeLock() succeeds only after reading threads unlock
      */
     public void testWriteAfterMultipleReadLocks() {
         final StampedLock lock = new StampedLock();
@@ -551,35 +551,36 @@
         assertFalse(lock.isWriteLocked());
         lock.unlockRead(s);
         awaitTermination(t2);
-        assertFalse(lock.isWriteLocked());
+        assertUnlocked(lock);
     }
 
     /**
-     * Readlocks succeed only after a writing thread unlocks
+     * readLock() succeed only after a writing thread unlocks
      */
     public void testReadAfterWriteLock() {
         final StampedLock lock = new StampedLock();
         final CountDownLatch threadsStarted = new CountDownLatch(2);
         final long s = lock.writeLock();
-        Thread t1 = newStartedThread(new CheckedRunnable() {
+        final Runnable acquireReleaseReadLock = new CheckedRunnable() {
             public void realRun() {
                 threadsStarted.countDown();
                 long rs = lock.readLock();
+                assertTrue(lock.isReadLocked());
+                assertFalse(lock.isWriteLocked());
                 lock.unlockRead(rs);
-            }});
-        Thread t2 = newStartedThread(new CheckedRunnable() {
-            public void realRun() {
-                threadsStarted.countDown();
-                long rs = lock.readLock();
-                lock.unlockRead(rs);
-            }});
+            }};
+        Thread t1 = newStartedThread(acquireReleaseReadLock);
+        Thread t2 = newStartedThread(acquireReleaseReadLock);
 
         await(threadsStarted);
-        waitForThreadToEnterWaitState(t1, MEDIUM_DELAY_MS);
-        waitForThreadToEnterWaitState(t2, MEDIUM_DELAY_MS);
+        waitForThreadToEnterWaitState(t1);
+        waitForThreadToEnterWaitState(t2);
+        assertTrue(lock.isWriteLocked());
+        assertFalse(lock.isReadLocked());
         releaseWriteLock(lock, s);
         awaitTermination(t1);
         awaitTermination(t2);
+        assertUnlocked(lock);
     }
 
     /**
@@ -765,22 +766,24 @@
      */
     public void testValidateOptimisticWriteLocked2()
             throws InterruptedException {
-        final CountDownLatch running = new CountDownLatch(1);
+        final CountDownLatch locked = new CountDownLatch(1);
         final StampedLock lock = new StampedLock();
         final long p = assertValid(lock, lock.tryOptimisticRead());
 
         Thread t = newStartedThread(new CheckedInterruptedRunnable() {
             public void realRun() throws InterruptedException {
                 lock.writeLockInterruptibly();
-                running.countDown();
+                locked.countDown();
                 lock.writeLockInterruptibly();
             }});
 
-        running.await();
+        locked.await();
         assertFalse(lock.validate(p));
         assertEquals(0L, lock.tryOptimisticRead());
+        waitForThreadToEnterWaitState(t);
         t.interrupt();
         awaitTermination(t);
+        assertTrue(lock.isWriteLocked());
     }
 
     /**
--- a/test/jdk/internal/misc/JavaLangAccess/NewUnsafeString.java	Mon Mar 06 17:03:26 2017 +0300
+++ b/test/jdk/internal/misc/JavaLangAccess/NewUnsafeString.java	Mon Mar 06 10:35:14 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2017, 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
@@ -32,6 +32,7 @@
  * @summary Test JavaLangAccess.newUnsafeString
  * @modules java.base/jdk.internal.misc
  * @compile -XDignore.symbol.file NewUnsafeString.java
+ * @run main NewUnsafeString
  */
 public class NewUnsafeString {
 
--- a/test/sun/security/ec/SignedObjectChain.java	Mon Mar 06 17:03:26 2017 +0300
+++ b/test/sun/security/ec/SignedObjectChain.java	Mon Mar 06 10:35:14 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -24,8 +24,9 @@
 /*
  * @test
  * @bug 8050374
+ * @summary Verify a chain of signed objects
  * @compile ../../../java/security/SignedObject/Chain.java
- * @summary Verify a chain of signed objects
+ * @run main SignedObjectChain
  */
 public class SignedObjectChain {
 
--- a/test/sun/security/mscapi/SignedObjectChain.java	Mon Mar 06 17:03:26 2017 +0300
+++ b/test/sun/security/mscapi/SignedObjectChain.java	Mon Mar 06 10:35:14 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -24,9 +24,10 @@
 /*
  * @test
  * @bug 8050374
+ * @summary Verify a chain of signed objects
  * @compile ../../../java/security/SignedObject/Chain.java
  * @requires os.family == "windows"
- * @summary Verify a chain of signed objects
+ * @run main SignedObjectChain
  */
 public class SignedObjectChain {
 
--- a/test/sun/security/rsa/SignedObjectChain.java	Mon Mar 06 17:03:26 2017 +0300
+++ b/test/sun/security/rsa/SignedObjectChain.java	Mon Mar 06 10:35:14 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -24,8 +24,9 @@
 /*
  * @test
  * @bug 8050374
+ * @summary Verify a chain of signed objects
  * @compile ../../../java/security/SignedObject/Chain.java
- * @summary Verify a chain of signed objects
+ * @run main SignedObjectChain
  */
 public class SignedObjectChain {
 
--- a/test/sun/security/ssl/rsa/SignedObjectChain.java	Mon Mar 06 17:03:26 2017 +0300
+++ b/test/sun/security/ssl/rsa/SignedObjectChain.java	Mon Mar 06 10:35:14 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -24,8 +24,9 @@
 /*
  * @test
  * @bug 8050374
+ * @summary Verify a chain of signed objects
  * @compile ../../../../java/security/SignedObject/Chain.java
- * @summary Verify a chain of signed objects
+ * @run main SignedObjectChain
  */
 public class SignedObjectChain {
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/launcher/Jexec.java	Mon Mar 06 10:35:14 2017 -0800
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8175000
+ * @summary test jexec
+ * @build TestHelper
+ * @run main Jexec
+ */
+
+import java.io.File;
+import java.io.IOException;
+
+public class Jexec extends TestHelper {
+    private final File testJar;
+    private final File jexecCmd;
+    private final String message = "Hello, do you read me ?";
+
+    Jexec() throws IOException {
+        jexecCmd = new File(JAVA_LIB, "jexec");
+        if (!jexecCmd.exists() || !jexecCmd.canExecute()) {
+            throw new Error("jexec: does not exist or not executable");
+        }
+
+        testJar = new File("test.jar");
+        StringBuilder tsrc = new StringBuilder();
+        tsrc.append("public static void main(String... args) {\n");
+        tsrc.append("   for (String x : args) {\n");
+        tsrc.append("        System.out.println(x);\n");
+        tsrc.append("   }\n");
+        tsrc.append("}\n");
+        createJar(testJar, tsrc.toString());
+    }
+
+    public static void main(String... args) throws Exception {
+        // linux is the only supported platform, give the others a pass
+        if (!isLinux) {
+            System.err.println("Warning: unsupported platform test passes vacuously");
+            return;
+        }
+        // ok to run the test now
+        Jexec t = new Jexec();
+        t.run(null);
+    }
+
+    @Test
+    void jexec() throws Exception {
+        TestResult tr = doExec(jexecCmd.getAbsolutePath(),
+                testJar.getAbsolutePath(), message);
+        if (!tr.isOK()) {
+            System.err.println(tr);
+            throw new Exception("incorrect exit value");
+        }
+        if (!tr.contains(message)) {
+            System.err.println(tr);
+            throw new Exception("expected message \'" + message + "\' not found");
+        }
+    }
+}